summaryrefslogtreecommitdiff
path: root/git-am.sh
diff options
context:
space:
mode:
Diffstat (limited to 'git-am.sh')
-rwxr-xr-xgit-am.sh218
1 files changed, 166 insertions, 52 deletions
diff --git a/git-am.sh b/git-am.sh
index d64d997..6cdd591 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -5,7 +5,7 @@
SUBDIRECTORY_OK=Yes
OPTIONS_KEEPDASHDASH=
OPTIONS_SPEC="\
-git am [options] [<mbox>|<Maildir>...]
+git am [options] [(<mbox>|<Maildir>)...]
git am [options] (--resolved | --skip | --abort)
--
i,interactive run interactively
@@ -15,18 +15,25 @@ q,quiet be quiet
s,signoff add a Signed-off-by line to the commit message
u,utf8 recode into utf8 (default)
k,keep pass -k flag to git-mailinfo
+keep-cr pass --keep-cr flag to git-mailsplit for mbox format
+no-keep-cr do not pass --keep-cr flag to git-mailsplit independent of am.keepcr
+c,scissors strip everything before a scissors line
whitespace= pass it through git-apply
+ignore-space-change pass it through git-apply
+ignore-whitespace pass it through git-apply
directory= pass it through git-apply
C= pass it through git-apply
p= pass it through git-apply
patch-format= format the patch(es) are in
reject pass it through git-apply
resolvemsg= override error message when patch failure occurs
-r,resolved to be used after a patch failure
+continue continue applying patches after resolving a conflict
+r,resolved synonyms for --continue
skip skip the current patch
abort restore the original branch and abort the patching operation.
committer-date-is-author-date lie about committer date
ignore-date use current timestamp for author date
+rerere-autoupdate update the index with reused conflict resolution if possible
rebasing* (internal use for git-rebase)"
. git-sh-setup
@@ -45,29 +52,52 @@ else
HAS_HEAD=
fi
+cmdline="git am"
+if test '' != "$interactive"
+then
+ cmdline="$cmdline -i"
+fi
+if test '' != "$threeway"
+then
+ cmdline="$cmdline -3"
+fi
+
sq () {
git rev-parse --sq-quote "$@"
}
stop_here () {
echo "$1" >"$dotest/next"
+ git rev-parse --verify -q HEAD >"$dotest/abort-safety"
exit 1
}
+safe_to_abort () {
+ if test -f "$dotest/dirtyindex"
+ then
+ return 1
+ fi
+
+ if ! test -s "$dotest/abort-safety"
+ then
+ return 0
+ fi
+
+ abort_safety=$(cat "$dotest/abort-safety")
+ if test "z$(git rev-parse --verify -q HEAD)" = "z$abort_safety"
+ then
+ return 0
+ fi
+ echo >&2 "You seem to have moved HEAD since the last 'am' failure."
+ echo >&2 "Not rewinding to ORIG_HEAD"
+ return 1
+}
+
stop_here_user_resolve () {
if [ -n "$resolvemsg" ]; then
printf '%s\n' "$resolvemsg"
stop_here $1
fi
- cmdline="git am"
- if test '' != "$interactive"
- then
- cmdline="$cmdline -i"
- fi
- if test '' != "$threeway"
- then
- cmdline="$cmdline -3"
- fi
echo "When you have resolved this problem run \"$cmdline --resolved\"."
echo "If you would prefer to skip this patch, instead run \"$cmdline --skip\"."
echo "To restore the original branch and stop patching run \"$cmdline --abort\"."
@@ -129,10 +159,10 @@ It does not apply to blobs recorded in its index."
export GITHEAD_$his_tree
if test -n "$GIT_QUIET"
then
- export GIT_MERGE_VERBOSITY=0
+ GIT_MERGE_VERBOSITY=0 && export GIT_MERGE_VERBOSITY
fi
git-merge-recursive $orig_tree -- HEAD $his_tree || {
- git rerere
+ git rerere $allow_rerere_autoupdate
echo Failed to merge in the changes.
exit 1
}
@@ -191,13 +221,34 @@ check_patch_format () {
esac
;;
esac
+ if test -z "$patch_format" &&
+ test -n "$l1" &&
+ test -n "$l2" &&
+ test -n "$l3"
+ then
+ # This begins with three non-empty lines. Is this a
+ # piece of e-mail a-la RFC2822? Grab all the headers,
+ # discarding the indented remainder of folded lines,
+ # and see if it looks like that they all begin with the
+ # header field names...
+ tr -d '\015' <"$1" |
+ sed -n -e '/^$/q' -e '/^[ ]/d' -e p |
+ sane_egrep -v '^[!-9;-~]+:' >/dev/null ||
+ patch_format=mbox
+ fi
} < "$1" || clean_abort
}
split_patches () {
case "$patch_format" in
mbox)
- git mailsplit -d"$prec" -o"$dotest" -b -- "$@" > "$dotest/last" ||
+ if test -n "$rebasing" || test t = "$keepcr"
+ then
+ keep_cr=--keep-cr
+ else
+ keep_cr=
+ fi
+ git mailsplit -d"$prec" -o"$dotest" -b $keep_cr -- "$@" > "$dotest/last" ||
clean_abort
;;
stgit-series)
@@ -254,18 +305,28 @@ split_patches () {
msgnum=
;;
*)
- clean_abort "Patch format $patch_format is not supported."
+ if test -n "$parse_patch" ; then
+ clean_abort "Patch format $patch_format is not supported."
+ else
+ clean_abort "Patch format detection failed."
+ fi
;;
esac
}
prec=4
dotest="$GIT_DIR/rebase-apply"
-sign= utf8=t keep= skip= interactive= resolved= rebasing= abort=
-resolvemsg= resume=
+sign= utf8=t keep= keepcr= skip= interactive= resolved= rebasing= abort=
+resolvemsg= resume= scissors= no_inbody_headers=
git_apply_opt=
committer_date_is_author_date=
ignore_date=
+allow_rerere_autoupdate=
+
+if test "$(git config --bool --get am.keepcr)" = true
+then
+ keepcr=t
+fi
while test $# != 0
do
@@ -284,14 +345,18 @@ do
utf8= ;;
-k|--keep)
keep=t ;;
- -r|--resolved)
+ -c|--scissors)
+ scissors=t ;;
+ --no-scissors)
+ scissors=f ;;
+ -r|--resolved|--continue)
resolved=t ;;
--skip)
skip=t ;;
--abort)
abort=t ;;
--rebasing)
- rebasing=t threeway=t keep=t ;;
+ rebasing=t threeway=t keep=t scissors=f no_inbody_headers=t ;;
-d|--dotest)
die "-d option is no longer supported. Do not use."
;;
@@ -303,14 +368,20 @@ do
git_apply_opt="$git_apply_opt $(sq "$1$2")"; shift ;;
--patch-format)
shift ; patch_format="$1" ;;
- --reject)
+ --reject|--ignore-whitespace|--ignore-space-change)
git_apply_opt="$git_apply_opt $1" ;;
--committer-date-is-author-date)
committer_date_is_author_date=t ;;
--ignore-date)
ignore_date=t ;;
+ --rerere-autoupdate|--no-rerere-autoupdate)
+ allow_rerere_autoupdate="$1" ;;
-q|--quiet)
GIT_QUIET=t ;;
+ --keep-cr)
+ keepcr=t ;;
+ --no-keep-cr)
+ keepcr=f ;;
--)
shift; break ;;
*)
@@ -370,10 +441,11 @@ then
exec git rebase --abort
fi
git rerere clear
- test -f "$dotest/dirtyindex" || {
+ if safe_to_abort
+ then
git read-tree --reset -u HEAD ORIG_HEAD
git reset ORIG_HEAD
- }
+ fi
rm -fr "$dotest"
exit ;;
esac
@@ -395,12 +467,12 @@ else
set x
first=
}
- case "$arg" in
- /*)
- set "$@" "$arg" ;;
- *)
- set "$@" "$prefix$arg" ;;
- esac
+ if is_absolute_path "$arg"
+ then
+ set "$@" "$arg"
+ else
+ set "$@" "$prefix$arg"
+ fi
done
shift
fi
@@ -409,14 +481,16 @@ else
split_patches "$@"
- # -s, -u, -k, --whitespace, -3, -C, -q and -p flags are kept
- # for the resuming session after a patch failure.
- # -i can and must be given when resuming.
+ # -i can and must be given when resuming; everything
+ # else is kept
echo " $git_apply_opt" >"$dotest/apply-opt"
echo "$threeway" >"$dotest/threeway"
echo "$sign" >"$dotest/sign"
echo "$utf8" >"$dotest/utf8"
echo "$keep" >"$dotest/keep"
+ echo "$keepcr" >"$dotest/keepcr"
+ echo "$scissors" >"$dotest/scissors"
+ echo "$no_inbody_headers" >"$dotest/no_inbody_headers"
echo "$GIT_QUIET" >"$dotest/quiet"
echo 1 >"$dotest/next"
if test -n "$rebasing"
@@ -458,6 +532,24 @@ if test "$(cat "$dotest/keep")" = t
then
keep=-k
fi
+case "$(cat "$dotest/keepcr")" in
+t)
+ keepcr=--keep-cr ;;
+f)
+ keepcr=--no-keep-cr ;;
+esac
+case "$(cat "$dotest/scissors")" in
+t)
+ scissors=--scissors ;;
+f)
+ scissors=--no-scissors ;;
+esac
+if test "$(cat "$dotest/no_inbody_headers")" = t
+then
+ no_inbody_headers=--no-inbody-headers
+else
+ no_inbody_headers=
+fi
if test "$(cat "$dotest/quiet")" = t
then
GIT_QUIET=t
@@ -485,13 +577,6 @@ then
resume=
fi
-if test "$this" -gt "$last"
-then
- say Nothing to do.
- rm -fr "$dotest"
- exit
-fi
-
while test "$this" -le "$last"
do
msgnum=`printf "%0${prec}d" $this`
@@ -512,19 +597,22 @@ do
# by the user, or the user can tell us to do so by --resolved flag.
case "$resume" in
'')
- git mailinfo $keep $utf8 "$dotest/msg" "$dotest/patch" \
+ git mailinfo $keep $no_inbody_headers $scissors $utf8 "$dotest/msg" "$dotest/patch" \
<"$dotest/$msgnum" >"$dotest/info" ||
stop_here $this
# skip pine's internal folder data
- grep '^Author: Mail System Internal Data$' \
+ sane_grep '^Author: Mail System Internal Data$' \
<"$dotest"/info >/dev/null &&
go_next && continue
test -s "$dotest/patch" || {
echo "Patch is empty. Was it split wrong?"
+ echo "If you would prefer to skip this patch, instead run \"$cmdline --skip\"."
+ echo "To restore the original branch and stop patching run \"$cmdline --abort\"."
stop_here $this
}
+ rm -f "$dotest/original-commit" "$dotest/author-script"
if test -f "$dotest/rebasing" &&
commit=$(sed -e 's/^From \([0-9a-f]*\) .*/\1/' \
-e q "$dotest/$msgnum") &&
@@ -532,19 +620,27 @@ do
then
git cat-file commit "$commit" |
sed -e '1,/^$/d' >"$dotest/msg-clean"
+ echo "$commit" > "$dotest/original-commit"
+ get_author_ident_from_commit "$commit" > "$dotest/author-script"
else
- SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$dotest/info")"
- case "$keep_subject" in -k) SUBJECT="[PATCH] $SUBJECT" ;; esac
-
- (printf '%s\n\n' "$SUBJECT"; cat "$dotest/msg") |
- git stripspace > "$dotest/msg-clean"
+ {
+ sed -n '/^Subject/ s/Subject: //p' "$dotest/info"
+ echo
+ cat "$dotest/msg"
+ } |
+ git stripspace > "$dotest/msg-clean"
fi
;;
esac
- GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$dotest/info")"
- GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$dotest/info")"
- GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$dotest/info")"
+ if test -f "$dotest/author-script"
+ then
+ eval $(cat "$dotest/author-script")
+ else
+ GIT_AUTHOR_NAME="$(sed -n '/^Author/ s/Author: //p' "$dotest/info")"
+ GIT_AUTHOR_EMAIL="$(sed -n '/^Email/ s/Email: //p' "$dotest/info")"
+ GIT_AUTHOR_DATE="$(sed -n '/^Date/ s/Date: //p' "$dotest/info")"
+ fi
if test -z "$GIT_AUTHOR_EMAIL"
then
@@ -612,14 +708,20 @@ do
[eE]*) git_editor "$dotest/final-commit"
action=again ;;
[vV]*) action=again
- LESS=-S ${PAGER:-less} "$dotest/patch" ;;
+ git_pager "$dotest/patch" ;;
*) action=again ;;
esac
done
else
action=yes
fi
- FIRSTLINE=$(sed 1q "$dotest/final-commit")
+
+ if test -f "$dotest/final-commit"
+ then
+ FIRSTLINE=$(sed 1q "$dotest/final-commit")
+ else
+ FIRSTLINE=""
+ fi
if test $action = skip
then
@@ -655,6 +757,8 @@ do
resolved=
git diff-index --quiet --cached HEAD -- && {
echo "No changes - did you forget to use 'git add'?"
+ echo "If there is nothing left to stage, chances are that something else"
+ echo "already introduced the same changes; you might want to skip this patch."
stop_here_user_resolve $this
}
unmerged=$(git ls-files -u)
@@ -669,7 +773,7 @@ do
;;
esac
- if test $apply_status = 1 && test "$threeway" = t
+ if test $apply_status != 0 && test "$threeway" = t
then
if (fall_back_3way)
then
@@ -714,6 +818,10 @@ do
git update-ref -m "$GIT_REFLOG_ACTION: $FIRSTLINE" HEAD $commit $parent ||
stop_here $this
+ if test -f "$dotest/original-commit"; then
+ echo "$(cat "$dotest/original-commit") $commit" >> "$dotest/rewritten"
+ fi
+
if test -x "$GIT_DIR"/hooks/post-applypatch
then
"$GIT_DIR"/hooks/post-applypatch
@@ -722,6 +830,12 @@ do
go_next
done
-git gc --auto
+if test -s "$dotest"/rewritten; then
+ git notes copy --for-rewrite=rebase < "$dotest"/rewritten
+ if test -x "$GIT_DIR"/hooks/post-rewrite; then
+ "$GIT_DIR"/hooks/post-rewrite rebase < "$dotest"/rewritten
+ fi
+fi
rm -fr "$dotest"
+git gc --auto