Turn the flags in struct dir_struct into a single variable
[gitweb.git] / git-am.sh
index 6809aa07f60165855ebc0192750e85569f9685ff..8bcb206022ae5d4ae82d2a37d8f79454c043ad78 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -2,16 +2,47 @@
 #
 # Copyright (c) 2005, 2006 Junio C Hamano
 
-USAGE='[--signoff] [--dotest=<dir>] [--keep] [--utf8 | --no-utf8]
-  [--3way] [--interactive] [--binary]
-  [--whitespace=<option>] [-C<n>] [-p<n>]
-  <mbox>|<Maildir>...
-  or, when resuming [--skip | --resolved]'
+SUBDIRECTORY_OK=Yes
+OPTIONS_KEEPDASHDASH=
+OPTIONS_SPEC="\
+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
+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
+whitespace=     pass it through git-apply
+directory=      pass it through git-apply
+C=              pass it through git-apply
+p=              pass it through git-apply
+reject          pass it through git-apply
+resolvemsg=     override error message when patch failure occurs
+r,resolved      to be used after a patch failure
+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
+rebasing*       (internal use for git-rebase)"
+
 . git-sh-setup
+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"
 
-git var GIT_COMMITTER_IDENT >/dev/null || exit
+sq () {
+       for sqarg
+       do
+               printf "%s" "$sqarg" |
+               sed -e 's/'\''/'\''\\'\'''\''/g' -e 's/.*/ '\''&'\''/'
+       done
+}
 
 stop_here () {
     echo "$1" >"$dotest/next"
@@ -23,7 +54,7 @@ stop_here_user_resolve () {
            printf '%s\n' "$resolvemsg"
            stop_here $1
     fi
-    cmdline=$(basename $0)
+    cmdline="git am"
     if test '' != "$interactive"
     then
         cmdline="$cmdline -i"
@@ -32,12 +63,9 @@ stop_here_user_resolve () {
     then
         cmdline="$cmdline -3"
     fi
-    if test '.dotest' != "$dotest"
-    then
-        cmdline="$cmdline -d=$dotest"
-    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\"."
 
     stop_here $1
 }
@@ -62,17 +90,15 @@ fall_back_3way () {
     mkdir "$dotest/patch-merge-tmp-dir"
 
     # First see if the patch records the index info that we can use.
-    git apply -z --index-info "$dotest/patch" \
-       >"$dotest/patch-merge-index-info" &&
-    GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
-    git update-index -z --index-info <"$dotest/patch-merge-index-info" &&
+    git apply --build-fake-ancestor "$dotest/patch-merge-tmp-index" \
+       "$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."
 
     echo Using index info to reconstruct a base tree...
     if GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
-       git apply $binary --cached <"$dotest/patch"
+       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"
@@ -94,7 +120,7 @@ It does not apply to blobs recorded in its index."
     # patch did not touch, so recursive ends up canceling them,
     # saying that we reverted all those changes.
 
-    eval GITHEAD_$his_tree='"$SUBJECT"'
+    eval GITHEAD_$his_tree='"$FIRSTLINE"'
     export GITHEAD_$his_tree
     git-merge-recursive $orig_tree -- HEAD $his_tree || {
            git rerere
@@ -105,56 +131,59 @@ It does not apply to blobs recorded in its index."
 }
 
 prec=4
-dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary=
+dotest="$GIT_DIR/rebase-apply"
+sign= utf8=t keep= skip= interactive= resolved= rebasing= abort=
 resolvemsg= resume=
 git_apply_opt=
+committer_date_is_author_date=
+ignore_date=
 
-while case "$#" in 0) break;; esac
+while test $# != 0
 do
        case "$1" in
-       -d=*|--d=*|--do=*|--dot=*|--dote=*|--dotes=*|--dotest=*)
-       dotest=`expr "z$1" : 'z-[^=]*=\(.*\)'`; shift ;;
-       -d|--d|--do|--dot|--dote|--dotes|--dotest)
-       case "$#" in 1) usage ;; esac; shift
-       dotest="$1"; shift;;
-
-       -i|--i|--in|--int|--inte|--inter|--intera|--interac|--interact|\
-       --interacti|--interactiv|--interactive)
-       interactive=t; shift ;;
-
-       -b|--b|--bi|--bin|--bina|--binar|--binary)
-       binary=t; shift ;;
-
-       -3|--3|--3w|--3wa|--3way)
-       threeway=t; shift ;;
-       -s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
-       sign=t; shift ;;
-       -u|--u|--ut|--utf|--utf8)
-       utf8=t; shift ;; # this is now default
-       --no-u|--no-ut|--no-utf|--no-utf8)
-       utf8=; shift ;;
-       -k|--k|--ke|--kee|--keep)
-       keep=t; shift ;;
-
-       -r|--r|--re|--res|--reso|--resol|--resolv|--resolve|--resolved)
-       resolved=t; shift ;;
-
-       --sk|--ski|--skip)
-       skip=t; shift ;;
-
-       --whitespace=*|-C*|-p*)
-       git_apply_opt="$git_apply_opt $1"; shift ;;
-
-       --resolvemsg=*)
-       resolvemsg=${1#--resolvemsg=}; shift ;;
-
+       -i|--interactive)
+               interactive=t ;;
+       -b|--binary)
+               : ;;
+       -3|--3way)
+               threeway=t ;;
+       -s|--signoff)
+               sign=t ;;
+       -u|--utf8)
+               utf8=t ;; # this is now default
+       --no-utf8)
+               utf8= ;;
+       -k|--keep)
+               keep=t ;;
+       -r|--resolved)
+               resolved=t ;;
+       --skip)
+               skip=t ;;
+       --abort)
+               abort=t ;;
+       --rebasing)
+               rebasing=t threeway=t keep=t ;;
+       -d|--dotest)
+               die "-d option is no longer supported.  Do not use."
+               ;;
+       --resolvemsg)
+               shift; resolvemsg=$1 ;;
+       --whitespace|--directory)
+               git_apply_opt="$git_apply_opt $(sq "$1=$2")"; shift ;;
+       -C|-p)
+               git_apply_opt="$git_apply_opt $(sq "$1$2")"; shift ;;
+       --reject)
+               git_apply_opt="$git_apply_opt $1" ;;
+       --committer-date-is-author-date)
+               committer_date_is_author_date=t ;;
+       --ignore-date)
+               ignore_date=t ;;
        --)
-       shift; break ;;
-       -*)
-       usage ;;
+               shift; break ;;
        *)
-       break ;;
+               usage ;;
        esac
+       shift
 done
 
 # If the dotest directory exists, but we have finished applying all the
@@ -170,7 +199,7 @@ fi
 
 if test -d "$dotest"
 then
-       case "$#,$skip$resolved" in
+       case "$#,$skip$resolved$abort" in
        0,*t*)
                # Explicit resume command and we do not have file, so
                # we are happy.
@@ -178,55 +207,89 @@ then
        0,)
                # No file input but without resume parameters; catch
                # user error to feed us a patch from standard input
-               # when there is already .dotest.  This is somewhat
+               # when there is already $dotest.  This is somewhat
                # unreliable -- stdin could be /dev/null for example
                # and the caller did not intend to feed us a patch but
                # wanted to continue unattended.
-               tty -s
+               test -t 0
                ;;
        *)
                false
                ;;
        esac ||
-       die "previous dotest directory $dotest still exists but mbox given."
+       die "previous rebase directory $dotest still exists but mbox given."
        resume=yes
+
+       case "$skip,$abort" in
+       t,)
+               git rerere clear
+               git read-tree --reset -u HEAD HEAD
+               orig_head=$(cat "$GIT_DIR/ORIG_HEAD")
+               git reset HEAD
+               git update-ref ORIG_HEAD $orig_head
+               ;;
+       ,t)
+               git rerere clear
+               git read-tree --reset -u HEAD ORIG_HEAD
+               git reset ORIG_HEAD
+               rm -fr "$dotest"
+               exit ;;
+       esac
 else
-       # Make sure we are not given --skip nor --resolved
-       test ",$skip,$resolved," = ,,, ||
+       # Make sure we are not given --skip, --resolved, nor --abort
+       test "$skip$resolved$abort" = "" ||
                die "Resolve operation not in progress, we are not resuming."
 
        # Start afresh.
        mkdir -p "$dotest" || exit
 
+       if test -n "$prefix" && test $# != 0
+       then
+               first=t
+               for arg
+               do
+                       test -n "$first" && {
+                               set x
+                               first=
+                       }
+                       case "$arg" in
+                       /*)
+                               set "$@" "$arg" ;;
+                       *)
+                               set "$@" "$prefix$arg" ;;
+                       esac
+               done
+               shift
+       fi
        git mailsplit -d"$prec" -o"$dotest" -b -- "$@" > "$dotest/last" ||  {
                rm -fr "$dotest"
                exit 1
        }
 
-       # -b, -s, -u, -k and --whitespace flags are kept for the
-       # resuming session after a patch failure.
-       # -3 and -i can and must be given when resuming.
-       echo "$binary" >"$dotest/binary"
-       echo " $ws" >"$dotest/whitespace"
+       # -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.
+       echo " $git_apply_opt" >"$dotest/apply-opt"
+       echo "$threeway" >"$dotest/threeway"
        echo "$sign" >"$dotest/sign"
        echo "$utf8" >"$dotest/utf8"
        echo "$keep" >"$dotest/keep"
        echo 1 >"$dotest/next"
+       if test -n "$rebasing"
+       then
+               : >"$dotest/rebasing"
+       else
+               : >"$dotest/applying"
+               git update-ref ORIG_HEAD HEAD
+       fi
 fi
 
 case "$resolved" in
 '')
-       files=$(git diff-index --cached --name-only HEAD) || exit
-       if [ "$files" ]; then
-          echo "Dirty index: cannot apply patches (dirty: $files)" >&2
-          exit 1
-       fi
+       files=$(git diff-index --cached --name-only HEAD --) || exit
+       test "$files" && die "Dirty index: cannot apply patches (dirty: $files)"
 esac
 
-if test "$(cat "$dotest/binary")" = t
-then
-       binary=--allow-binary-replacement
-fi
 if test "$(cat "$dotest/utf8")" = t
 then
        utf8=-u
@@ -237,10 +300,14 @@ if test "$(cat "$dotest/keep")" = t
 then
        keep=-k
 fi
-ws=`cat "$dotest/whitespace"`
+if test "$(cat "$dotest/threeway")" = t
+then
+       threeway=t
+fi
+git_apply_opt=$(cat "$dotest/apply-opt")
 if test "$(cat "$dotest/sign")" = t
 then
-       SIGNOFF=`git-var GIT_COMMITTER_IDENT | sed -e '
+       SIGNOFF=`git var GIT_COMMITTER_IDENT | sed -e '
                        s/>.*/>/
                        s/^/Signed-off-by: /'
                `
@@ -252,7 +319,6 @@ last=`cat "$dotest/last"`
 this=`cat "$dotest/next"`
 if test "$skip" = t
 then
-       git rerere clear
        this=`expr "$this" + 1`
        resume=
 fi
@@ -293,11 +359,24 @@ do
                        <"$dotest"/info >/dev/null &&
                        go_next && continue
 
-               test -s $dotest/patch || {
+               test -s "$dotest/patch" || {
                        echo "Patch is empty.  Was it split wrong?"
                        stop_here $this
                }
-               git stripspace < "$dotest/msg" > "$dotest/msg-clean"
+               if test -f "$dotest/rebasing" &&
+                       commit=$(sed -e 's/^From \([0-9a-f]*\) .*/\1/' \
+                               -e q "$dotest/$msgnum") &&
+                       test "$(git cat-file -t "$commit")" = commit
+               then
+                       git cat-file commit "$commit" |
+                       sed -e '1,/^$/d' >"$dotest/msg-clean"
+               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"
+               fi
                ;;
        esac
 
@@ -313,9 +392,6 @@ do
 
        export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
 
-       SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$dotest/info")"
-       case "$keep_subject" in -k)  SUBJECT="[PATCH] $SUBJECT" ;; esac
-
        case "$resume" in
        '')
            if test '' != "$SIGNOFF"
@@ -323,7 +399,7 @@ do
                LAST_SIGNED_OFF_BY=`
                    sed -ne '/^Signed-off-by: /p' \
                    "$dotest/msg-clean" |
-                   tail -n 1
+                   sed -ne '$p'
                `
                ADD_SIGNOFF=`
                    test "$LAST_SIGNED_OFF_BY" = "$SIGNOFF" || {
@@ -334,10 +410,8 @@ do
                ADD_SIGNOFF=
            fi
            {
-               printf '%s\n' "$SUBJECT"
                if test -s "$dotest/msg-clean"
                then
-                       echo
                        cat "$dotest/msg-clean"
                fi
                if test '' != "$ADD_SIGNOFF"
@@ -350,7 +424,7 @@ do
                case "$resolved$interactive" in
                tt)
                        # This is used only for interactive view option.
-                       git diff-index -p --cached HEAD >"$dotest/patch"
+                       git diff-index -p --cached HEAD -- >"$dotest/patch"
                        ;;
                esac
        esac
@@ -383,6 +457,7 @@ do
        else
            action=yes
        fi
+       FIRSTLINE=$(sed 1q "$dotest/final-commit")
 
        if test $action = skip
        then
@@ -396,13 +471,11 @@ do
                stop_here $this
        fi
 
-       echo
-       printf 'Applying %s\n' "$SUBJECT"
-       echo
+       printf 'Applying: %s\n' "$FIRSTLINE"
 
        case "$resolved" in
        '')
-               git apply $git_apply_opt $binary --index "$dotest/patch"
+               eval 'git apply '"$git_apply_opt"' --index "$dotest/patch"'
                apply_status=$?
                ;;
        t)
@@ -411,7 +484,7 @@ do
                # trust what the user has in the index file and the
                # working tree.
                resolved=
-               git diff-index --quiet --cached HEAD && {
+               git diff-index --quiet --cached HEAD -- && {
                        echo "No changes - did you forget to use 'git add'?"
                        stop_here_user_resolve $this
                }
@@ -433,7 +506,7 @@ do
                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 && {
+                   git diff-index --quiet --cached HEAD -- && {
                        echo No changes -- Patch already applied.
                        go_next
                        continue
@@ -444,7 +517,7 @@ do
        fi
        if test $apply_status != 0
        then
-               echo Patch failed at $msgnum.
+               printf 'Patch failed at %s %s\n' "$msgnum" "$FIRSTLINE"
                stop_here_user_resolve $this
        fi
 
@@ -454,11 +527,20 @@ do
        fi
 
        tree=$(git write-tree) &&
-       echo Wrote tree $tree &&
        parent=$(git rev-parse --verify HEAD) &&
-       commit=$(git commit-tree $tree -p $parent <"$dotest/final-commit") &&
-       echo Committed: $commit &&
-       git update-ref -m "$GIT_REFLOG_ACTION: $SUBJECT" HEAD $commit $parent ||
+       commit=$(
+               if test -n "$ignore_date"
+               then
+                       GIT_AUTHOR_DATE=
+               fi
+               if test -n "$committer_date_is_author_date"
+               then
+                       GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
+                       export GIT_COMMITTER_DATE
+               fi &&
+               git commit-tree $tree -p $parent <"$dotest/final-commit"
+       ) &&
+       git update-ref -m "$GIT_REFLOG_ACTION: $FIRSTLINE" HEAD $commit $parent ||
        stop_here $this
 
        if test -x "$GIT_DIR"/hooks/post-applypatch
@@ -469,4 +551,6 @@ do
        go_next
 done
 
+git gc --auto
+
 rm -fr "$dotest"