Move filter_contents to plugin system
[fast-export.git] / hg-fast-export.sh
blob62392530136eb23e058d3e04b2aed9cfc63bd3b7
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 2>&1 ; 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:-python2}
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>
58 --mappings-are-raw Assume mappings are raw <key>=<value> lines
59 --filter-contents <cmd> Pipe contents of each exported file through <cmd>
60 with <file-path> <hg-hash> <is-binary> as arguments
61 --plugin <plugin=init> Add a plugin with the given init string (repeatable)
62 --plugin-path <plugin-path> Add an additional plugin lookup path
64 case "$1" in
65 -h|--help)
66 echo "usage: $(basename "$0") $USAGE"
67 echo ""
68 echo "$LONG_USAGE"
69 exit 0
70 esac
72 IS_BARE=$(git rev-parse --is-bare-repository) \
73 || (echo "Could not find git repo" ; exit 1)
74 if test "z$IS_BARE" != ztrue; then
75 # This is not a bare repo, cd to the toplevel
76 TOPLEVEL=$(git rev-parse --show-toplevel) \
77 || (echo "Could not find git repo toplevel" ; exit 1)
78 cd "$TOPLEVEL" || exit 1
80 GIT_DIR=$(git rev-parse --git-dir) || (echo "Could not find git repo" ; exit 1)
83 IGNORECASEWARN=""
84 IGNORECASE=`git config core.ignoreCase`
85 if [ "true" = "$IGNORECASE" ]; then
86 IGNORECASEWARN="true"
87 fi;
90 while case "$#" in 0) break ;; esac
92 case "$1" in
93 -r|--r|--re|--rep|--repo)
94 shift
95 REPO="$1"
97 --q|--qu|--qui|--quie|--quiet)
98 GFI_OPTS="$GFI_OPTS --quiet"
100 --force)
101 # pass --force to git-fast-import and hg-fast-export.py
102 GFI_OPTS="$GFI_OPTS --force"
103 IGNORECASEWARN="";
104 break
107 # pass any other options down to hg2git.py
108 break
111 break
113 esac
114 shift
115 done
117 if [ ! -z "$IGNORECASEWARN" ]; then
118 echo "Error: The option core.ignoreCase is set to true in the git"
119 echo "repository. This will produce empty changesets for renames that just"
120 echo "change the case of the file name."
121 echo "Use --force to skip this check or change the option with"
122 echo "git config core.ignoreCase false"
123 exit 1
126 # Make a backup copy of each state file
127 for i in $SFX_STATE $SFX_MARKS $SFX_MAPPING $SFX_HEADS ; do
128 if [ -f "$GIT_DIR/$PFX-$i" ] ; then
129 cp "$GIT_DIR/$PFX-$i" "$GIT_DIR/$PFX-$i~"
131 done
133 # for convenience: get default repo from state file
134 if [ x"$REPO" = x -a -f "$GIT_DIR/$PFX-$SFX_STATE" ] ; then
135 REPO="`grep '^:repo ' "$GIT_DIR/$PFX-$SFX_STATE" | cut -d ' ' -f 2`"
136 echo "Using last hg repository \"$REPO\""
139 if [ -z "$REPO" ]; then
140 echo "no repo given, use -r flag"
141 exit 1
144 # make sure we have a marks cache
145 if [ ! -f "$GIT_DIR/$PFX-$SFX_MARKS" ] ; then
146 touch "$GIT_DIR/$PFX-$SFX_MARKS"
149 # cleanup on exit
150 trap 'rm -f "$GIT_DIR/$PFX-$SFX_MARKS.old" "$GIT_DIR/$PFX-$SFX_MARKS.tmp"' 0
152 _err1=
153 _err2=
154 exec 3>&1
155 { read -r _err1 || :; read -r _err2 || :; } <<-EOT
157 exec 4>&3 3>&1 1>&4 4>&-
159 _e1=0
160 GIT_DIR="$GIT_DIR" "$PYTHON" "$ROOT/hg-fast-export.py" \
161 --repo "$REPO" \
162 --marks "$GIT_DIR/$PFX-$SFX_MARKS" \
163 --mapping "$GIT_DIR/$PFX-$SFX_MAPPING" \
164 --heads "$GIT_DIR/$PFX-$SFX_HEADS" \
165 --status "$GIT_DIR/$PFX-$SFX_STATE" \
166 "$@" 3>&- || _e1=$?
167 echo $_e1 >&3
168 } | \
170 _e2=0
171 git fast-import $GFI_OPTS --export-marks="$GIT_DIR/$PFX-$SFX_MARKS.tmp" 3>&- || _e2=$?
172 echo $_e2 >&3
176 exec 3>&-
177 [ "$_err1" = 0 -a "$_err2" = 0 ] || exit 1
179 # move recent marks cache out of the way...
180 if [ -f "$GIT_DIR/$PFX-$SFX_MARKS" ] ; then
181 mv "$GIT_DIR/$PFX-$SFX_MARKS" "$GIT_DIR/$PFX-$SFX_MARKS.old"
182 else
183 touch "$GIT_DIR/$PFX-$SFX_MARKS.old"
186 # ...to create a new merged one
187 cat "$GIT_DIR/$PFX-$SFX_MARKS.old" "$GIT_DIR/$PFX-$SFX_MARKS.tmp" \
188 | uniq > "$GIT_DIR/$PFX-$SFX_MARKS"
190 # save SHA1s of current heads for incremental imports
191 # and connectivity (plus sanity checking)
192 for head in `git branch | sed 's#^..##'` ; do
193 id="`git rev-parse refs/heads/$head`"
194 echo ":$head $id"
195 done > "$GIT_DIR/$PFX-$SFX_HEADS"
197 # check diff with color:
198 # ( for i in `find . -type f | grep -v '\.git'` ; do diff -u $i $REPO/$i ; done | cdiff ) | less -r