Behave nicely when the found readlink does not understand '-f'
[fast-export.git] / hg-fast-export.sh
blobb2912968b01590f9a3534afee6891b0f6c66dc80
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 READLINK="readlink"
7 if command -v greadlink > /dev/null; then
8 READLINK="greadlink" # Prefer greadlink over readlink
9 fi
11 if ! $READLINK -f "$(which "$0")" >& /dev/null; then
12 ROOT="$(dirname "$(which "$0")")"
13 if [ ! -f "$ROOT/hg-fast-export.py" ] ; then
14 echo "hg-fast-exports requires a readlink implementation which knows" \
15 " how to canonicalize paths in order to be called via a symlink."
16 exit 1
18 else
19 ROOT="$(dirname "$($READLINK -f "$(which "$0")")")"
22 REPO=""
23 PFX="hg2git"
24 SFX_MAPPING="mapping"
25 SFX_MARKS="marks"
26 SFX_HEADS="heads"
27 SFX_STATE="state"
28 GFI_OPTS=""
29 PYTHON=${PYTHON:-python}
31 USAGE="[--quiet] [-r <repo>] [--force] [-m <max>] [-s] [--hgtags] [-A <file>] [-B <file>] [-T <file>] [-M <name>] [-o <name>] [--hg-hash] [-e <encoding>]"
32 LONG_USAGE="Import hg repository <repo> up to either tip or <max>
33 If <repo> is omitted, use last hg repository as obtained from state file,
34 GIT_DIR/$PFX-$SFX_STATE by default.
36 Note: The argument order matters.
38 Options:
39 --quiet Passed to git-fast-import(1)
40 -r <repo> Mercurial repository to import
41 --force Ignore validation errors when converting, and pass --force
42 to git-fast-import(1)
43 -m <max> Maximum revision to import
44 -s Enable parsing Signed-off-by lines
45 --hgtags Enable exporting .hgtags files
46 -A <file> Read author map from file
47 (Same as in git-svnimport(1) and git-cvsimport(1))
48 -B <file> Read branch map from file
49 -T <file> Read tags map from file
50 -M <name> Set the default branch name (defaults to 'master')
51 -o <name> Use <name> as branch namespace to track upstream (eg 'origin')
52 --hg-hash Annotate commits with the hg hash as git notes in the
53 hg namespace.
54 -e <encoding> Assume commit and author strings retrieved from
55 Mercurial are encoded in <encoding>
56 --fe <filename_encoding> Assume filenames from Mercurial are encoded
57 in <filename_encoding>
59 case "$1" in
60 -h|--help)
61 echo "usage: $(basename "$0") $USAGE"
62 echo ""
63 echo "$LONG_USAGE"
64 exit 0
65 esac
67 IS_BARE=$(git rev-parse --is-bare-repository) \
68 || (echo "Could not find git repo" ; exit 1)
69 if test "z$IS_BARE" != ztrue; then
70 # This is not a bare repo, cd to the toplevel
71 TOPLEVEL=$(git rev-parse --show-toplevel) \
72 || (echo "Could not find git repo toplevel" ; exit 1)
73 cd $TOPLEVEL || exit 1
75 GIT_DIR=$(git rev-parse --git-dir) || (echo "Could not find git repo" ; exit 1)
78 IGNORECASEWARN=""
79 IGNORECASE=`git config core.ignoreCase`
80 if [ "true" = "$IGNORECASE" ]; then
81 IGNORECASEWARN="true"
82 fi;
85 while case "$#" in 0) break ;; esac
87 case "$1" in
88 -r|--r|--re|--rep|--repo)
89 shift
90 REPO="$1"
92 --q|--qu|--qui|--quie|--quiet)
93 GFI_OPTS="$GFI_OPTS --quiet"
95 --force)
96 # pass --force to git-fast-import and hg-fast-export.py
97 GFI_OPTS="$GFI_OPTS --force"
98 IGNORECASEWARN="";
99 break
102 # pass any other options down to hg2git.py
103 break
106 break
108 esac
109 shift
110 done
112 if [ ! -z "$IGNORECASEWARN" ]; then
113 echo "Error: The option core.ignoreCase is set to true in the git"
114 echo "repository. This will produce empty changesets for renames that just"
115 echo "change the case of the file name."
116 echo "Use --force to skip this check or change the option with"
117 echo "git config core.ignoreCase false"
118 exit 1
121 # Make a backup copy of each state file
122 for i in $SFX_STATE $SFX_MARKS $SFX_MAPPING $SFX_HEADS ; do
123 if [ -f "$GIT_DIR/$PFX-$i" ] ; then
124 cp "$GIT_DIR/$PFX-$i" "$GIT_DIR/$PFX-$i~"
126 done
128 # for convenience: get default repo from state file
129 if [ x"$REPO" = x -a -f "$GIT_DIR/$PFX-$SFX_STATE" ] ; then
130 REPO="`grep '^:repo ' "$GIT_DIR/$PFX-$SFX_STATE" | cut -d ' ' -f 2`"
131 echo "Using last hg repository \"$REPO\""
134 if [ -z "$REPO" ]; then
135 echo "no repo given, use -r flag"
136 exit 1
139 # make sure we have a marks cache
140 if [ ! -f "$GIT_DIR/$PFX-$SFX_MARKS" ] ; then
141 touch "$GIT_DIR/$PFX-$SFX_MARKS"
144 # cleanup on exit
145 trap 'rm -f "$GIT_DIR/$PFX-$SFX_MARKS.old" "$GIT_DIR/$PFX-$SFX_MARKS.tmp"' 0
147 _err1=
148 _err2=
149 exec 3>&1
150 { read -r _err1 || :; read -r _err2 || :; } <<-EOT
152 exec 4>&3 3>&1 1>&4 4>&-
154 _e1=0
155 GIT_DIR="$GIT_DIR" $PYTHON "$ROOT/hg-fast-export.py" \
156 --repo "$REPO" \
157 --marks "$GIT_DIR/$PFX-$SFX_MARKS" \
158 --mapping "$GIT_DIR/$PFX-$SFX_MAPPING" \
159 --heads "$GIT_DIR/$PFX-$SFX_HEADS" \
160 --status "$GIT_DIR/$PFX-$SFX_STATE" \
161 "$@" 3>&- || _e1=$?
162 echo $_e1 >&3
163 } | \
165 _e2=0
166 git fast-import $GFI_OPTS --export-marks="$GIT_DIR/$PFX-$SFX_MARKS.tmp" 3>&- || _e2=$?
167 echo $_e2 >&3
171 exec 3>&-
172 [ "$_err1" = 0 -a "$_err2" = 0 ] || exit 1
174 # move recent marks cache out of the way...
175 if [ -f "$GIT_DIR/$PFX-$SFX_MARKS" ] ; then
176 mv "$GIT_DIR/$PFX-$SFX_MARKS" "$GIT_DIR/$PFX-$SFX_MARKS.old"
177 else
178 touch "$GIT_DIR/$PFX-$SFX_MARKS.old"
181 # ...to create a new merged one
182 cat "$GIT_DIR/$PFX-$SFX_MARKS.old" "$GIT_DIR/$PFX-$SFX_MARKS.tmp" \
183 | uniq > "$GIT_DIR/$PFX-$SFX_MARKS"
185 # save SHA1s of current heads for incremental imports
186 # and connectivity (plus sanity checking)
187 for head in `git branch | sed 's#^..##'` ; do
188 id="`git rev-parse refs/heads/$head`"
189 echo ":$head $id"
190 done > "$GIT_DIR/$PFX-$SFX_HEADS"
192 # check diff with color:
193 # ( for i in `find . -type f | grep -v '\.git'` ; do diff -u $i $REPO/$i ; done | cdiff ) | less -r