chris@1544: #!/bin/sh chris@1544: chris@1544: # Copyright (c) 2007, 2008 Rocco Rutte and others. chris@1544: # License: MIT chris@1544: chris@1544: ROOT="$(dirname "$(which "$0")")" chris@1544: REPO="" chris@1544: PFX="hg2git" chris@1544: SFX_MAPPING="mapping" chris@1544: SFX_MARKS="marks" chris@1544: SFX_HEADS="heads" chris@1544: SFX_STATE="state" chris@1544: GFI_OPTS="" chris@1544: PYTHON=${PYTHON:-python} chris@1544: chris@1544: USAGE="[--quiet] [-r ] [--force] [-m ] [-s] [--hgtags] [-A ] [-B ] [-T ] [-M ] [-o ] [--hg-hash] [-e ]" chris@1544: LONG_USAGE="Import hg repository up to either tip or chris@1544: If is omitted, use last hg repository as obtained from state file, chris@1544: GIT_DIR/$PFX-$SFX_STATE by default. chris@1544: chris@1544: Note: The argument order matters. chris@1544: chris@1544: Options: chris@1544: --quiet Passed to git-fast-import(1) chris@1544: -r Mercurial repository to import chris@1544: --force Ignore validation errors when converting, and pass --force chris@1544: to git-fast-import(1) chris@1544: -m Maximum revision to import chris@1544: -s Enable parsing Signed-off-by lines chris@1544: --hgtags Enable exporting .hgtags files chris@1544: -A Read author map from file chris@1544: (Same as in git-svnimport(1) and git-cvsimport(1)) chris@1544: -B Read branch map from file chris@1544: -T Read tags map from file chris@1544: -M Set the default branch name (defaults to 'master') chris@1544: -o Use as branch namespace to track upstream (eg 'origin') chris@1544: --hg-hash Annotate commits with the hg hash as git notes in the chris@1544: hg namespace. chris@1544: -e Assume commit and author strings retrieved from chris@1544: Mercurial are encoded in chris@1544: --fe Assume filenames from Mercurial are encoded chris@1544: in chris@1544: " chris@1544: case "$1" in chris@1544: -h|--help) chris@1544: echo "usage: $(basename "$0") $USAGE" chris@1544: echo "" chris@1544: echo "$LONG_USAGE" chris@1544: exit 0 chris@1544: esac Chris@1567: Chris@1567: IS_BARE=$(git rev-parse --is-bare-repository) \ Chris@1567: || (echo "Could not find git repo" ; exit 1) Chris@1567: if test "z$IS_BARE" != ztrue; then Chris@1567: # This is not a bare repo, cd to the toplevel Chris@1567: TOPLEVEL=$(git rev-parse --show-toplevel) \ Chris@1567: || (echo "Could not find git repo toplevel" ; exit 1) Chris@1567: cd $TOPLEVEL || exit 1 Chris@1567: fi Chris@1567: GIT_DIR=$(git rev-parse --git-dir) || (echo "Could not find git repo" ; exit 1) chris@1544: chris@1544: while case "$#" in 0) break ;; esac chris@1544: do chris@1544: case "$1" in chris@1544: -r|--r|--re|--rep|--repo) chris@1544: shift chris@1544: REPO="$1" chris@1544: ;; chris@1544: --q|--qu|--qui|--quie|--quiet) chris@1544: GFI_OPTS="$GFI_OPTS --quiet" chris@1544: ;; chris@1544: --force) chris@1544: # pass --force to git-fast-import and hg-fast-export.py chris@1544: GFI_OPTS="$GFI_OPTS --force" chris@1544: break chris@1544: ;; chris@1544: -*) chris@1544: # pass any other options down to hg2git.py chris@1544: break chris@1544: ;; chris@1544: *) chris@1544: break chris@1544: ;; chris@1544: esac chris@1544: shift chris@1544: done chris@1544: chris@1544: # for convenience: get default repo from state file chris@1544: if [ x"$REPO" = x -a -f "$GIT_DIR/$PFX-$SFX_STATE" ] ; then chris@1544: REPO="`grep '^:repo ' "$GIT_DIR/$PFX-$SFX_STATE" | cut -d ' ' -f 2`" chris@1544: echo "Using last hg repository \"$REPO\"" chris@1544: fi chris@1544: chris@1544: if [ -z "$REPO" ]; then chris@1544: echo "no repo given, use -r flag" chris@1544: exit 1 chris@1544: fi chris@1544: chris@1544: # make sure we have a marks cache chris@1544: if [ ! -f "$GIT_DIR/$PFX-$SFX_MARKS" ] ; then chris@1544: touch "$GIT_DIR/$PFX-$SFX_MARKS" chris@1544: fi chris@1544: chris@1544: # cleanup on exit chris@1544: trap 'rm -f "$GIT_DIR/$PFX-$SFX_MARKS.old" "$GIT_DIR/$PFX-$SFX_MARKS.tmp"' 0 chris@1544: chris@1544: _err1= chris@1544: _err2= chris@1544: exec 3>&1 chris@1544: { read -r _err1 || :; read -r _err2 || :; } <<-EOT chris@1544: $( chris@1544: exec 4>&3 3>&1 1>&4 4>&- chris@1544: { chris@1544: _e1=0 chris@1544: GIT_DIR="$GIT_DIR" $PYTHON "$ROOT/hg-fast-export.py" \ chris@1544: --repo "$REPO" \ chris@1544: --marks "$GIT_DIR/$PFX-$SFX_MARKS" \ chris@1544: --mapping "$GIT_DIR/$PFX-$SFX_MAPPING" \ chris@1544: --heads "$GIT_DIR/$PFX-$SFX_HEADS" \ chris@1544: --status "$GIT_DIR/$PFX-$SFX_STATE" \ chris@1544: "$@" 3>&- || _e1=$? chris@1544: echo $_e1 >&3 chris@1544: } | \ chris@1544: { chris@1544: _e2=0 chris@1544: git fast-import $GFI_OPTS --export-marks="$GIT_DIR/$PFX-$SFX_MARKS.tmp" 3>&- || _e2=$? chris@1544: echo $_e2 >&3 chris@1544: } chris@1544: ) chris@1544: EOT chris@1544: exec 3>&- chris@1544: [ "$_err1" = 0 -a "$_err2" = 0 ] || exit 1 chris@1544: chris@1544: # move recent marks cache out of the way... chris@1544: if [ -f "$GIT_DIR/$PFX-$SFX_MARKS" ] ; then chris@1544: mv "$GIT_DIR/$PFX-$SFX_MARKS" "$GIT_DIR/$PFX-$SFX_MARKS.old" chris@1544: else chris@1544: touch "$GIT_DIR/$PFX-$SFX_MARKS.old" chris@1544: fi chris@1544: chris@1544: # ...to create a new merged one chris@1544: cat "$GIT_DIR/$PFX-$SFX_MARKS.old" "$GIT_DIR/$PFX-$SFX_MARKS.tmp" \ chris@1544: | uniq > "$GIT_DIR/$PFX-$SFX_MARKS" chris@1544: chris@1544: # save SHA1s of current heads for incremental imports chris@1544: # and connectivity (plus sanity checking) chris@1544: for head in `git branch | sed 's#^..##'` ; do chris@1544: id="`git rev-parse refs/heads/$head`" chris@1544: echo ":$head $id" chris@1544: done > "$GIT_DIR/$PFX-$SFX_HEADS" chris@1544: chris@1544: # check diff with color: chris@1544: # ( for i in `find . -type f | grep -v '\.git'` ; do diff -u $i $REPO/$i ; done | cdiff ) | less -r