Merge branch 'pt/am-abort-fix'
authorJunio C Hamano <gitster@pobox.com>
Wed, 24 Jun 2015 19:21:49 +0000 (12:21 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 24 Jun 2015 19:21:50 +0000 (12:21 -0700)
Various fixes around "git am" that applies a patch to a history
that is not there yet.

* pt/am-abort-fix:
am --abort: keep unrelated commits on unborn branch
am --abort: support aborting to unborn branch
am --abort: revert changes introduced by failed 3way merge
am --skip: support skipping while on unborn branch
am -3: support 3way merge on unborn branch
am --skip: revert changes introduced by failed 3way merge

1  2 
git-am.sh
t/t4151-am-abort.sh
diff --combined git-am.sh
index 75e701a3b03cd64c3beaea82c7eb462c49a69d6d,f594ed0811b3f45bd8990e3a099dfbbceb1c864b..8733071624df86f21373db8c3e3cc5a1ece37830
+++ b/git-am.sh
@@@ -17,7 -17,6 +17,7 @@@ s,signoff       add a Signed-off-by lin
  u,utf8          recode into utf8 (default)
  k,keep          pass -k flag to git-mailinfo
  keep-non-patch  pass -b flag to git-mailinfo
 +m,message-id    pass -m 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
@@@ -69,6 -68,8 +69,8 @@@ the
        cmdline="$cmdline -3"
  fi
  
+ empty_tree=4b825dc642cb6eb9a060e54bf8d69288fbee4904
  sq () {
        git rev-parse --sq-quote "$@"
  }
@@@ -85,7 -86,7 +87,7 @@@ safe_to_abort () 
                return 1
        fi
  
-       if ! test -s "$dotest/abort-safety"
+       if ! test -f "$dotest/abort-safety"
        then
                return 0
        fi
@@@ -177,7 -178,8 +179,8 @@@ It does not apply to blobs recorded in 
      then
            GIT_MERGE_VERBOSITY=0 && export GIT_MERGE_VERBOSITY
      fi
-     git-merge-recursive $orig_tree -- HEAD $his_tree || {
+     our_tree=$(git rev-parse --verify -q HEAD || echo $empty_tree)
+     git-merge-recursive $orig_tree -- $our_tree $his_tree || {
            git rerere $allow_rerere_autoupdate
            die "$(gettext "Failed to merge in the changes.")"
      }
@@@ -372,29 -374,18 +375,29 @@@ split_patches () 
  prec=4
  dotest="$GIT_DIR/rebase-apply"
  sign= utf8=t keep= keepcr= skip= interactive= resolved= rebasing= abort=
 -resolvemsg= resume= scissors= no_inbody_headers=
 +messageid= resolvemsg= resume= scissors= no_inbody_headers=
  git_apply_opt=
  committer_date_is_author_date=
  ignore_date=
  allow_rerere_autoupdate=
  gpg_sign_opt=
 +threeway=
 +
 +if test "$(git config --bool --get am.messageid)" = true
 +then
 +    messageid=t
 +fi
  
  if test "$(git config --bool --get am.keepcr)" = true
  then
      keepcr=t
  fi
  
 +if test "$(git config --bool --get am.threeWay)" = true
 +then
 +    threeway=t
 +fi
 +
  while test $# != 0
  do
        case "$1" in
@@@ -406,18 -397,12 +409,18 @@@ it will be removed. Please do not use i
                ;;
        -3|--3way)
                threeway=t ;;
 +      --no-3way)
 +              threeway=f ;;
        -s|--signoff)
                sign=t ;;
        -u|--utf8)
                utf8=t ;; # this is now default
        --no-utf8)
                utf8= ;;
 +      -m|--message-id)
 +              messageid=t ;;
 +      --no-message-id)
 +              messageid=f ;;
        -k|--keep)
                keep=t ;;
        --keep-non-patch)
@@@ -510,10 -495,11 +513,11 @@@ the
                ;;
        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
+               head_tree=$(git rev-parse --verify -q HEAD || echo $empty_tree) &&
+               git read-tree --reset -u $head_tree $head_tree &&
+               index_tree=$(git write-tree) &&
+               git read-tree -m -u $index_tree $head_tree
+               git read-tree $head_tree
                ;;
        ,t)
                if test -f "$dotest/rebasing"
                git rerere clear
                if safe_to_abort
                then
-                       git read-tree --reset -u HEAD ORIG_HEAD
-                       git reset ORIG_HEAD
+                       head_tree=$(git rev-parse --verify -q HEAD || echo $empty_tree) &&
+                       git read-tree --reset -u $head_tree $head_tree &&
+                       index_tree=$(git write-tree) &&
+                       orig_head=$(git rev-parse --verify -q ORIG_HEAD || echo $empty_tree) &&
+                       git read-tree -m -u $index_tree $orig_head
+                       if git rev-parse --verify -q ORIG_HEAD >/dev/null 2>&1
+                       then
+                               git reset ORIG_HEAD
+                       else
+                               git read-tree $empty_tree
+                               curr_branch=$(git symbolic-ref HEAD 2>/dev/null) &&
+                               git update-ref -d $curr_branch
+                       fi
                fi
                rm -fr "$dotest"
                exit ;;
@@@ -585,7 -582,6 +600,7 @@@ Use \"git am --abort\" to remove it.")
        echo "$sign" >"$dotest/sign"
        echo "$utf8" >"$dotest/utf8"
        echo "$keep" >"$dotest/keep"
 +      echo "$messageid" >"$dotest/messageid"
        echo "$scissors" >"$dotest/scissors"
        echo "$no_inbody_headers" >"$dotest/no_inbody_headers"
        echo "$GIT_QUIET" >"$dotest/quiet"
  *)
        keep= ;;
  esac
 +case "$(cat "$dotest/messageid")" in
 +t)
 +      messageid=-m ;;
 +f)
 +      messageid= ;;
 +esac
  case "$(cat "$dotest/scissors")" in
  t)
        scissors=--scissors ;;
@@@ -665,8 -655,6 +680,8 @@@ f
  if test "$(cat "$dotest/threeway")" = t
  then
        threeway=t
 +else
 +      threeway=f
  fi
  git_apply_opt=$(cat "$dotest/apply-opt")
  if test "$(cat "$dotest/sign")" = t
@@@ -719,7 -707,7 +734,7 @@@ d
                        get_author_ident_from_commit "$commit" >"$dotest/author-script"
                        git diff-tree --root --binary --full-index "$commit" >"$dotest/patch"
                else
 -                      git mailinfo $keep $no_inbody_headers $scissors $utf8 "$dotest/msg" "$dotest/patch" \
 +                      git mailinfo $keep $no_inbody_headers $messageid $scissors $utf8 "$dotest/msg" "$dotest/patch" \
                                <"$dotest/$msgnum" >"$dotest/info" ||
                                stop_here $this
  
@@@ -837,10 -825,10 +852,10 @@@ To restore the original branch and sto
                continue
        fi
  
 -      if test -x "$GIT_DIR"/hooks/applypatch-msg
 +      hook="$(git rev-parse --git-path hooks/applypatch-msg)"
 +      if test -x "$hook"
        then
 -              "$GIT_DIR"/hooks/applypatch-msg "$dotest/final-commit" ||
 -              stop_here $this
 +              "$hook" "$dotest/final-commit" || stop_here $this
        fi
  
        if test -f "$dotest/final-commit"
@@@ -914,10 -902,9 +929,10 @@@ did you forget to use 'git add'?
                stop_here_user_resolve $this
        fi
  
 -      if test -x "$GIT_DIR"/hooks/pre-applypatch
 +      hook="$(git rev-parse --git-path hooks/pre-applypatch)"
 +      if test -x "$hook"
        then
 -              "$GIT_DIR"/hooks/pre-applypatch || stop_here $this
 +              "$hook" || stop_here $this
        fi
  
        tree=$(git write-tree) &&
                echo "$(cat "$dotest/original-commit") $commit" >> "$dotest/rewritten"
        fi
  
 -      if test -x "$GIT_DIR"/hooks/post-applypatch
 -      then
 -              "$GIT_DIR"/hooks/post-applypatch
 -      fi
 +      hook="$(git rev-parse --git-path hooks/post-applypatch)"
 +      test -x "$hook" && "$hook"
  
        go_next
  done
  
  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
 +    hook="$(git rev-parse --git-path hooks/post-rewrite)"
 +    if test -x "$hook"; then
 +      "$hook" rebase < "$dotest"/rewritten
      fi
  fi
  
diff --combined t/t4151-am-abort.sh
index 8d90634ab81b3d24326c519a8bae52c2e2cb3ac0,1274f75a609765a1323aa23ba5fe48abee0beb69..833e7b2ceae99cbf53bac63e239b362e368ddf8f
@@@ -14,13 -14,14 +14,14 @@@ test_expect_success setup 
        git add file-1 file-2 &&
        git commit -m initial &&
        git tag initial &&
+       git format-patch --stdout --root initial >initial.patch &&
        for i in 2 3 4 5 6
        do
                echo $i >>file-1 &&
                echo $i >otherfile-$i &&
                git add otherfile-$i &&
                test_tick &&
 -              git commit -a -m $i || break
 +              git commit -a -m $i || return 1
        done &&
        git format-patch --no-numbered initial &&
        git checkout -b side initial &&
@@@ -63,6 -64,28 +64,28 @@@ d
  
  done
  
+ test_expect_success 'am -3 --skip removes otherfile-4' '
+       git reset --hard initial &&
+       test_must_fail git am -3 0003-*.patch &&
+       test 3 -eq $(git ls-files -u | wc -l) &&
+       test 4 = "$(cat otherfile-4)" &&
+       git am --skip &&
+       test_cmp_rev initial HEAD &&
+       test -z "$(git ls-files -u)" &&
+       test_path_is_missing otherfile-4
+ '
+ test_expect_success 'am -3 --abort removes otherfile-4' '
+       git reset --hard initial &&
+       test_must_fail git am -3 0003-*.patch &&
+       test 3 -eq $(git ls-files -u | wc -l) &&
+       test 4 = "$(cat otherfile-4)" &&
+       git am --abort &&
+       test_cmp_rev initial HEAD &&
+       test -z $(git ls-files -u) &&
+       test_path_is_missing otherfile-4
+ '
  test_expect_success 'am --abort will keep the local commits intact' '
        test_must_fail git am 0004-*.patch &&
        test_commit unrelated &&
        test_cmp expect actual
  '
  
+ test_expect_success 'am -3 stops on conflict on unborn branch' '
+       git checkout -f --orphan orphan &&
+       git reset &&
+       rm -f otherfile-4 &&
+       test_must_fail git am -3 0003-*.patch &&
+       test 2 -eq $(git ls-files -u | wc -l) &&
+       test 4 = "$(cat otherfile-4)"
+ '
+ test_expect_success 'am -3 --skip clears index on unborn branch' '
+       test_path_is_dir .git/rebase-apply &&
+       echo tmpfile >tmpfile &&
+       git add tmpfile &&
+       git am --skip &&
+       test -z "$(git ls-files)" &&
+       test_path_is_missing otherfile-4 &&
+       test_path_is_missing tmpfile
+ '
+ test_expect_success 'am -3 --abort removes otherfile-4 on unborn branch' '
+       git checkout -f --orphan orphan &&
+       git reset &&
+       rm -f otherfile-4 file-1 &&
+       test_must_fail git am -3 0003-*.patch &&
+       test 2 -eq $(git ls-files -u | wc -l) &&
+       test 4 = "$(cat otherfile-4)" &&
+       git am --abort &&
+       test -z "$(git ls-files -u)" &&
+       test_path_is_missing otherfile-4
+ '
+ test_expect_success 'am -3 --abort on unborn branch removes applied commits' '
+       git checkout -f --orphan orphan &&
+       git reset &&
+       rm -f otherfile-4 otherfile-2 file-1 file-2 &&
+       test_must_fail git am -3 initial.patch 0003-*.patch &&
+       test 3 -eq $(git ls-files -u | wc -l) &&
+       test 4 = "$(cat otherfile-4)" &&
+       git am --abort &&
+       test -z "$(git ls-files -u)" &&
+       test_path_is_missing otherfile-4 &&
+       test_path_is_missing file-1 &&
+       test_path_is_missing file-2 &&
+       test 0 -eq $(git log --oneline 2>/dev/null | wc -l) &&
+       test refs/heads/orphan = "$(git symbolic-ref HEAD)"
+ '
+ test_expect_success 'am --abort on unborn branch will keep local commits intact' '
+       git checkout -f --orphan orphan &&
+       git reset &&
+       test_must_fail git am 0004-*.patch &&
+       test_commit unrelated2 &&
+       git rev-parse HEAD >expect &&
+       git am --abort &&
+       git rev-parse HEAD >actual &&
+       test_cmp expect actual
+ '
  test_done