Code

the use of 'tr' in the test suite isn't really portable
[git.git] / git-filter-branch.sh
index a4b6577bd496819232754c1275b7c1e798b0470c..3bb2f676bde370b8667a7999ba43706dbd9b9375 100755 (executable)
@@ -8,6 +8,9 @@
 # a new branch. You can specify a number of filters to modify the commits,
 # files and trees.
 
+# The following functions will also be available in the commit filter:
+
+functions=$(cat << \EOF
 warn () {
         echo "$*" >&2
 }
@@ -46,6 +49,10 @@ die()
        echo "$*" >&2
        exit 1
 }
+EOF
+)
+
+eval "$functions"
 
 # When piped a commit, output a script to set the ident of either
 # "author" or "committer
@@ -59,17 +66,17 @@ set_ident () {
                        h
                        s/^'$lid' \([^<]*\) <[^>]*> .*$/\1/
                        s/'\''/'\''\'\'\''/g
-                       s/.*/export GIT_'$uid'_NAME='\''&'\''/p
+                       s/.*/GIT_'$uid'_NAME='\''&'\''; export GIT_'$uid'_NAME/p
 
                        g
                        s/^'$lid' [^<]* <\([^>]*\)> .*$/\1/
                        s/'\''/'\''\'\'\''/g
-                       s/.*/export GIT_'$uid'_EMAIL='\''&'\''/p
+                       s/.*/GIT_'$uid'_EMAIL='\''&'\''; export GIT_'$uid'_EMAIL/p
 
                        g
                        s/^'$lid' [^<]* <[^>]*> \(.*\)$/\1/
                        s/'\''/'\''\'\'\''/g
-                       s/.*/export GIT_'$uid'_DATE='\''&'\''/p
+                       s/.*/GIT_'$uid'_DATE='\''&'\''; export GIT_'$uid'_DATE/p
 
                        q
                }
@@ -77,14 +84,9 @@ set_ident () {
 
        LANG=C LC_ALL=C sed -ne "$pick_id_script"
        # Ensure non-empty id name.
-       echo "[ -n \"\$GIT_${uid}_NAME\" ] || export GIT_${uid}_NAME=\"\${GIT_${uid}_EMAIL%%@*}\""
+       echo "case \"\$GIT_${uid}_NAME\" in \"\") GIT_${uid}_NAME=\"\${GIT_${uid}_EMAIL%%@*}\" && export GIT_${uid}_NAME;; esac"
 }
 
-# This script can be sourced by the commit filter to get the functions
-test "a$SOURCE_FUNCTIONS" = a1 && return
-this_script="$(cd "$(dirname "$0")"; pwd)"/$(basename "$0")
-export this_script
-
 USAGE="[--env-filter <command>] [--tree-filter <command>] \
 [--index-filter <command>] [--parent-filter <command>] \
 [--msg-filter <command>] [--commit-filter <command>] \
@@ -92,8 +94,13 @@ USAGE="[--env-filter <command>] [--tree-filter <command>] \
 [--original <namespace>] [-d <directory>] [-f | --force] \
 [<rev-list options>...]"
 
+OPTIONS_SPEC=
 . git-sh-setup
 
+git diff-files --quiet &&
+       git diff-index --cached --quiet HEAD -- ||
+       die "Cannot rewrite branch(es) with a dirty working directory."
+
 tempdir=.git-rewrite
 filter_env=
 filter_tree=
@@ -105,8 +112,9 @@ filter_tag_name=
 filter_subdir=
 orig_namespace=refs/original/
 force=
-while case "$#" in 0) usage;; esac
+while :
 do
+       test $# = 0 && usage
        case "$1" in
        --)
                shift
@@ -150,7 +158,7 @@ do
                filter_msg="$OPTARG"
                ;;
        --commit-filter)
-               filter_commit='SOURCE_FUNCTIONS=1 . "$this_script";'" $OPTARG"
+               filter_commit="$functions; $OPTARG"
                ;;
        --tag-name-filter)
                filter_tag_name="$OPTARG"
@@ -195,7 +203,11 @@ do
        esac
 done < "$tempdir"/backup-refs
 
-export GIT_DIR GIT_WORK_TREE=.
+ORIG_GIT_DIR="$GIT_DIR"
+ORIG_GIT_WORK_TREE="$GIT_WORK_TREE"
+ORIG_GIT_INDEX_FILE="$GIT_INDEX_FILE"
+GIT_WORK_TREE=.
+export GIT_DIR GIT_WORK_TREE
 
 # These refs should be updated if their heads were rewritten
 
@@ -220,7 +232,8 @@ done > "$tempdir"/heads
 test -s "$tempdir"/heads ||
        die "Which ref do you want to rewrite?"
 
-export GIT_INDEX_FILE="$(pwd)/../index"
+GIT_INDEX_FILE="$(pwd)/../index"
+export GIT_INDEX_FILE
 git read-tree || die "Could not seed the index"
 
 ret=0
@@ -256,7 +269,8 @@ while read commit parents; do
                git read-tree -i -m $commit:"$filter_subdir"
        esac || die "Could not initialize the index"
 
-       export GIT_COMMIT=$commit
+       GIT_COMMIT=$commit
+       export GIT_COMMIT
        git cat-file commit "$commit" >../commit ||
                die "Cannot read commit $commit"
 
@@ -276,7 +290,7 @@ while read commit parents; do
                eval "$filter_tree" < /dev/null ||
                        die "tree filter failed: $filter_tree"
 
-               git diff-index -r $commit | cut -f 2- | tr '\n' '\0' | \
+               git diff-index -r $commit | cut -f 2- | tr '\n' '\000' | \
                        xargs -0 git update-index --add --replace --remove
                git ls-files -z --others | \
                        xargs -0 git update-index --add --replace --remove
@@ -390,7 +404,8 @@ if [ "$filter_tag_name" ]; then
 
                [ -f "../map/$sha1" ] || continue
                new_sha1="$(cat "../map/$sha1")"
-               export GIT_COMMIT="$sha1"
+               GIT_COMMIT="$sha1"
+               export GIT_COMMIT
                new_ref="$(echo "$ref" | eval "$filter_tag_name")" ||
                        die "tag name filter failed: $filter_tag_name"
 
@@ -412,4 +427,12 @@ echo
 test $count -gt 0 && echo "These refs were rewritten:"
 git show-ref | grep ^"$orig_namespace"
 
+unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE
+test -z "$ORIG_GIT_DIR" || GIT_DIR="$ORIG_GIT_DIR" && export GIT_DIR
+test -z "$ORIG_GIT_WORK_TREE" || GIT_WORK_TREE="$ORIG_GIT_WORK_TREE" &&
+       export GIT_WORK_TREE
+test -z "$ORIG_GIT_INDEX_FILE" || GIT_INDEX_FILE="$ORIG_GIT_INDEX_FILE" &&
+       export GIT_INDEX_FILE
+git read-tree -u -m HEAD
+
 exit $ret