Merge branch 'es/test-fixes'
authorJunio C Hamano <gitster@pobox.com>
Thu, 2 Aug 2018 22:30:40 +0000 (15:30 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 2 Aug 2018 22:30:40 +0000 (15:30 -0700)
Test clean-up and corrections.

* es/test-fixes: (26 commits)
t5608: fix broken &&-chain
t9119: fix broken &&-chains
t9000-t9999: fix broken &&-chains
t7000-t7999: fix broken &&-chains
t6000-t6999: fix broken &&-chains
t5000-t5999: fix broken &&-chains
t4000-t4999: fix broken &&-chains
t3030: fix broken &&-chains
t3000-t3999: fix broken &&-chains
t2000-t2999: fix broken &&-chains
t1000-t1999: fix broken &&-chains
t0000-t0999: fix broken &&-chains
t9814: simplify convoluted check that command correctly errors out
t9001: fix broken "invoke hook" test
t7810: use test_expect_code() instead of hand-rolled comparison
t7400: fix broken "submodule add/reconfigure --force" test
t7201: drop pointless "exit 0" at end of subshell
t6036: fix broken "merge fails but has appropriate contents" tests
t5505: modernize and simplify hard-to-digest test
t5406: use write_script() instead of birthing shell script manually
...

1  2 
t/lib-submodule-update.sh
t/t3404-rebase-interactive.sh
t/t3418-rebase-continue.sh
t/t5500-fetch-pack.sh
t/t6036-recursive-corner-cases.sh
t/t7400-submodule-basic.sh
t/t7810-grep.sh
t/t9001-send-email.sh
index be78cdc1ff0aeaa21feaccc40cb439eb72837b7f,e90ec7908771788d0d7723d2dd729b6defb4bb7f..5b56b23166bb3dea2e89cea38a19eb5698dfff53
@@@ -235,7 -235,7 +235,7 @@@ reset_work_tree_to_interested () 
        then
                mkdir -p submodule_update/.git/modules/sub1/modules &&
                cp -r submodule_update_repo/.git/modules/sub1/modules/sub2 submodule_update/.git/modules/sub1/modules/sub2
 -              GIT_WORK_TREE=. git -C submodule_update/.git/modules/sub1/modules/sub2 config --unset core.worktree
 +              # core.worktree is unset for sub2 as it is not checked out
        fi &&
        # indicate we are interested in the submodule:
        git -C submodule_update config submodule.sub1.url "bogus" &&
@@@ -709,8 -709,7 +709,8 @@@ test_submodule_recursing_with_args_comm
                        git branch -t remove_sub1 origin/remove_sub1 &&
                        $command remove_sub1 &&
                        test_superproject_content origin/remove_sub1 &&
 -                      ! test -e sub1
 +                      ! test -e sub1 &&
 +                      test_must_fail git config -f .git/modules/sub1/config core.worktree
                )
        '
        # ... absorbing a .git directory along the way.
                        : >sub1/untrackedfile &&
                        test_must_fail $command replace_sub1_with_file &&
                        test_superproject_content origin/add_sub1 &&
-                       test_submodule_content sub1 origin/add_sub1
+                       test_submodule_content sub1 origin/add_sub1 &&
                        test -f sub1/untracked_file
                )
        '
                (
                        cd submodule_update &&
                        git branch -t invalid_sub1 origin/invalid_sub1 &&
 -                      test_must_fail $command invalid_sub1 &&
 +                      test_must_fail $command invalid_sub1 2>err &&
 +                      test_i18ngrep sub1 err &&
                        test_superproject_content origin/add_sub1 &&
                        test_submodule_content sub1 origin/add_sub1
                )
@@@ -844,7 -842,7 +844,7 @@@ test_submodule_switch_recursing_with_ar
                        cd submodule_update &&
                        git branch -t add_sub1 origin/add_sub1 &&
                        : >sub1 &&
-                       echo sub1 >.git/info/exclude
+                       echo sub1 >.git/info/exclude &&
                        $command add_sub1 &&
                        test_superproject_content origin/add_sub1 &&
                        test_submodule_content sub1 origin/add_sub1
@@@ -971,7 -969,6 +971,6 @@@ test_submodule_forced_switch_recursing_
                        rm -rf .git/modules/sub1 &&
                        $command replace_sub1_with_directory &&
                        test_superproject_content origin/replace_sub1_with_directory &&
-                       test_submodule_content sub1 origin/modify_sub1
                        test_git_directory_exists sub1
                )
        '
index 01616901bdc868619fee8e28dce14649bd2661db,85e99aac1342b4b6a7b0458354f6b721ce7f1b6c..46657ceadd257b4603e5783c02d7879af4e8f3d6
@@@ -264,18 -264,11 +264,18 @@@ test_expect_success 'retain authorship
  '
  
  test_expect_success 'retain authorship w/ conflicts' '
 +      oGIT_AUTHOR_NAME=$GIT_AUTHOR_NAME &&
 +      test_when_finished "GIT_AUTHOR_NAME=\$oGIT_AUTHOR_NAME" &&
 +
        git reset --hard twerp &&
        test_commit a conflict a conflict-a &&
        git reset --hard twerp &&
 -      GIT_AUTHOR_NAME=AttributeMe \
 +
 +      GIT_AUTHOR_NAME=AttributeMe &&
 +      export GIT_AUTHOR_NAME &&
        test_commit b conflict b conflict-b &&
 +      GIT_AUTHOR_NAME=$oGIT_AUTHOR_NAME &&
 +
        set_fake_editor &&
        test_must_fail git rebase -i conflict-a &&
        echo resolved >conflict &&
@@@ -516,7 -509,7 +516,7 @@@ test_expect_success 'interrupted squas
        one=$(git rev-parse HEAD~3) &&
        set_fake_editor &&
        test_must_fail env FAKE_LINES="1 squash 3 2" git rebase -i HEAD~3 &&
-       (echo one; echo two; echo four) > conflict &&
+       test_write_lines one two four > conflict &&
        git add conflict &&
        test_must_fail git rebase --continue &&
        echo resolved > conflict &&
@@@ -530,10 -523,10 +530,10 @@@ test_expect_success 'interrupted squas
        one=$(git rev-parse HEAD~3) &&
        set_fake_editor &&
        test_must_fail env FAKE_LINES="3 squash 1 2" git rebase -i HEAD~3 &&
-       (echo one; echo four) > conflict &&
+       test_write_lines one four > conflict &&
        git add conflict &&
        test_must_fail git rebase --continue &&
-       (echo one; echo two; echo four) > conflict &&
+       test_write_lines one two four > conflict &&
        git add conflict &&
        test_must_fail git rebase --continue &&
        echo resolved > conflict &&
@@@ -560,16 -553,15 +560,16 @@@ test_expect_success '--continue tries t
  '
  
  test_expect_success 'aborted --continue does not squash commits after "edit"' '
 +      test_when_finished "git rebase --abort" &&
        old=$(git rev-parse HEAD) &&
        test_tick &&
        set_fake_editor &&
        FAKE_LINES="edit 1" git rebase -i HEAD^ &&
        echo "edited again" > file7 &&
        git add file7 &&
 -      test_must_fail env FAKE_COMMIT_MESSAGE=" " git rebase --continue &&
 -      test $old = $(git rev-parse HEAD) &&
 -      git rebase --abort
 +      echo all the things >>conflict &&
 +      test_must_fail git rebase --continue &&
 +      test $old = $(git rev-parse HEAD)
  '
  
  test_expect_success 'auto-amend only edited commits after "edit"' '
@@@ -989,35 -981,7 +989,35 @@@ test_expect_success 'rebase -i --root r
        test -z "$(git show -s --format=%p HEAD^)"
  '
  
 +test_expect_success 'rebase -i --root when root has untracked file confilct' '
 +      test_when_finished "reset_rebase" &&
 +      git checkout -b failing-root-pick A &&
 +      echo x >file2 &&
 +      git rm file1 &&
 +      git commit -m "remove file 1 add file 2" &&
 +      echo z >file1 &&
 +      set_fake_editor &&
 +      test_must_fail env FAKE_LINES="1 2" git rebase -i --root &&
 +      rm file1 &&
 +      git rebase --continue &&
 +      test "$(git log -1 --format=%B)" = "remove file 1 add file 2" &&
 +      test "$(git rev-list --count HEAD)" = 2
 +'
 +
 +test_expect_success 'rebase -i --root reword root when root has untracked file conflict' '
 +      test_when_finished "reset_rebase" &&
 +      echo z>file1 &&
 +      set_fake_editor &&
 +      test_must_fail env FAKE_LINES="reword 1 2" \
 +              FAKE_COMMIT_MESSAGE="Modified A" git rebase -i --root &&
 +      rm file1 &&
 +      FAKE_COMMIT_MESSAGE="Reworded A" git rebase --continue &&
 +      test "$(git log -1 --format=%B HEAD^)" = "Reworded A" &&
 +      test "$(git rev-list --count HEAD)" = 2
 +'
 +
  test_expect_success C_LOCALE_OUTPUT 'rebase --edit-todo does not work on non-interactive rebase' '
 +      git checkout reword-root-branch &&
        git reset --hard &&
        git checkout conflict-branch &&
        set_fake_editor &&
index c145dbac38bb549ab7e9030b4134a50eac468383,853e015839fce0cb2cb50eb6b08aef0dcb24adb3..65ed7db1592d97ae7120e028bc6023446e973a4a
@@@ -60,7 -60,7 +60,7 @@@ test_expect_success 'rebase --continue 
        EOF
        chmod +x test-bin/git-merge-funny &&
        (
-               PATH=./test-bin:$PATH
+               PATH=./test-bin:$PATH &&
                test_must_fail git rebase -s funny -Xopt master topic
        ) &&
        test -f funny.was.run &&
        echo "Resolved" >F2 &&
        git add F2 &&
        (
-               PATH=./test-bin:$PATH
+               PATH=./test-bin:$PATH &&
                git rebase --continue
        ) &&
        test -f funny.was.run
  '
  
 +test_expect_success 'rebase -i --continue handles merge strategy and options' '
 +      rm -fr .git/rebase-* &&
 +      git reset --hard commit-new-file-F2-on-topic-branch &&
 +      test_commit "commit-new-file-F3-on-topic-branch-for-dash-i" F3 32 &&
 +      test_when_finished "rm -fr test-bin funny.was.run funny.args" &&
 +      mkdir test-bin &&
 +      cat >test-bin/git-merge-funny <<-EOF &&
 +      #!$SHELL_PATH
 +      echo "\$@" >>funny.args
 +      case "\$1" in --opt) ;; *) exit 2 ;; esac
 +      case "\$2" in --foo) ;; *) exit 2 ;; esac
 +      case "\$4" in --) ;; *) exit 2 ;; esac
 +      shift 2 &&
 +      >funny.was.run &&
 +      exec git merge-recursive "\$@"
 +      EOF
 +      chmod +x test-bin/git-merge-funny &&
 +      (
 +              PATH=./test-bin:$PATH &&
 +              test_must_fail git rebase -i -s funny -Xopt -Xfoo master topic
 +      ) &&
 +      test -f funny.was.run &&
 +      rm funny.was.run &&
 +      echo "Resolved" >F2 &&
 +      git add F2 &&
 +      (
 +              PATH=./test-bin:$PATH &&
 +              git rebase --continue
 +      ) &&
 +      test -f funny.was.run
 +'
 +
  test_expect_success 'rebase passes merge strategy options correctly' '
        rm -fr .git/rebase-* &&
        git reset --hard commit-new-file-F3-on-topic-branch &&
diff --combined t/t5500-fetch-pack.sh
index 3d33ab3875383f4f71b486bfd3f2fce342e00474,ebbbbfe05424dd805640180f9dfa41068c1d2d4e..0f0fefad1e94097831dd5f9894c200454a9e1da9
@@@ -259,7 -259,7 +259,7 @@@ test_expect_success 'clone shallow obje
  test_expect_success 'pull in shallow repo with missing merge base' '
        (
                cd shallow &&
-               git fetch --depth 4 .. A
+               git fetch --depth 4 .. A &&
                test_must_fail git merge --allow-unrelated-histories FETCH_HEAD
        )
  '
@@@ -533,26 -533,19 +533,26 @@@ test_expect_success 'test --all wrt ta
        # are reachable only via created tag references.
        blob=$(echo "hello blob" | git hash-object -t blob -w --stdin) &&
        git tag -a -m "tag -> blob" tag-to-blob $blob &&
 - \
 +
        tree=$(printf "100644 blob $blob\tfile" | git mktree) &&
        git tag -a -m "tag -> tree" tag-to-tree $tree &&
 - \
 +
        tree2=$(printf "100644 blob $blob\tfile2" | git mktree) &&
        commit=$(git commit-tree -m "hello commit" $tree) &&
        git tag -a -m "tag -> commit" tag-to-commit $commit &&
 - \
 +
        blob2=$(echo "hello blob2" | git hash-object -t blob -w --stdin) &&
 -      tag=$(printf "object $blob2\ntype blob\ntag tag-to-blob2\n\
 -tagger author A U Thor <author@example.com> 0 +0000\n\nhello tag" | git mktag) &&
 +      tag=$(git mktag <<-EOF
 +              object $blob2
 +              type blob
 +              tag tag-to-blob2
 +              tagger author A U Thor <author@example.com> 0 +0000
 +
 +              hello tag
 +      EOF
 +      ) &&
        git tag -a -m "tag -> tag" tag-to-tag $tag &&
 - \
 +
        # `fetch-pack --all` should succeed fetching all those objects.
        mkdir fetchall &&
        (
index ef1c26ec588995499fd9a07c2816ea47d4faa6e9,892cf08743007aec0424edd6d7428489109ead59..4abe8a06e737be2ea500642762854f6e64632423
@@@ -72,7 -72,7 +72,7 @@@ test_expect_success 'merge simple renam
                git rev-parse   >actual     \
                        :2:three   :3:three &&
                git hash-object >>actual    \
-                       three~HEAD three~R2^0
+                       three~HEAD three~R2^0 &&
                test_cmp expect actual
        )
  '
@@@ -148,7 -148,7 +148,7 @@@ test_expect_success 'merge criss-cross 
                git rev-parse   >actual     \
                        :2:three   :3:three &&
                git hash-object >>actual    \
-                       three~HEAD three~R2^0
+                       three~HEAD three~R2^0 &&
                test_cmp expect actual
        )
  '
@@@ -228,7 -228,7 +228,7 @@@ test_expect_success 'git detects differ
                        D:new_a  E:new_a &&
                git rev-parse   >actual     \
                        :2:new_a :3:new_a &&
-               test_cmp expect actual
+               test_cmp expect actual &&
  
                git cat-file -p B:new_a >ours &&
                git cat-file -p C:new_a >theirs &&
@@@ -345,97 -345,40 +345,97 @@@ test_expect_success 'git detects confli
        )
  '
  
 +#      SORRY FOR THE SUPER LONG DESCRIPTION, BUT THIS NEXT ONE IS HAIRY
  #
  # criss-cross + d/f conflict via add/add:
  #   Commit A: Neither file 'a' nor directory 'a/' exists.
  #   Commit B: Introduce 'a'
  #   Commit C: Introduce 'a/file'
 -#   Commit D: Merge B & C, keeping 'a' and deleting 'a/'
 -#
 -# Two different later cases:
 +#   Commit D1: Merge B & C, keeping 'a'    and deleting 'a/'
  #   Commit E1: Merge B & C, deleting 'a' but keeping 'a/file'
 -#   Commit E2: Merge B & C, deleting 'a' but keeping a slightly modified 'a/file'
  #
 -#      B   D
 +#      B   D1 or D2
  #      o---o
  #     / \ / \
  #  A o   X   ? F
  #     \ / \ /
  #      o---o
 -#      C   E1 or E2
 +#      C   E1 or E2 or E3
 +#
 +# I'll describe D2, E2, & E3 (which are alternatives for D1 & E1) more below...
 +#
 +# Merging D1 & E1 requires we first create a virtual merge base X from
 +# merging A & B in memory.  There are several possibilities for the merge-base:
 +#   1: Keep both 'a' and 'a/file' (assuming crazy filesystem allowing a tree
 +#      with a directory and file at same path): results in merge of D1 & E1
 +#      being clean with both files deleted.  Bad (no conflict detected).
 +#   2: Keep 'a' but not 'a/file': Merging D1 & E1 is clean and matches E1.  Bad.
 +#   3: Keep 'a/file' but not 'a': Merging D1 & E1 is clean and matches D1.  Bad.
 +#   4: Keep neither file: Merging D1 & E1 reports the D/F add/add conflict.
 +#
 +# So 4 sounds good for this case, but if we were to merge D1 & E3, where E3
 +# is defined as:
 +#   Commit E3: Merge B & C, keeping modified a, and deleting a/
 +# then we'd get an add/add conflict for 'a', which seems suboptimal.  A little
 +# creativity leads us to an alternate choice:
 +#   5: Keep 'a' as 'a~$UNIQUE' and a/file; results:
 +#        Merge D1 & E1: rename/delete conflict for 'a'; a/file silently deleted
 +#        Merge D1 & E3 is clean, as expected.
 +#
 +# So choice 5 at least provides some kind of conflict for the original case,
 +# and can merge cleanly as expected with D1 and E3.  It also made things just
 +# slightly funny for merging D1 and e$, where E4 is defined as:
 +#   Commit E4: Merge B & C, modifying 'a' and renaming to 'a2', and deleting 'a/'
 +# in this case, we'll get a rename/rename(1to2) conflict because a~$UNIQUE
 +# gets renamed to 'a' in D1 and to 'a2' in E4.  But that's better than having
 +# two files (both 'a' and 'a2') sitting around without the user being notified
 +# that we could detect they were related and need to be merged.  Also, choice
 +# 5 makes the handling of 'a/file' seem suboptimal.  What if we were to merge
 +# D2 and E4, where D2 is:
 +#   Commit D2: Merge B & C, renaming 'a'->'a2', keeping 'a/file'
 +# This would result in a clean merge with 'a2' having three-way merged
 +# contents (good), and deleting 'a/' (bad) -- it doesn't detect the
 +# conflict in how the different sides treated a/file differently.
 +# Continuing down the creative route:
 +#   6: Keep 'a' as 'a~$UNIQUE1' and keep 'a/' as 'a~$UNIQUE2/'; results:
 +#        Merge D1 & E1: rename/delete conflict for 'a' and each path under 'a/'.
 +#        Merge D1 & E3: clean, as expected.
 +#        Merge D1 & E4: rename/rename(1to2) conflict on 'a' vs 'a2'.
 +#        Merge D2 & E4: clean for 'a2', rename/delete for a/file
  #
 -# Merging D & E1 requires we first create a virtual merge base X from
 -# merging A & B in memory.  Now, if X could keep both 'a' and 'a/file' in
 -# the index, then the merge of D & E1 could be resolved cleanly with both
 -# 'a' and 'a/file' removed.  Since git does not currently allow creating
 -# such a tree, the best we can do is have X contain both 'a~<unique>' and
 -# 'a/file' resulting in the merge of D and E1 having a rename/delete
 -# conflict for 'a'.  (Although this merge appears to be unsolvable with git
 -# currently, git could do a lot better than it currently does with these
 -# d/f conflicts, which is the purpose of this test.)
 +# Choice 6 could cause rename detection to take longer (providing more targets
 +# that need to be searched).  Also, the conflict message for each path under
 +# 'a/' might be annoying unless we can detect it at the directory level, print
 +# it once, and then suppress it for individual filepaths underneath.
  #
 -# Merge of D & E2 has similar issues for path 'a', but should always result
 -# in a modify/delete conflict for path 'a/file'.
  #
 -# We run each merge in both directions, to check for directional issues
 -# with D/F conflict handling.
 +# As of time of writing, git uses choice 5.  Directory rename detection and
 +# rename detection performance improvements might make choice 6 a desirable
 +# improvement.  But we can at least document where we fall short for now...
 +#
 +#
 +# Historically, this testcase also used:
 +#   Commit E2: Merge B & C, deleting 'a' but keeping slightly modified 'a/file'
 +# The merge of D1 & E2 is very similar to D1 & E1 -- it has similar issues for
 +# path 'a', but should always result in a modify/delete conflict for path
 +# 'a/file'.  These tests ran the two merges
 +#   D1 & E1
 +#   D1 & E2
 +# in both directions, to check for directional issues with D/F conflict
 +# handling. Later we added
 +#   D1 & E3
 +#   D1 & E4
 +#   D2 & E4
 +# for good measure, though we only ran those one way because we had pretty
 +# good confidence in merge-recursive's directional handling of D/F issues.
 +#
 +# Just to summarize all the intermediate merge commits:
 +#   Commit D1: Merge B & C, keeping a    and deleting a/
 +#   Commit D2: Merge B & C, renaming a->a2, keeping a/file
 +#   Commit E1: Merge B & C, deleting a but keeping a/file
 +#   Commit E2: Merge B & C, deleting a but keeping slightly modified a/file
 +#   Commit E3: Merge B & C, keeping modified a, and deleting a/
 +#   Commit E4: Merge B & C, modifying 'a' and renaming to 'a2', and deleting 'a/'
  #
  
  test_expect_success 'setup differently handled merges of directory/file conflict' '
                git branch B &&
                git checkout -b C &&
                mkdir a &&
 -              echo 10 >a/file &&
 +              test_write_lines a b c d e f g >a/file &&
                git add a/file &&
                test_tick &&
                git commit -m C &&
  
                git checkout B &&
 -              echo 5 >a &&
 +              test_write_lines 1 2 3 4 5 6 7 >a &&
                git add a &&
                test_tick &&
                git commit -m B &&
  
                git checkout B^0 &&
 -              test_must_fail git merge C &&
 -              git clean -f &&
 -              rm -rf a/ &&
 -              echo 5 >a &&
 -              git add a &&
 -              test_tick &&
 -              git commit -m D &&
 -              git tag D &&
 +              git merge -s ours -m D1 C^0 &&
 +              git tag D1 &&
 +
 +              git checkout B^0 &&
 +              test_must_fail git merge C^0 &&
 +              git clean -fd &&
 +              git rm -rf a/ &&
 +              git rm a &&
 +              git cat-file -p B:a >a2 &&
 +              git add a2 &&
 +              git commit -m D2 &&
 +              git tag D2 &&
  
                git checkout C^0 &&
 -              test_must_fail git merge B &&
 -              git clean -f &&
 -              git rm --cached a &&
 -              echo 10 >a/file &&
 -              git add a/file &&
 -              test_tick &&
 -              git commit -m E1 &&
 +              git merge -s ours -m E1 B^0 &&
                git tag E1 &&
  
                git checkout C^0 &&
 -              test_must_fail git merge B &&
 -              git clean -f &&
 -              git rm --cached a &&
 -              printf "10\n11\n" >a/file &&
 +              git merge -s ours -m E2 B^0 &&
 +              test_write_lines a b c d e f g h >a/file &&
                git add a/file &&
 -              test_tick &&
 -              git commit -m E2 &&
 -              git tag E2
 +              git commit --amend -C HEAD &&
 +              git tag E2 &&
 +
 +              git checkout C^0 &&
 +              test_must_fail git merge B^0 &&
 +              git clean -fd &&
 +              git rm -rf a/ &&
 +              test_write_lines 1 2 3 4 5 6 7 8 >a &&
 +              git add a &&
 +              git commit -m E3 &&
 +              git tag E3 &&
 +
 +              git checkout C^0 &&
 +              test_must_fail git merge B^0 &&
 +              git clean -fd &&
 +              git rm -rf a/ &&
 +              git rm a &&
 +              test_write_lines 1 2 3 4 5 6 7 8 >a2 &&
 +              git add a2 &&
 +              git commit -m E4 &&
 +              git tag E4
        )
  '
  
 -test_expect_success 'merge of D & E1 fails but has appropriate contents' '
 +test_expect_success 'merge of D1 & E1 fails but has appropriate contents' '
        test_when_finished "git -C directory-file reset --hard" &&
        test_when_finished "git -C directory-file clean -fdqx" &&
        (
                cd directory-file &&
  
 -              git checkout D^0 &&
 +              git checkout D1^0 &&
  
                test_must_fail git merge -s recursive E1^0 &&
  
        )
  '
  
 -test_expect_success 'merge of E1 & D fails but has appropriate contents' '
 +test_expect_success 'merge of E1 & D1 fails but has appropriate contents' '
        test_when_finished "git -C directory-file reset --hard" &&
        test_when_finished "git -C directory-file clean -fdqx" &&
        (
  
                git checkout E1^0 &&
  
 -              test_must_fail git merge -s recursive D^0 &&
 +              test_must_fail git merge -s recursive D1^0 &&
  
                git ls-files -s >out &&
                test_line_count = 2 out &&
        )
  '
  
 -test_expect_success 'merge of D & E2 fails but has appropriate contents' '
 +test_expect_success 'merge of D1 & E2 fails but has appropriate contents' '
        test_when_finished "git -C directory-file reset --hard" &&
        test_when_finished "git -C directory-file clean -fdqx" &&
        (
                cd directory-file &&
  
 -              git checkout D^0 &&
 +              git checkout D1^0 &&
  
                test_must_fail git merge -s recursive E2^0 &&
  
                test_line_count = 2 out &&
  
                git rev-parse >expect    \
-                       B:a   E2:a/file  c:a/file   A:ignore-me &&
+                       B:a   E2:a/file  C:a/file   A:ignore-me &&
                git rev-parse   >actual   \
                        :2:a  :3:a/file  :1:a/file  :0:ignore-me &&
-               test_cmp expect actual
+               test_cmp expect actual &&
  
                test_path_is_file a~HEAD
        )
  '
  
 -test_expect_success 'merge of E2 & D fails but has appropriate contents' '
 +test_expect_success 'merge of E2 & D1 fails but has appropriate contents' '
        test_when_finished "git -C directory-file reset --hard" &&
        test_when_finished "git -C directory-file clean -fdqx" &&
        (
  
                git checkout E2^0 &&
  
 -              test_must_fail git merge -s recursive D^0 &&
 +              test_must_fail git merge -s recursive D1^0 &&
  
                git ls-files -s >out &&
                test_line_count = 4 out &&
                test_line_count = 2 out &&
  
                git rev-parse >expect    \
-                       B:a   E2:a/file  c:a/file   A:ignore-me &&
+                       B:a   E2:a/file  C:a/file   A:ignore-me &&
                git rev-parse   >actual   \
                        :3:a  :2:a/file  :1:a/file  :0:ignore-me &&
-               test_cmp expect actual
+               test_cmp expect actual &&
  
 -              test_path_is_file a~D^0
 +              test_path_is_file a~D1^0
 +      )
 +'
 +
 +test_expect_success 'merge of D1 & E3 succeeds' '
 +      test_when_finished "git -C directory-file reset --hard" &&
 +      test_when_finished "git -C directory-file clean -fdqx" &&
 +      (
 +              cd directory-file &&
 +
 +              git checkout D1^0 &&
 +
 +              git merge -s recursive E3^0 &&
 +
 +              git ls-files -s >out &&
 +              test_line_count = 2 out &&
 +              git ls-files -u >out &&
 +              test_line_count = 0 out &&
 +              git ls-files -o >out &&
 +              test_line_count = 1 out &&
 +
 +              git rev-parse >expect    \
 +                      A:ignore-me  E3:a &&
 +              git rev-parse   >actual   \
 +                      :0:ignore-me :0:a &&
 +              test_cmp expect actual
 +      )
 +'
 +
 +test_expect_success 'merge of D1 & E4 notifies user a and a2 are related' '
 +      test_when_finished "git -C directory-file reset --hard" &&
 +      test_when_finished "git -C directory-file clean -fdqx" &&
 +      (
 +              cd directory-file &&
 +
 +              git checkout D1^0 &&
 +
 +              test_must_fail git merge -s recursive E4^0 &&
 +
 +              git ls-files -s >out &&
 +              test_line_count = 4 out &&
 +              git ls-files -u >out &&
 +              test_line_count = 3 out &&
 +              git ls-files -o >out &&
 +              test_line_count = 1 out &&
 +
 +              git rev-parse >expect                  \
 +                      A:ignore-me  B:a   D1:a  E4:a2 &&
 +              git rev-parse   >actual                \
 +                      :0:ignore-me :1:a~Temporary\ merge\ branch\ 2  :2:a  :3:a2 &&
 +              test_cmp expect actual
 +      )
 +'
 +
 +test_expect_failure 'merge of D2 & E4 merges a2s & reports conflict for a/file' '
 +      test_when_finished "git -C directory-file reset --hard" &&
 +      test_when_finished "git -C directory-file clean -fdqx" &&
 +      (
 +              cd directory-file &&
 +
 +              git checkout D2^0 &&
 +
 +              test_must_fail git merge -s recursive E4^0 &&
 +
 +              git ls-files -s >out &&
 +              test_line_count = 3 out &&
 +              git ls-files -u >out &&
 +              test_line_count = 1 out &&
 +              git ls-files -o >out &&
 +              test_line_count = 1 out &&
 +
 +              git rev-parse >expect                 \
 +                      A:ignore-me  E4:a2  D2:a/file &&
 +              git rev-parse   >actual               \
 +                      :0:ignore-me :0:a2  :2:a/file &&
 +              test_cmp expect actual
        )
  '
  
index 48fd14fae6e9ac04f2edac3592bfb3ee4504cab1,bfb09dd56615580fd82f8f3d680f45ecd12f9f0e..2c2c97e144172a54319ac4d988884407f4186243
@@@ -171,12 -171,13 +171,13 @@@ test_expect_success 'submodule add to .
  test_expect_success 'submodule add to reconfigure existing submodule with --force' '
        (
                cd addtest-ignore &&
-               git submodule add --force bogus-url submod &&
-               git submodule add -b initial "$submodurl" submod-branch &&
-               test "bogus-url" = "$(git config -f .gitmodules submodule.submod.url)" &&
-               test "bogus-url" = "$(git config submodule.submod.url)" &&
+               bogus_url="$(pwd)/bogus-url" &&
+               git submodule add --force "$bogus_url" submod &&
+               git submodule add --force -b initial "$submodurl" submod-branch &&
+               test "$bogus_url" = "$(git config -f .gitmodules submodule.submod.url)" &&
+               test "$bogus_url" = "$(git config submodule.submod.url)" &&
                # Restore the url
-               git submodule add --force "$submodurl" submod
+               git submodule add --force "$submodurl" submod &&
                test "$submodurl" = "$(git config -f .gitmodules submodule.submod.url)" &&
                test "$submodurl" = "$(git config submodule.submod.url)"
        )
@@@ -818,7 -819,7 +819,7 @@@ test_expect_success '../bar/a/b/c work
                cp pristine-.git-config .git/config &&
                cp pristine-.gitmodules .gitmodules &&
                mkdir -p a/b/c &&
-               (cd a/b/c; git init) &&
+               (cd a/b/c && git init) &&
                git config remote.origin.url ../foo/bar.git &&
                git submodule add ../bar/a/b/c ./a/b/c &&
                git submodule init &&
@@@ -993,11 -994,6 +994,11 @@@ test_expect_success 'submodule deinit s
        rmdir init
  '
  
 +test_expect_success 'submodule deinit should unset core.worktree' '
 +      test_path_is_file .git/modules/example/config &&
 +      test_must_fail git config -f .git/modules/example/config core.worktree
 +'
 +
  test_expect_success 'submodule deinit from subdirectory' '
        git submodule update --init &&
        git config submodule.example.foo bar &&
diff --combined t/t7810-grep.sh
index 9312c8daf523abd3da1681e8777648c71401e2ae,fecee602c1de3e46676b3c8cd77dcab97a862dcf..90bba4fcefd5f32d9200188cef77913b5d5fd40f
@@@ -99,101 -99,6 +99,101 @@@ d
                test_cmp expected actual
        '
  
 +      test_expect_success "grep -w $L (with --column)" '
 +              {
 +                      echo ${HC}file:5:foo mmap bar
 +                      echo ${HC}file:14:foo_mmap bar mmap
 +                      echo ${HC}file:5:foo mmap bar_mmap
 +                      echo ${HC}file:14:foo_mmap bar mmap baz
 +              } >expected &&
 +              git grep --column -w -e mmap $H >actual &&
 +              test_cmp expected actual
 +      '
 +
 +      test_expect_success "grep -w $L (with --column, extended OR)" '
 +              {
 +                      echo ${HC}file:14:foo_mmap bar mmap
 +                      echo ${HC}file:19:foo_mmap bar mmap baz
 +              } >expected &&
 +              git grep --column -w -e mmap$ --or -e baz $H >actual &&
 +              test_cmp expected actual
 +      '
 +
 +      test_expect_success "grep -w $L (with --column, --invert)" '
 +              {
 +                      echo ${HC}file:1:foo mmap bar
 +                      echo ${HC}file:1:foo_mmap bar
 +                      echo ${HC}file:1:foo_mmap bar mmap
 +                      echo ${HC}file:1:foo mmap bar_mmap
 +              } >expected &&
 +              git grep --column --invert -w -e baz $H -- file >actual &&
 +              test_cmp expected actual
 +      '
 +
 +      test_expect_success "grep $L (with --column, --invert, extended OR)" '
 +              {
 +                      echo ${HC}hello_world:6:HeLLo_world
 +              } >expected &&
 +              git grep --column --invert -e ll --or --not -e _ $H -- hello_world \
 +                      >actual &&
 +              test_cmp expected actual
 +      '
 +
 +      test_expect_success "grep $L (with --column, --invert, extended AND)" '
 +              {
 +                      echo ${HC}hello_world:3:Hello world
 +                      echo ${HC}hello_world:3:Hello_world
 +                      echo ${HC}hello_world:6:HeLLo_world
 +              } >expected &&
 +              git grep --column --invert --not -e _ --and --not -e ll $H -- hello_world \
 +                      >actual &&
 +              test_cmp expected actual
 +      '
 +
 +      test_expect_success "grep $L (with --column, double-negation)" '
 +              {
 +                      echo ${HC}file:1:foo_mmap bar mmap baz
 +              } >expected &&
 +              git grep --column --not \( --not -e foo --or --not -e baz \) $H -- file \
 +                      >actual &&
 +              test_cmp expected actual
 +      '
 +
 +      test_expect_success "grep -w $L (with --column, -C)" '
 +              {
 +                      echo ${HC}file:5:foo mmap bar
 +                      echo ${HC}file-foo_mmap bar
 +                      echo ${HC}file:14:foo_mmap bar mmap
 +                      echo ${HC}file:5:foo mmap bar_mmap
 +                      echo ${HC}file:14:foo_mmap bar mmap baz
 +              } >expected &&
 +              git grep --column -w -C1 -e mmap $H >actual &&
 +              test_cmp expected actual
 +      '
 +
 +      test_expect_success "grep -w $L (with --line-number, --column)" '
 +              {
 +                      echo ${HC}file:1:5:foo mmap bar
 +                      echo ${HC}file:3:14:foo_mmap bar mmap
 +                      echo ${HC}file:4:5:foo mmap bar_mmap
 +                      echo ${HC}file:5:14:foo_mmap bar mmap baz
 +              } >expected &&
 +              git grep -n --column -w -e mmap $H >actual &&
 +              test_cmp expected actual
 +      '
 +
 +      test_expect_success "grep -w $L (with non-extended patterns, --column)" '
 +              {
 +                      echo ${HC}file:5:foo mmap bar
 +                      echo ${HC}file:10:foo_mmap bar
 +                      echo ${HC}file:10:foo_mmap bar mmap
 +                      echo ${HC}file:5:foo mmap bar_mmap
 +                      echo ${HC}file:10:foo_mmap bar mmap baz
 +              } >expected &&
 +              git grep --column -w -e bar -e mmap $H >actual &&
 +              test_cmp expected actual
 +      '
 +
        test_expect_success "grep -w $L" '
                {
                        echo ${HC}file:1:foo mmap bar
@@@ -940,10 -845,9 +940,9 @@@ test_expect_success 'grep from a subdir
  test_expect_success 'grep from a subdirectory to search wider area (2)' '
        mkdir -p s &&
        (
-               cd s || exit 1
-               ( git grep xxyyzz .. >out ; echo $? >status )
-               ! test -s out &&
-               test 1 = $(cat status)
+               cd s &&
+               test_expect_code 1 git grep xxyyzz .. >out &&
+               ! test -s out
        )
  '
  
diff --combined t/t9001-send-email.sh
index 1da282c415b81215ddb149f6230ff2a5a8eb4c95,53314ff54e4e186bfef99877ac1f5aa082c8eab6..b8e919e25d6b4def87a6d66db32b94e8813b1cd7
@@@ -225,8 -225,6 +225,8 @@@ X-Mailer: X-MAILER-STRIN
  In-Reply-To: <unique-message-id@example.com>
  References: <unique-message-id@example.com>
  Reply-To: Reply <reply@example.com>
 +MIME-Version: 1.0
 +Content-Transfer-Encoding: 8bit
  
  Result: OK
  EOF
@@@ -332,7 -330,7 +332,7 @@@ test_expect_success $PREREQ 'Show all h
  
  test_expect_success $PREREQ 'Prompting works' '
        clean_fake_sendmail &&
-       (echo "to@example.com"
+       (echo "to@example.com" &&
         echo ""
        ) | GIT_SEND_EMAIL_NOTTY=1 git send-email \
                --smtp-server="$(pwd)/fake.sendmail" \
@@@ -417,7 -415,6 +417,7 @@@ test_expect_success $PREREQ 'reject lon
                --from="Example <nobody@example.com>" \
                --to=nobody@example.com \
                --smtp-server="$(pwd)/fake.sendmail" \
 +              --transfer-encoding=8bit \
                $patches longline.patch \
                2>errors &&
        grep longline.patch errors
@@@ -459,42 -456,6 +459,42 @@@ test_expect_success $PREREQ 'allow lon
                2>errors
  '
  
 +test_expect_success $PREREQ 'short lines with auto encoding are 8bit' '
 +      clean_fake_sendmail &&
 +      git send-email \
 +              --from="A <author@example.com>" \
 +              --to=nobody@example.com \
 +              --smtp-server="$(pwd)/fake.sendmail" \
 +              --transfer-encoding=auto \
 +              $patches &&
 +      grep "Content-Transfer-Encoding: 8bit" msgtxt1
 +'
 +
 +test_expect_success $PREREQ 'long lines with auto encoding are quoted-printable' '
 +      clean_fake_sendmail &&
 +      git send-email \
 +              --from="Example <nobody@example.com>" \
 +              --to=nobody@example.com \
 +              --smtp-server="$(pwd)/fake.sendmail" \
 +              --transfer-encoding=auto \
 +              --no-validate \
 +              longline.patch &&
 +      grep "Content-Transfer-Encoding: quoted-printable" msgtxt1
 +'
 +
 +for enc in auto quoted-printable base64
 +do
 +      test_expect_success $PREREQ "--validate passes with encoding $enc" '
 +              git send-email \
 +                      --from="Example <nobody@example.com>" \
 +                      --to=nobody@example.com \
 +                      --smtp-server="$(pwd)/fake.sendmail" \
 +                      --transfer-encoding=$enc \
 +                      --validate \
 +                      $patches longline.patch
 +      '
 +done
 +
  test_expect_success $PREREQ 'Invalid In-Reply-To' '
        clean_fake_sendmail &&
        git send-email \
  
  test_expect_success $PREREQ 'Valid In-Reply-To when prompting' '
        clean_fake_sendmail &&
-       (echo "From Example <from@example.com>"
-        echo "To Example <to@example.com>"
+       (echo "From Example <from@example.com>" &&
+        echo "To Example <to@example.com>" &&
         echo ""
        ) | GIT_SEND_EMAIL_NOTTY=1 git send-email \
                --smtp-server="$(pwd)/fake.sendmail" \
@@@ -612,8 -573,6 +612,8 @@@ Subject: [PATCH 1/1] Second
  Date: DATE-STRING
  Message-Id: MESSAGE-ID-STRING
  X-Mailer: X-MAILER-STRING
 +MIME-Version: 1.0
 +Content-Transfer-Encoding: 8bit
  
  Result: OK
  EOF
@@@ -658,8 -617,6 +658,8 @@@ Subject: [PATCH 1/1] Second
  Date: DATE-STRING
  Message-Id: MESSAGE-ID-STRING
  X-Mailer: X-MAILER-STRING
 +MIME-Version: 1.0
 +Content-Transfer-Encoding: 8bit
  
  Result: OK
  EOF
@@@ -695,8 -652,6 +695,8 @@@ Subject: [PATCH 1/1] Second
  Date: DATE-STRING
  Message-Id: MESSAGE-ID-STRING
  X-Mailer: X-MAILER-STRING
 +MIME-Version: 1.0
 +Content-Transfer-Encoding: 8bit
  
  Result: OK
  EOF
@@@ -723,8 -678,6 +723,8 @@@ Subject: [PATCH 1/1] Second
  Date: DATE-STRING
  Message-Id: MESSAGE-ID-STRING
  X-Mailer: X-MAILER-STRING
 +MIME-Version: 1.0
 +Content-Transfer-Encoding: 8bit
  
  Result: OK
  EOF
@@@ -759,8 -712,6 +759,8 @@@ Subject: [PATCH 1/1] Second
  Date: DATE-STRING
  Message-Id: MESSAGE-ID-STRING
  X-Mailer: X-MAILER-STRING
 +MIME-Version: 1.0
 +Content-Transfer-Encoding: 8bit
  
  Result: OK
  EOF
@@@ -792,8 -743,6 +792,8 @@@ Subject: [PATCH 1/1] Second
  Date: DATE-STRING
  Message-Id: MESSAGE-ID-STRING
  X-Mailer: X-MAILER-STRING
 +MIME-Version: 1.0
 +Content-Transfer-Encoding: 8bit
  
  Result: OK
  EOF
@@@ -825,8 -774,6 +825,8 @@@ Subject: [PATCH 1/1] Second
  Date: DATE-STRING
  Message-Id: MESSAGE-ID-STRING
  X-Mailer: X-MAILER-STRING
 +MIME-Version: 1.0
 +Content-Transfer-Encoding: 8bit
  
  Result: OK
  EOF
@@@ -862,8 -809,6 +862,8 @@@ Subject: [PATCH 1/1] Second
  Date: DATE-STRING
  Message-Id: MESSAGE-ID-STRING
  X-Mailer: X-MAILER-STRING
 +MIME-Version: 1.0
 +Content-Transfer-Encoding: 8bit
  
  Result: OK
  EOF
@@@ -892,8 -837,6 +892,8 @@@ Subject: [PATCH 1/1] Second
  Date: DATE-STRING
  Message-Id: MESSAGE-ID-STRING
  X-Mailer: X-MAILER-STRING
 +MIME-Version: 1.0
 +Content-Transfer-Encoding: 8bit
  
  Result: OK
  EOF
@@@ -2023,11 -1966,11 +2023,11 @@@ test_expect_success $PREREQ 'invoke hoo
  
                # Verify error message when a patch is rejected by the hook
                sed -e "s/add master/x/" ../0001-add-master.patch >../another.patch &&
-               git send-email \
+               test_must_fail git send-email \
                        --from="Example <nobody@example.com>" \
                        --to=nobody@example.com \
                        --smtp-server="$(pwd)/../fake.sendmail" \
-                       ../another.patch 2>err
+                       ../another.patch 2>err &&
                test_i18ngrep "rejected by sendemail-validate hook" err
        )
  '