functions=$(cat << \EOF
warn () {
- echo "$*" >&2
+ echo "$*" >&2
}
map()
eval "$functions"
-# When piped a commit, output a script to set the ident of either
-# "author" or "committer
+finish_ident() {
+ # Ensure non-empty id name.
+ echo "case \"\$GIT_$1_NAME\" in \"\") GIT_$1_NAME=\"\${GIT_$1_EMAIL%%@*}\" && export GIT_$1_NAME;; esac"
+ # And make sure everything is exported.
+ echo "export GIT_$1_NAME"
+ echo "export GIT_$1_EMAIL"
+ echo "export GIT_$1_DATE"
+}
set_ident () {
- lid="$(echo "$1" | tr "[A-Z]" "[a-z]")"
- uid="$(echo "$1" | tr "[a-z]" "[A-Z]")"
- pick_id_script='
- /^'$lid' /{
- s/'\''/'\''\\'\'\''/g
- h
- s/^'$lid' \([^<]*\) <[^>]*> .*$/\1/
- s/'\''/'\''\'\'\''/g
- s/.*/GIT_'$uid'_NAME='\''&'\''; export GIT_'$uid'_NAME/p
-
- g
- s/^'$lid' [^<]* <\([^>]*\)> .*$/\1/
- s/'\''/'\''\'\'\''/g
- s/.*/GIT_'$uid'_EMAIL='\''&'\''; export GIT_'$uid'_EMAIL/p
-
- g
- s/^'$lid' [^<]* <[^>]*> \(.*\)$/\1/
- s/'\''/'\''\'\'\''/g
- s/.*/GIT_'$uid'_DATE='\''&'\''; export GIT_'$uid'_DATE/p
-
- q
- }
- '
-
- LANG=C LC_ALL=C sed -ne "$pick_id_script"
- # Ensure non-empty id name.
- echo "case \"\$GIT_${uid}_NAME\" in \"\") GIT_${uid}_NAME=\"\${GIT_${uid}_EMAIL%%@*}\" && export GIT_${uid}_NAME;; esac"
+ parse_ident_from_commit author AUTHOR committer COMMITTER
+ finish_ident AUTHOR
+ finish_ident COMMITTER
}
-USAGE="[--env-filter <command>] [--tree-filter <command>] \
-[--index-filter <command>] [--parent-filter <command>] \
-[--msg-filter <command>] [--commit-filter <command>] \
-[--tag-name-filter <command>] [--subdirectory-filter <directory>] \
-[--original <namespace>] [-d <directory>] [-f | --force] \
-[<rev-list options>...]"
+USAGE="[--env-filter <command>] [--tree-filter <command>]
+ [--index-filter <command>] [--parent-filter <command>]
+ [--msg-filter <command>] [--commit-filter <command>]
+ [--tag-name-filter <command>] [--subdirectory-filter <directory>]
+ [--original <namespace>] [-d <directory>] [-f | --force]
+ [<rev-list options>...]"
OPTIONS_SPEC=
. git-sh-setup
if [ "$(is_bare_repository)" = false ]; then
- git diff-files --ignore-submodules --quiet &&
- git diff-index --cached --quiet HEAD -- ||
- die "Cannot rewrite branch(es) with a dirty working directory."
+ require_clean_work_tree 'rewrite branches'
fi
tempdir=.git-rewrite
orig_namespace=refs/original/
force=
prune_empty=
+remap_to_ancestor=
while :
do
case "$1" in
force=t
continue
;;
+ --remap-to-ancestor)
+ # deprecated ($remap_to_ancestor is set now automatically)
+ shift
+ remap_to_ancestor=t
+ continue
+ ;;
--prune-empty)
shift
prune_empty=t
;;
--subdirectory-filter)
filter_subdir="$OPTARG"
+ remap_to_ancestor=t
;;
--original)
orig_namespace=$(expr "$OPTARG/" : '\(.*[^/]\)/*$')/
,*)
;;
*)
- die "Cannot set --prune-empty and --filter-commit at the same time"
+ die "Cannot set --prune-empty and --commit-filter at the same time"
esac
case "$force" in
GIT_INDEX_FILE="$(pwd)/../index"
export GIT_INDEX_FILE
-git read-tree || die "Could not seed the index"
# map old->new commit ids for rewriting parents
mkdir ../map || die "Could not create map/ directory"
+# we need "--" only if there are no path arguments in $@
+nonrevs=$(git rev-parse --no-revs "$@") || exit
+if test -z "$nonrevs"
+then
+ dashdash=--
+else
+ dashdash=
+ remap_to_ancestor=t
+fi
+
+rev_args=$(git rev-parse --revs-only "$@")
+
case "$filter_subdir" in
"")
- git rev-list --reverse --topo-order --default HEAD \
- --parents --simplify-merges "$@"
+ eval set -- "$(git rev-parse --sq --no-revs "$@")"
;;
*)
- git rev-list --reverse --topo-order --default HEAD \
- --parents --simplify-merges "$@" -- "$filter_subdir"
-esac > ../revs || die "Could not get the commits"
+ eval set -- "$(git rev-parse --sq --no-revs "$@" $dashdash \
+ "$filter_subdir")"
+ ;;
+esac
+
+git rev-list --reverse --topo-order --default HEAD \
+ --parents --simplify-merges $rev_args "$@" > ../revs ||
+ die "Could not get the commits"
commits=$(wc -l <../revs | tr -d " ")
test $commits -eq 0 && die "Found nothing to rewrite"
git cat-file commit "$commit" >../commit ||
die "Cannot read commit $commit"
- eval "$(set_ident AUTHOR <../commit)" ||
- die "setting author failed for commit $commit"
- eval "$(set_ident COMMITTER <../commit)" ||
- die "setting committer failed for commit $commit"
+ eval "$(set_ident <../commit)" ||
+ die "setting author/committer failed for commit $commit"
eval "$filter_env" < /dev/null ||
die "env filter failed: $filter_env"
die "tree filter failed: $filter_tree"
(
- git diff-index -r --name-only $commit &&
+ git diff-index -r --name-only --ignore-submodules $commit &&
git ls-files --others
) > "$tempdir"/tree-state || exit
git update-index --add --replace --remove --stdin \
sed -e '1,/^$/d' <../commit | \
eval "$filter_msg" > ../message ||
die "msg filter failed: $filter_msg"
- @SHELL_PATH@ -c "$filter_commit" "git commit-tree" \
+ workdir=$workdir @SHELL_PATH@ -c "$filter_commit" "git commit-tree" \
$(git write-tree) $parentstr < ../message > ../map/$commit ||
die "could not write rewritten commit"
done <../revs
-# In case of a subdirectory filter, it is possible that a specified head
-# is not in the set of rewritten commits, because it was pruned by the
-# revision walker. Fix it by mapping these heads to the unique nearest
-# ancestor that survived the pruning.
+# If we are filtering for paths, as in the case of a subdirectory
+# filter, it is possible that a specified head is not in the set of
+# rewritten commits, because it was pruned by the revision walker.
+# Ancestor remapping fixes this by mapping these heads to the unique
+# nearest ancestor that survived the pruning.
-if test "$filter_subdir"
+if test "$remap_to_ancestor" = t
then
while read ref
do
sha1=$(git rev-parse "$ref"^0)
test -f "$workdir"/../map/$sha1 && continue
- ancestor=$(git rev-list --simplify-merges -1 \
- $ref -- "$filter_subdir")
+ ancestor=$(git rev-list --simplify-merges -1 "$ref" "$@")
test "$ancestor" && echo $(map $ancestor) >> "$workdir"/../map/$sha1
done < "$tempdir"/heads
fi
"$new_sha1" "$new_ref"
git cat-file tag "$ref" |
sed -n \
- -e "1,/^$/{
+ -e '1,/^$/{
/^object /d
/^type /d
/^tag /d
- }" \
+ }' \
-e '/^-----BEGIN PGP SIGNATURE-----/q' \
-e 'p' ) |
git mktag) ||
die "Could not create new tag object for $ref"
if git cat-file tag "$ref" | \
- grep '^-----BEGIN PGP SIGNATURE-----' >/dev/null 2>&1
+ sane_grep '^-----BEGIN PGP SIGNATURE-----' >/dev/null 2>&1
then
warn "gpg signature stripped from tag object $sha1t"
fi