Merge branch 'pt/am-abort-fix' into maint
authorJunio C Hamano <gitster@pobox.com>
Mon, 3 Aug 2015 17:41:32 +0000 (10:41 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 3 Aug 2015 17:41:32 +0000 (10:41 -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 a67d0f98989706fa69df4cf7d6242ad9531eb337,f594ed0811b3f45bd8990e3a099dfbbceb1c864b..3103c0fc28910560d85f4c2cff2b6a0595848b16
+++ 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,18 -374,13 +375,18 @@@ 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=
  
 +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
@@@ -406,10 -403,6 +409,10 @@@ it will be removed. Please do not use i
                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)
@@@ -502,10 -495,11 +505,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 ;;
@@@ -577,7 -582,6 +592,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 ;;
@@@ -709,7 -707,7 +724,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
  
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