Fix crash when a branch name starts with '/'
[fast-export.git] / hg-fast-export.sh
blob93342ee5477ae8faf87f2da81d4fd123815e9751
1 #!/bin/sh
3 # Copyright (c) 2007, 2008 Rocco Rutte <pdmef@gmx.net> and others.
4 # License: MIT <http://www.opensource.org/licenses/mit-license.php>
6 ROOT="$(dirname "$(which "$0")")"
7 REPO=""
8 PFX="hg2git"
9 SFX_MAPPING="mapping"
10 SFX_MARKS="marks"
11 SFX_HEADS="heads"
12 SFX_STATE="state"
13 GFI_OPTS=""
14 PYTHON=${PYTHON:-python}
16 USAGE="[--quiet] [-r <repo>] [--force] [-m <max>] [-s] [--hgtags] [-A <file>] [-B <file>] [-T <file>] [-M <name>] [-o <name>] [--hg-hash] [-e <encoding>]"
17 LONG_USAGE="Import hg repository <repo> up to either tip or <max>
18 If <repo> is omitted, use last hg repository as obtained from state file,
19 GIT_DIR/$PFX-$SFX_STATE by default.
21 Note: The argument order matters.
23 Options:
24 --quiet Passed to git-fast-import(1)
25 -r <repo> Mercurial repository to import
26 --force Ignore validation errors when converting, and pass --force
27 to git-fast-import(1)
28 -m <max> Maximum revision to import
29 -s Enable parsing Signed-off-by lines
30 --hgtags Enable exporting .hgtags files
31 -A <file> Read author map from file
32 (Same as in git-svnimport(1) and git-cvsimport(1))
33 -B <file> Read branch map from file
34 -T <file> Read tags map from file
35 -M <name> Set the default branch name (defaults to 'master')
36 -o <name> Use <name> as branch namespace to track upstream (eg 'origin')
37 --hg-hash Annotate commits with the hg hash as git notes in the
38 hg namespace.
39 -e <encoding> Assume commit and author strings retrieved from
40 Mercurial are encoded in <encoding>
41 --fe <filename_encoding> Assume filenames from Mercurial are encoded
42 in <filename_encoding>
44 case "$1" in
45 -h|--help)
46 echo "usage: $(basename "$0") $USAGE"
47 echo ""
48 echo "$LONG_USAGE"
49 exit 0
50 esac
52 IS_BARE=$(git rev-parse --is-bare-repository) \
53 || (echo "Could not find git repo" ; exit 1)
54 if test "z$IS_BARE" != ztrue; then
55 # This is not a bare repo, cd to the toplevel
56 TOPLEVEL=$(git rev-parse --show-toplevel) \
57 || (echo "Could not find git repo toplevel" ; exit 1)
58 cd $TOPLEVEL || exit 1
60 GIT_DIR=$(git rev-parse --git-dir) || (echo "Could not find git repo" ; exit 1)
63 IGNORECASEWARN=""
64 IGNORECASE=`git config core.ignoreCase`
65 if [ "true" = "$IGNORECASE" ]; then
66 IGNORECASEWARN="true"
67 fi;
70 while case "$#" in 0) break ;; esac
72 case "$1" in
73 -r|--r|--re|--rep|--repo)
74 shift
75 REPO="$1"
77 --q|--qu|--qui|--quie|--quiet)
78 GFI_OPTS="$GFI_OPTS --quiet"
80 --force)
81 # pass --force to git-fast-import and hg-fast-export.py
82 GFI_OPTS="$GFI_OPTS --force"
83 IGNORECASEWARN="";
84 break
86 -*)
87 # pass any other options down to hg2git.py
88 break
91 break
93 esac
94 shift
95 done
97 if [ ! -z "$IGNORECASEWARN" ]; then
98 echo "Error: The option core.ignoreCase is set to true in the git"
99 echo "repository. This will produce empty changesets for renames that just"
100 echo "change the case of the file name."
101 echo "Use --force to skip this check or change the option with"
102 echo "git config core.ignoreCase false"
103 exit 1
106 # Make a backup copy of each state file
107 for i in $SFX_STATE $SFX_MARKS $SFX_MAPPING $SFX_HEADS ; do
108 if [ -f "$GIT_DIR/$PFX-$i" ] ; then
109 cp "$GIT_DIR/$PFX-$i" "$GIT_DIR/$PFX-$i~"
111 done
113 # for convenience: get default repo from state file
114 if [ x"$REPO" = x -a -f "$GIT_DIR/$PFX-$SFX_STATE" ] ; then
115 REPO="`grep '^:repo ' "$GIT_DIR/$PFX-$SFX_STATE" | cut -d ' ' -f 2`"
116 echo "Using last hg repository \"$REPO\""
119 if [ -z "$REPO" ]; then
120 echo "no repo given, use -r flag"
121 exit 1
124 # make sure we have a marks cache
125 if [ ! -f "$GIT_DIR/$PFX-$SFX_MARKS" ] ; then
126 touch "$GIT_DIR/$PFX-$SFX_MARKS"
129 # cleanup on exit
130 trap 'rm -f "$GIT_DIR/$PFX-$SFX_MARKS.old" "$GIT_DIR/$PFX-$SFX_MARKS.tmp"' 0
132 _err1=
133 _err2=
134 exec 3>&1
135 { read -r _err1 || :; read -r _err2 || :; } <<-EOT
137 exec 4>&3 3>&1 1>&4 4>&-
139 _e1=0
140 GIT_DIR="$GIT_DIR" $PYTHON "$ROOT/hg-fast-export.py" \
141 --repo "$REPO" \
142 --marks "$GIT_DIR/$PFX-$SFX_MARKS" \
143 --mapping "$GIT_DIR/$PFX-$SFX_MAPPING" \
144 --heads "$GIT_DIR/$PFX-$SFX_HEADS" \
145 --status "$GIT_DIR/$PFX-$SFX_STATE" \
146 "$@" 3>&- || _e1=$?
147 echo $_e1 >&3
148 } | \
150 _e2=0
151 git fast-import $GFI_OPTS --export-marks="$GIT_DIR/$PFX-$SFX_MARKS.tmp" 3>&- || _e2=$?
152 echo $_e2 >&3
156 exec 3>&-
157 [ "$_err1" = 0 -a "$_err2" = 0 ] || exit 1
159 # move recent marks cache out of the way...
160 if [ -f "$GIT_DIR/$PFX-$SFX_MARKS" ] ; then
161 mv "$GIT_DIR/$PFX-$SFX_MARKS" "$GIT_DIR/$PFX-$SFX_MARKS.old"
162 else
163 touch "$GIT_DIR/$PFX-$SFX_MARKS.old"
166 # ...to create a new merged one
167 cat "$GIT_DIR/$PFX-$SFX_MARKS.old" "$GIT_DIR/$PFX-$SFX_MARKS.tmp" \
168 | uniq > "$GIT_DIR/$PFX-$SFX_MARKS"
170 # save SHA1s of current heads for incremental imports
171 # and connectivity (plus sanity checking)
172 for head in `git branch | sed 's#^..##'` ; do
173 id="`git rev-parse refs/heads/$head`"
174 echo ":$head $id"
175 done > "$GIT_DIR/$PFX-$SFX_HEADS"
177 # check diff with color:
178 # ( for i in `find . -type f | grep -v '\.git'` ; do diff -u $i $REPO/$i ; done | cdiff ) | less -r