summaryrefslogtreecommitdiff
path: root/git-am.sh
diff options
context:
space:
mode:
Diffstat (limited to 'git-am.sh')
-rwxr-xr-xgit-am.sh335
1 files changed, 240 insertions, 95 deletions
diff --git a/git-am.sh b/git-am.sh
index 3afa92b85a..016b5056eb 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -5,37 +5,47 @@
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
b,binary* (historical option -- no-op)
3,3way allow fall back on 3way merging if needed
+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
+exclude= 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
+. git-sh-i18n
prefix=$(git rev-parse --show-prefix)
set_reflog_action am
require_work_tree
cd_to_toplevel
git var GIT_COMMITTER_IDENT >/dev/null ||
- die "You need to set your committer info first"
+ die "$(gettext "You need to set your committer info first")"
if git rev-parse --verify -q HEAD >/dev/null
then
@@ -44,32 +54,55 @@ 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
+ gettextln "You seem to have moved HEAD since the last 'am' failure.
+Not rewinding to ORIG_HEAD" >&2
+ 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\"."
+ eval_gettextln "When you have resolved this problem run \"\$cmdline --resolved\".
+If you would prefer to skip this patch, instead run \"\$cmdline --skip\".
+To restore the original branch and stop patching run \"\$cmdline --abort\"."
stop_here $1
}
@@ -83,7 +116,7 @@ go_next () {
cannot_fallback () {
echo "$1"
- echo "Cannot fall back to three-way merge."
+ gettextln "Cannot fall back to three-way merge."
exit 1
}
@@ -98,17 +131,17 @@ fall_back_3way () {
"$dotest/patch" &&
GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
git write-tree >"$dotest/patch-merge-base+" ||
- cannot_fallback "Repository lacks necessary blobs to fall back on 3-way merge."
+ cannot_fallback "$(gettext "Repository lacks necessary blobs to fall back on 3-way merge.")"
- echo Using index info to reconstruct a base tree...
+ say Using index info to reconstruct a base tree...
if GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
git apply --cached <"$dotest/patch"
then
mv "$dotest/patch-merge-base+" "$dotest/patch-merge-base"
mv "$dotest/patch-merge-tmp-index" "$dotest/patch-merge-index"
else
- cannot_fallback "Did you hand edit your patch?
-It does not apply to blobs recorded in its index."
+ cannot_fallback "$(gettext "Did you hand edit your patch?
+It does not apply to blobs recorded in its index.")"
fi
test -f "$dotest/patch-merge-index" &&
@@ -116,7 +149,7 @@ It does not apply to blobs recorded in its index."
orig_tree=$(cat "$dotest/patch-merge-base") &&
rm -fr "$dotest"/patch-merge-* || exit 1
- echo Falling back to patching base and 3-way merge...
+ say "$(gettext "Falling back to patching base and 3-way merge...")"
# This is not so wrong. Depending on which base we picked,
# orig_tree may be wildly different from ours, but his_tree
@@ -126,14 +159,24 @@ It does not apply to blobs recorded in its index."
eval GITHEAD_$his_tree='"$FIRSTLINE"'
export GITHEAD_$his_tree
+ if test -n "$GIT_QUIET"
+ then
+ 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
}
unset GITHEAD_$his_tree
}
+clean_abort () {
+ test $# = 0 || echo >&2 "$@"
+ rm -fr "$dotest"
+ exit 1
+}
+
patch_format=
check_patch_format () {
@@ -151,10 +194,15 @@ check_patch_format () {
return 0
fi
- # otherwise, check the first few lines of the first patch to try
- # to detect its format
+ # otherwise, check the first few non-blank lines of the first
+ # patch to try to detect its format
{
- read l1
+ # Start from first line containing non-whitespace
+ l1=
+ while test -z "$l1"
+ do
+ read l1
+ done
read l2
read l3
case "$l1" in
@@ -180,22 +228,40 @@ check_patch_format () {
esac
;;
esac
- } < "$1"
+ 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" || {
- rm -fr "$dotest"
- exit 1
- }
+ 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)
if test $# -ne 1
then
- echo "Only one StGIT patch series can be applied at once"
- exit 1
+ clean_abort "$(gettext "Only one StGIT patch series can be applied at once")"
fi
series_dir=`dirname "$1"`
series_file="$1"
@@ -210,7 +276,7 @@ split_patches () {
shift
# remove the arg coming from the first-line comment
shift
- } < "$series_file"
+ } < "$series_file" || clean_abort
# set the patch format appropriately
patch_format=stgit
# now handle the actual StGIT patches
@@ -239,29 +305,36 @@ split_patches () {
print "Subject: ", $_ ;
$subject = 1;
}
- ' < "$stgit" > "$dotest/$msgnum" || {
- echo "Failed to import $patch_format patch $stgit"
- exit 1
- }
+ ' < "$stgit" > "$dotest/$msgnum" || clean_abort
done
echo "$this" > "$dotest/last"
this=
msgnum=
;;
*)
- echo "Patch format $patch_format is not supported."
- exit 1
+ if test -n "$patch_format"
+ then
+ clean_abort "$(eval_gettext "Patch format \$patch_format is not supported.")"
+ else
+ clean_abort "$(gettext "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
@@ -280,31 +353,43 @@ 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."
+ die "$(gettext "-d option is no longer supported. Do not use.")"
;;
--resolvemsg)
shift; resolvemsg=$1 ;;
- --whitespace|--directory)
+ --whitespace|--directory|--exclude)
git_apply_opt="$git_apply_opt $(sq "$1=$2")"; shift ;;
-C|-p)
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 ;;
*)
@@ -344,12 +429,12 @@ then
false
;;
esac ||
- die "previous rebase directory $dotest still exists but mbox given."
+ die "$(eval_gettext "previous rebase directory \$dotest still exists but mbox given.")"
resume=yes
case "$skip,$abort" in
t,t)
- die "Please make up your mind. --skip or --abort?"
+ die "$(gettext "Please make up your mind. --skip or --abort?")"
;;
t,)
git rerere clear
@@ -364,10 +449,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
@@ -375,7 +461,7 @@ then
else
# Make sure we are not given --skip, --resolved, nor --abort
test "$skip$resolved$abort" = "" ||
- die "Resolve operation not in progress, we are not resuming."
+ die "$(gettext "Resolve operation not in progress, we are not resuming.")"
# Start afresh.
mkdir -p "$dotest" || exit
@@ -389,12 +475,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
@@ -403,14 +489,17 @@ else
split_patches "$@"
- # -s, -u, -k, --whitespace, -3, -C 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"
then
@@ -426,6 +515,8 @@ else
fi
fi
+git update-index -q --refresh
+
case "$resolved" in
'')
case "$HAS_HEAD" in
@@ -437,7 +528,7 @@ case "$resolved" in
if test "$files"
then
test -n "$HAS_HEAD" && : >"$dotest/dirtyindex"
- die "Dirty index: cannot apply patches (dirty: $files)"
+ die "$(eval_gettext "Dirty index: cannot apply patches (dirty: \$files)")"
fi
esac
@@ -451,6 +542,28 @@ 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
+fi
if test "$(cat "$dotest/threeway")" = t
then
threeway=t
@@ -474,13 +587,6 @@ then
resume=
fi
-if test "$this" -gt "$last"
-then
- echo Nothing to do.
- rm -fr "$dotest"
- exit
-fi
-
while test "$this" -le "$last"
do
msgnum=`printf "%0${prec}d" $this`
@@ -501,19 +607,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?"
+ eval_gettextln "Patch is empty. Was it split wrong?
+If you would prefer to skip this patch, instead run \"\$cmdline --skip\".
+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") &&
@@ -521,23 +630,31 @@ 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
- echo "Patch does not have a valid e-mail address."
+ gettextln "Patch does not have a valid e-mail address."
stop_here $this
fi
@@ -584,15 +701,18 @@ do
if test "$interactive" = t
then
test -t 0 ||
- die "cannot be interactive without stdin connected to a terminal."
+ die "$(gettext "cannot be interactive without stdin connected to a terminal.")"
action=again
while test "$action" = again
do
- echo "Commit Body is:"
+ gettextln "Commit Body is:"
echo "--------------------------"
cat "$dotest/final-commit"
echo "--------------------------"
- printf "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
+ # TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
+ # in your translation. The program will only accept English
+ # input at this point.
+ gettext "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
read reply
case "$reply" in
[yY]*) action=yes ;;
@@ -601,14 +721,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
@@ -622,11 +748,18 @@ do
stop_here $this
fi
- printf 'Applying: %s\n' "$FIRSTLINE"
+ say "$(eval_gettext "Applying: \$FIRSTLINE")"
case "$resolved" in
'')
- eval 'git apply '"$git_apply_opt"' --index "$dotest/patch"'
+ # When we are allowed to fall back to 3-way later, don't give
+ # false errors during the initial attempt.
+ squelch=
+ if test "$threeway" = t
+ then
+ squelch='>/dev/null 2>&1 '
+ fi
+ eval "git apply $squelch$git_apply_opt"' --index "$dotest/patch"'
apply_status=$?
;;
t)
@@ -636,14 +769,16 @@ do
# working tree.
resolved=
git diff-index --quiet --cached HEAD -- && {
- echo "No changes - did you forget to use 'git add'?"
+ gettextln "No changes - did you forget to use 'git add'?
+If there is nothing left to stage, chances are that something else
+already introduced the same changes; you might want to skip this patch."
stop_here_user_resolve $this
}
unmerged=$(git ls-files -u)
if test -n "$unmerged"
then
- echo "You still have unmerged paths in your index"
- echo "did you forget to use 'git add'?"
+ gettextln "You still have unmerged paths in your index
+did you forget to use 'git add'?"
stop_here_user_resolve $this
fi
apply_status=0
@@ -651,14 +786,14 @@ do
;;
esac
- if test $apply_status = 1 && test "$threeway" = t
+ if test $apply_status != 0 && test "$threeway" = t
then
if (fall_back_3way)
then
# Applying the patch to an earlier tree and merging the
# result may have produced the same tree as ours.
git diff-index --quiet --cached HEAD -- && {
- echo No changes -- Patch already applied.
+ say "$(gettext "No changes -- Patch already applied.")"
go_next
continue
}
@@ -668,7 +803,7 @@ do
fi
if test $apply_status != 0
then
- printf 'Patch failed at %s %s\n' "$msgnum" "$FIRSTLINE"
+ eval_gettextln 'Patch failed at $msgnum $FIRSTLINE'
stop_here_user_resolve $this
fi
@@ -684,7 +819,7 @@ do
GIT_AUTHOR_DATE=
fi
parent=$(git rev-parse --verify -q HEAD) ||
- echo >&2 "applying to an empty history"
+ say >&2 "$(gettext "applying to an empty history")"
if test -n "$committer_date_is_author_date"
then
@@ -696,6 +831,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
@@ -704,6 +843,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