{"thread":{"id":"41962","subject":"[RFC/PATCH 13/18] Add --index-only support with read_tree_trivial merges, kind of","startedAt":"2016-04-08T06:58:28Z","lastAt":"2016-04-10T05:33:24Z","messageCount":30,"participants":["Elijah Newren","Michael J Gruber","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":18},"messages":[{"id":"282930","messageId":"1460098726-5958-1-git-send-email-newren@gmail.com","threadId":"41962","inReplyTo":null,"subject":"[RFC/PATCH 00/18] Add --index-only option to git merge","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2016-04-08T06:58:28Z","receivedAt":"2016-04-08T06:58:28Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"This patch series adds an --index-only flag to git merge, the idea\nbeing to allow a merge to be performed entirely in the index without\ntouching (or even needing) a working tree.\n\nThe core fix, to merge-recursive, was actually quite easy.  The\nrecursive merge logic already had the ability to ignore the working\ndirectory and operate entirely on the index -- it needed to do this\nwhen creating a virtual merge base, i.e. when o->call_depth > 0.  It's\njust that o->call_depth was also used for other purposes, so I just\nneeded to introduce a new flag to disambiguate and switch all the\nnecessary index-only-related call sites to use it.  It actually seems\nto make the code slightly easier to read too, which is a nice bonus.\nThat was all done in patch 12 of this series.\n\nAdding all the necessary testcases and switching over the other merge\nstrategies turned out to be the harder part...and still has a problem,\nas I'll mention below.\n\n\nA brief-ish summary of the series:\n\n* Patches 1 and 2 are unrelated cleanups, which could be submitted\n  independently.  However, submitting them as separate patches would\n  result in a minor conflict, so I'm just including them together.\n\n* Patches 3 and 4 add some testcases and a fix for a separate issue\n  found while testing this series which could be split off and\n  submitted separately, but fixing this problem and enforcing the\n  starting state I expected permitted me to reduce the range of\n  testcases I needed to consider for the --index-only merges.  So I\n  thought it made sense to include in this series.\n\n  In particular, I was worried about how git merge behaved when the\n  index differed from HEAD prior to the merge starting.  I discovered\n  that it wasn't just allowed for fast-forward merges (where behavior\n  is well-defined) but that it was also (accidentally I'm assuming)\n  allowed for octopus merges with weird/surprising behavior.\n\n* Patches 5-10 add testcases for the --index-only option for each of\n  the merge strategies/cases: recursive, fast-forward update, resolve,\n  octopus, ours, subtree.\n\n* Patch 11 adds the --index-only option to the code and adds the\n  documentation, while patches 12-18 implement the --index-only option\n  for each of the strategies/cases.\n\n\nSome things I am concerned about:\n\n* The option name (--index-only) may be slightly misleading since the\n  index isn't the only thing modified within the git directory, other\n  normal things under there are still modified (e.g. writing MERGE_*\n  files, sticking blobs/trees/commits in objects/*, and updating refs\n  and reflogs).  I personally prefer this name and think the confusion\n  would be minor, but I'm a bit unsure and wanted some other opinions\n  on this.\n\n* I didn't add this option to the separate git-merge-recursive\n  executable and make the worktree-optional modification to the git\n  subcommands merge-recursive, merge-recursive-ours,\n  merge-recursive-theirs, and merge-subtree in git.c.  Should I, or\n  are these separate binaries and subcommands just present for\n  historical backward compatibility reasons with people expected to\n  call e.g. \"git merge -s recursive\" these days?\n\n* The addition of --index-only to the various git-merge*.sh scripts is\n  lacking in flexibility (e.g. has to be passed as the first\n  argument).  These scripts seemed to have fairly rigid calling\n  conventions already, suggesting there's not much value in making\n  them flexible, but perhaps that was the wrong conclusion to draw.\n\n* Expanding on the last item, git-merge-one-file.sh is of particular\n  concern; it seemed to strongly assume exactly seven arguments and\n  that the position of each mattered.  I didn't want to break that, so\n  I added --index-only as an optional 8th argument, even though it\n  seems slightly odd force an option argument to always come after\n  positional ones (and it made the changes to merge_entry in\n  merge-index.c slightly easier to implement).  Does that seem okay?\n\n* For a long time I had two remaining bugs, one of which was in\n  checkout_fast_forward.  I was feeling kind of sheepish about that,\n  because how much simpler could it be than handling a fast-forward\n  merge (with possible uncommited index entries to carry forward)?\n  Getting stuck on a simple case like that would be embarrassing.\n  Luckily, I figured out that bug.  So, that leaves just one case left\n  that I can't seem to figure out: read_tree_trivial.  So much better,\n  right?  Even it's name is sitting there, mocking me.  \"Ha ha, I'm\n  read_tree_*trivial* and you can't figure me out.\"  read_tree_trivial\n  is a jerk.\n\n\nElijah Newren (18):\n  Remove duplicate code\n  Avoid checking working copy when creating a virtual merge base\n  Document weird bug in octopus merges via testcases\n  merge-octopus: Abort if index not clean\n  Add testcase for --index-only merges needing the recursive strategy\n  Add testcase for --index-only merges needing an ff update\n  Add testcase for --index-only merges with the resolve strategy\n  Add testcase for --index-only merges with the octopus strategy\n  Add testcase for --index-only merges with the ours strategy\n  Add testcase for --index-only merges with the subtree strategy\n  merge: Add a new --index-only option, not yet implemented\n  Add --index-only support for recursive merges\n  Add --index-only support with read_tree_trivial merges, kind of\n  Add --index-only support for ff_only merges\n  merge: Pass --index-only along to external merge strategy programs\n  git-merge-one-file.sh: support --index-only option\n  git-merge-resolve.sh: support --index-only option\n  git-merge-octopus.sh: support --index-only option\n\n Documentation/git-merge.txt              |  14 +\n builtin/merge-index.c                    |   9 +-\n builtin/merge.c                          |  13 +-\n builtin/pull.c                           |   4 +-\n cache.h                                  |   3 +-\n git-merge-octopus.sh                     |  19 +-\n git-merge-one-file.sh                    |  44 ++-\n git-merge-resolve.sh                     |  12 +-\n git.c                                    |   2 +-\n merge-recursive.c                        |  40 +--\n merge-recursive.h                        |   1 +\n merge.c                                  |   8 +-\n sequencer.c                              |   4 +-\n t/t6043-merge-index-only.sh              | 443 +++++++++++++++++++++++++++++++\n t/t6044-merge-unrelated-index-changes.sh | 156 +++++++++++\n 15 files changed, 730 insertions(+), 42 deletions(-)\n create mode 100755 t/t6043-merge-index-only.sh\n create mode 100755 t/t6044-merge-unrelated-index-changes.sh\n\n-- \n2.8.0.18.gc685494\n"},{"id":"282941","messageId":"1460098726-5958-2-git-send-email-newren@gmail.com","threadId":"41962","inReplyTo":"1460098726-5958-1-git-send-email-newren@gmail.com","subject":"[RFC/PATCH 01/18] Remove duplicate code","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2016-04-08T06:58:29Z","receivedAt":"2016-04-08T06:58:29Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"In commit 51931bf (merge-recursive: Improve handling of rename target vs.\ndirectory addition, 2011-08-11) I apparently added two lines of code that\nwere immediately duplicated a few lines later.  No idea why, other than\nit seems pretty clear this was a mistake: there is no need to remove the\nsame file twice; removing it once is sufficient...especially since the\nintervening line was working with a different file entirely.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c | 2 --\n 1 file changed, 2 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex b880ae5..d4292de 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1773,8 +1773,6 @@ static int process_entry(struct merge_options *o,\n \t\t\toutput(o, 1, _(\"CONFLICT (%s): There is a directory with name %s in %s. \"\n \t\t\t       \"Adding %s as %s\"),\n \t\t\t       conf, path, other_branch, path, new_path);\n-\t\t\tif (o->call_depth)\n-\t\t\t\tremove_file_from_cache(path);\n \t\t\tupdate_file(o, 0, sha, mode, new_path);\n \t\t\tif (o->call_depth)\n \t\t\t\tremove_file_from_cache(path);\n-- \n2.8.0.18.gc685494\n"},{"id":"282926","messageId":"1460098726-5958-3-git-send-email-newren@gmail.com","threadId":"41962","inReplyTo":"1460098726-5958-1-git-send-email-newren@gmail.com","subject":"[RFC/PATCH 02/18] Avoid checking working copy when creating a virtual merge base","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2016-04-08T06:58:30Z","receivedAt":"2016-04-08T06:58:30Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"There were a few cases in merge-recursive that could result in a check for\nthe presence of files in the working copy while trying to create a virtual\nmerge base.  These were rare and innocuous, but somewhat illogical.  The\ntwo cases were:\n\n  * When there was naming conflicts (e.g. a D/F conflict) and we had to\n    pick a new unique name for a file.  Since the new name is somewhat\n    arbitrary, it didn't matter that we consulted the working copy to\n    avoid picking a filename it has, but since the virtual merge base is\n    never checked out, it's a waste of time and slightly odd to do so.\n\n  * When two different files get renamed to the same name (on opposite\n    sides of the merge), we needed to delete the original filenames from\n    the cache and possibly also the working directory.  The caller's check\n    for determining whether to delete from the working directory was a\n    call to would_lose_untracked().  It turns out this didn't matter\n    because remove_file() had logic to avoid modifying the working\n    directory when creating a virtual merge base, but there is no reason\n    for the caller to check the working directory in such circumstances.\n    It's a waste of time, if not also a bit weird.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex d4292de..06d31ed 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -622,7 +622,7 @@ static char *unique_path(struct merge_options *o, const char *path, const char *\n \tbase_len = newpath.len;\n \twhile (string_list_has_string(&o->current_file_set, newpath.buf) ||\n \t       string_list_has_string(&o->current_directory_set, newpath.buf) ||\n-\t       file_exists(newpath.buf)) {\n+\t       (!o->call_depth && file_exists(newpath.buf))) {\n \t\tstrbuf_setlen(&newpath, base_len);\n \t\tstrbuf_addf(&newpath, \"_%d\", suffix++);\n \t}\n@@ -1234,8 +1234,8 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n \t       a->path, c1->path, ci->branch1,\n \t       b->path, c2->path, ci->branch2);\n \n-\tremove_file(o, 1, a->path, would_lose_untracked(a->path));\n-\tremove_file(o, 1, b->path, would_lose_untracked(b->path));\n+\tremove_file(o, 1, a->path, o->call_depth || would_lose_untracked(a->path));\n+\tremove_file(o, 1, b->path, o->call_depth || would_lose_untracked(b->path));\n \n \tmfi_c1 = merge_file_special_markers(o, a, c1, &ci->ren1_other,\n \t\t\t\t\t    o->branch1, c1->path,\n-- \n2.8.0.18.gc685494\n"},{"id":"282924","messageId":"1460098726-5958-4-git-send-email-newren@gmail.com","threadId":"41962","inReplyTo":"1460098726-5958-1-git-send-email-newren@gmail.com","subject":"[RFC/PATCH 03/18] Document weird bug in octopus merges via testcases","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2016-04-08T06:58:31Z","receivedAt":"2016-04-08T06:58:31Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"...and check all other merge strategies with testcases while we're at it.\n\nWhen the index has a staged change before running git merge, most the time\ngit merge will error out telling the user that the merge operation would\ntoss their merged changes unless they commit them first.  There are two\nexceptions:\n  * ff updates\n  * octopus merges\n\nff updates actually will error out if the staged change is to a path\nmodified between HEAD and the commit being merged.  If the path(s) that\nare staged are files unrelated to the changes between these two commits,\nthough, then an ff update will just keep these staged changes around after\nthe merge.\n\nFor octopus merges, the staged changes seem to take the place of HEAD just\nbefore the merge is performed.  So if the staged changes can be cleanly\nmerged with all the other heads, then the staged changes will just be\nincorported into the resulting commit.  If the staged changes cannot be\ncleanly merged with all the other heads, the merge is not aborted -- merge\nconflicts are simply reported as if HEAD had originally contained whatever\nthe index did.\n\nSomewhat amusingly, though, an octopus merge will abort if the changes are\nnot staged.  So, for example, if you do a 'git rm --cached somefile' right\nbefore attempting an octopus merge then you'll get an error, but if you do\na 'git rm somefile' right before attempting an octopus merge git will\nhappily proceed.  Similarly if you just modify a tracked file without\nadding it.\n\n...makes me wonder if I should add a testcase for unstaged and untracked\nchanges for each merge strategy while I'm at it.  Hmmm...\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n t/t6044-merge-unrelated-index-changes.sh | 165 +++++++++++++++++++++++++++++++\n 1 file changed, 165 insertions(+)\n create mode 100755 t/t6044-merge-unrelated-index-changes.sh\n\ndiff --git a/t/t6044-merge-unrelated-index-changes.sh b/t/t6044-merge-unrelated-index-changes.sh\nnew file mode 100755\nindex 0000000..726c898\n--- /dev/null\n+++ b/t/t6044-merge-unrelated-index-changes.sh\n@@ -0,0 +1,165 @@\n+#!/bin/sh\n+\n+test_description=\"merges with unrelated index changes\"\n+\n+. ./test-lib.sh\n+\n+# Testcase for some simple merges\n+#   A\n+#   o-----o B\n+#    \\\n+#     \\---o C\n+#      \\\n+#       \\-o D\n+#        \\\n+#         o E\n+#   Commit A: some file a\n+#   Commit B: adds file b, modifies end of a\n+#   Commit C: adds file c\n+#   Commit D: adds file d, modifies beginning of a\n+#   Commit E: renames a->subdir/a, adds subdir/e\n+\n+test_expect_success 'setup trivial merges' '\n+\tseq 1 10 >a &&\n+\tgit add a &&\n+\ttest_tick && git commit -m A &&\n+\n+\tgit branch A &&\n+\tgit branch B &&\n+\tgit branch C &&\n+\tgit branch D &&\n+\tgit branch E &&\n+\n+\tgit checkout B &&\n+\techo b >b &&\n+\techo 11 >>a &&\n+\tgit add a b &&\n+\ttest_tick && git commit -m B &&\n+\n+\tgit checkout C &&\n+\techo c >c &&\n+\tgit add c &&\n+\ttest_tick && git commit -m C &&\n+\n+\tgit checkout D &&\n+\tseq 2 10 >a &&\n+\techo d >d &&\n+\tgit add a d &&\n+\ttest_tick && git commit -m D &&\n+\n+\tgit checkout E &&\n+\tmkdir subdir &&\n+\tgit mv a subdir/a &&\n+\techo e >subdir/e &&\n+\tgit add subdir &&\n+\ttest_tick && git commit -m E\n+'\n+\n+test_expect_success 'ff update' '\n+\tgit reset --hard &&\n+\tgit checkout A^0 &&\n+\n+\ttouch random_file && git add random_file &&\n+\n+\tgit merge E^0 &&\n+\n+\ttest_must_fail git rev-parse HEAD:random_file &&\n+\ttest \"$(git diff --name-only --cached E)\" = \"random_file\"\n+'\n+\n+test_expect_success 'ff update, important file modified' '\n+\tgit reset --hard &&\n+\tgit checkout A^0 &&\n+\n+\tmkdir subdir &&\n+\ttouch subdir/e &&\n+\tgit add subdir/e &&\n+\n+\ttest_must_fail git merge E^0\n+'\n+\n+test_expect_success 'resolve, trivial' '\n+\tgit reset --hard &&\n+\tgit checkout B^0 &&\n+\n+\ttouch random_file && git add random_file &&\n+\n+\ttest_must_fail git merge -s resolve C^0\n+'\n+\n+test_expect_success 'resolve, non-trivial' '\n+\tgit reset --hard &&\n+\tgit checkout B^0 &&\n+\n+\ttouch random_file && git add random_file &&\n+\n+\ttest_must_fail git merge -s resolve D^0\n+'\n+\n+test_expect_success 'recursive' '\n+\tgit reset --hard &&\n+\tgit checkout B^0 &&\n+\n+\ttouch random_file && git add random_file &&\n+\n+\ttest_must_fail git merge -s recursive C^0\n+'\n+\n+test_expect_failure 'octopus, unrelated file touched' '\n+\tgit reset --hard &&\n+\tgit checkout B^0 &&\n+\n+\ttouch random_file && git add random_file &&\n+\n+\techo \"I think the next line should fail, but maybe it was intended...\" &&\n+\ttest_might_fail git merge C^0 D^0 &&\n+\n+\techo \"No matter what, random_file should NOT be part of HEAD\" &&\n+\ttest_must_fail git rev-parse HEAD:random_file\n+'\n+\n+test_expect_failure 'octopus, related file removed' '\n+\tgit reset --hard &&\n+\tgit checkout B^0 &&\n+\n+\tgit rm b &&\n+\n+\techo \"I think the next line should fail, but maybe it was intended...\" &&\n+\ttest_might_fail git merge C^0 D^0 &&\n+\n+\techo \"No matter what, b should still be in HEAD\" &&\n+\tgit cat-file -p HEAD:b | grep b$\n+'\n+\n+test_expect_failure 'octopus, related file modified' '\n+\tgit reset --hard &&\n+\tgit checkout B^0 &&\n+\n+\techo 12 >>a && git add a &&\n+\n+\techo \"I think the next line should fail, but maybe it was intended...\" &&\n+\ttest_might_fail git merge C^0 D^0 &&\n+\n+\techo \"No matter what, 12 should NOT be in the copy of a from HEAD\" &&\n+\tgit cat-file -p HEAD:a | test_must_fail grep 12\n+'\n+\n+test_expect_success 'ours' '\n+\tgit reset --hard &&\n+\tgit checkout B^0 &&\n+\n+\ttouch random_file && git add random_file &&\n+\n+\ttest_must_fail git merge -s ours C^0\n+'\n+\n+test_expect_success 'subtree' '\n+\tgit reset --hard &&\n+\tgit checkout B^0 &&\n+\n+\ttouch random_file && git add random_file &&\n+\n+\ttest_must_fail git merge -s subtree E^0\n+'\n+\n+test_done\n-- \n2.8.0.18.gc685494\n"},{"id":"282933","messageId":"1460098726-5958-5-git-send-email-newren@gmail.com","threadId":"41962","inReplyTo":"1460098726-5958-1-git-send-email-newren@gmail.com","subject":"[RFC/PATCH 04/18] merge-octopus: Abort if index not clean","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2016-04-08T06:58:32Z","receivedAt":"2016-04-08T06:58:32Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Signed-off-by: Elijah Newren <newren@gmail.com>\n---\n git-merge-octopus.sh                     |  5 +++++\n t/t6044-merge-unrelated-index-changes.sh | 21 ++++++---------------\n 2 files changed, 11 insertions(+), 15 deletions(-)\n\ndiff --git a/git-merge-octopus.sh b/git-merge-octopus.sh\nindex 8643f74..a1d7702 100755\n--- a/git-merge-octopus.sh\n+++ b/git-merge-octopus.sh\n@@ -44,6 +44,11 @@ esac\n # MRC is the current \"merge reference commit\"\n # MRT is the current \"merge result tree\"\n \n+if ! git diff-index --quiet --cached HEAD --; then\n+    echo \"Error: Your local changes to the following files need to be resolved before merge\"\n+    git diff-index --cached --name-only HEAD -- | sed -e 's/^/    /'\n+    exit 2\n+fi\n MRC=$(git rev-parse --verify -q $head)\n MRT=$(git write-tree)\n NON_FF_MERGE=0\ndiff --git a/t/t6044-merge-unrelated-index-changes.sh b/t/t6044-merge-unrelated-index-changes.sh\nindex 726c898..4c1dc5a 100755\n--- a/t/t6044-merge-unrelated-index-changes.sh\n+++ b/t/t6044-merge-unrelated-index-changes.sh\n@@ -105,43 +105,34 @@ test_expect_success 'recursive' '\n \ttest_must_fail git merge -s recursive C^0\n '\n \n-test_expect_failure 'octopus, unrelated file touched' '\n+test_expect_success 'octopus, unrelated file touched' '\n \tgit reset --hard &&\n \tgit checkout B^0 &&\n \n \ttouch random_file && git add random_file &&\n \n \techo \"I think the next line should fail, but maybe it was intended...\" &&\n-\ttest_might_fail git merge C^0 D^0 &&\n-\n-\techo \"No matter what, random_file should NOT be part of HEAD\" &&\n-\ttest_must_fail git rev-parse HEAD:random_file\n+\ttest_must_fail git merge C^0 D^0\n '\n \n-test_expect_failure 'octopus, related file removed' '\n+test_expect_success 'octopus, related file removed' '\n \tgit reset --hard &&\n \tgit checkout B^0 &&\n \n \tgit rm b &&\n \n \techo \"I think the next line should fail, but maybe it was intended...\" &&\n-\ttest_might_fail git merge C^0 D^0 &&\n-\n-\techo \"No matter what, b should still be in HEAD\" &&\n-\tgit cat-file -p HEAD:b | grep b$\n+\ttest_must_fail git merge C^0 D^0\n '\n \n-test_expect_failure 'octopus, related file modified' '\n+test_expect_success 'octopus, related file modified' '\n \tgit reset --hard &&\n \tgit checkout B^0 &&\n \n \techo 12 >>a && git add a &&\n \n \techo \"I think the next line should fail, but maybe it was intended...\" &&\n-\ttest_might_fail git merge C^0 D^0 &&\n-\n-\techo \"No matter what, 12 should NOT be in the copy of a from HEAD\" &&\n-\tgit cat-file -p HEAD:a | test_must_fail grep 12\n+\ttest_must_fail git merge C^0 D^0\n '\n \n test_expect_success 'ours' '\n-- \n2.8.0.18.gc685494\n"},{"id":"282927","messageId":"1460098726-5958-6-git-send-email-newren@gmail.com","threadId":"41962","inReplyTo":"1460098726-5958-1-git-send-email-newren@gmail.com","subject":"[RFC/PATCH 05/18] Add testcase for --index-only merges needing the recursive strategy","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2016-04-08T06:58:33Z","receivedAt":"2016-04-08T06:58:33Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Signed-off-by: Elijah Newren <newren@gmail.com>\n---\n t/t6043-merge-index-only.sh | 170 ++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 170 insertions(+)\n create mode 100755 t/t6043-merge-index-only.sh\n\ndiff --git a/t/t6043-merge-index-only.sh b/t/t6043-merge-index-only.sh\nnew file mode 100755\nindex 0000000..b8b22ab\n--- /dev/null\n+++ b/t/t6043-merge-index-only.sh\n@@ -0,0 +1,170 @@\n+#!/bin/sh\n+\n+test_description=\"index-only merges\"\n+\n+. ./test-lib.sh\n+\n+# Testcase setup for rename/modify:\n+#   Commit A: new file: a\n+#   Commit B: modify a slightly\n+#   Commit C: rename a->b\n+# We should be able to merge B & C cleanly\n+\n+test_expect_success 'setup rename/modify merge' '\n+\tgit init &&\n+\n+\tprintf \"1\\n2\\n3\\n4\\n5\\n6\\n7\\n\" >a &&\n+\tgit add a &&\n+\tgit commit -m A &&\n+\tgit tag A &&\n+\n+\tgit checkout -b B A &&\n+\techo 8 >>a &&\n+\tgit add a &&\n+\tgit commit -m B &&\n+\n+\tgit checkout -b C A &&\n+\tgit mv a b &&\n+\tgit commit -m C\n+'\n+\n+test_expect_failure '--index-only with rename/modify works in non-bare-clone' '\n+\tgit checkout B^0 &&\n+\n+\tgit merge --index-only -s recursive C^0 &&\n+\n+\techo \"Making sure the working copy was not updated\" &&\n+\ttest ! -f b &&\n+\ttest -f a &&\n+\ttest $(git rev-parse B:a) = $(git hash-object a) &&\n+\n+\techo \"Making sure the index was updated\" &&\n+\ttest 1 -eq $(git ls-files -s | wc -l) &&\n+\ttest $(git rev-parse B:a) = $(git rev-parse :b)\n+'\n+\n+test_expect_failure '--index-only with rename/modify works in a bare clone' '\n+\tgit clone --bare . bare.clone &&\n+\t(cd bare.clone &&\n+\n+\t git update-ref --no-deref HEAD B &&\n+\t git read-tree HEAD &&\n+\n+\t git merge --index-only -s recursive C^0 &&\n+\n+\t echo \"Making sure the working copy was not updated\" &&\n+\t test ! -f b &&\n+\t test ! -f a &&\n+\n+\t echo \"Making sure the index was updated\" &&\n+\t test 1 -eq $(git ls-files -s | wc -l) &&\n+\t test $(git rev-parse B:a) = $(git rev-parse :b)\n+\t)\n+'\n+\n+# Testcase requiring recursion to handle:\n+#\n+#    L1      L2\n+#     o---o--o\n+#    / \\ /    \\\n+# B o   X      o C\n+#    \\ / \\    /\n+#     o---o--o\n+#    R1      R2\n+#\n+# B : 1..20\n+# L1: 1..3 a..c 4..20\n+# R1: 1..13n..p14..20\n+# L2: 1..3 a..i 4..13 n..p 14..20\n+# R2: 1..3 a..c 4..13 n..v 14..20\n+# C:  1..3 a..i 4..13 n..v 14..20\n+#   needs 'recursive' strategy to get correct solution; 'resolve' would fail\n+#\n+\n+test_expect_success 'setup single-file criss-cross resolvable with recursive strategy' '\n+\tgit reset --hard &&\n+\tgit rm -rf . &&\n+\tgit clean -fdqx &&\n+\trm -rf .git &&\n+\tgit init &&\n+\n+\tseq 1 20 >contents &&\n+\tgit add contents &&\n+\ttest_tick && git commit -m initial &&\n+\n+\tgit branch R1 &&\n+\tgit checkout -b L1 &&\n+\tseq 1 3 >contents &&\n+\tseq 1 3 | tr 1-3 a-c >>contents &&\n+\tseq 4 20 >>contents &&\n+\tgit add contents &&\n+\ttest_tick && git commit -m L1 &&\n+\n+\tgit checkout R1 &&\n+\tseq 1 13 >contents &&\n+\tseq 1 3 | tr 1-3 n-p >>contents &&\n+\tseq 14 20 >>contents &&\n+\tgit add contents &&\n+\ttest_tick && git commit -m R1 &&\n+\n+\tgit checkout -b L2 L1^0 &&\n+\ttest_tick && git merge R1 &&\n+\tseq 1 3 >contents &&\n+\tseq 1 9 | tr 1-9 a-i >>contents &&\n+\tseq 4 13 >>contents &&\n+\tseq 1 3 | tr 1-3 n-p >>contents &&\n+\tseq 14 20 >>contents &&\n+\tgit add contents &&\n+\ttest_tick && git commit -m L2 &&\n+\n+\tgit checkout -b R2 R1^0 &&\n+\ttest_tick && git merge L1 &&\n+\tseq 1 3 >contents &&\n+\tseq 1 3 | tr 1-3 a-c >>contents &&\n+\tseq 4 13 >>contents &&\n+\tseq 1 9 | tr 1-9 n-v >>contents &&\n+\tseq 14 20 >>contents &&\n+\tgit add contents &&\n+\ttest_tick && git commit -m R2 &&\n+\n+\tseq 1 3 >answer &&\n+\tseq 1 9 | tr 1-9 a-i >>answer &&\n+\tseq 4 13 >>answer &&\n+\tseq 1 9 | tr 1-9 n-v >>answer &&\n+\tseq 14 20 >>answer &&\n+\tgit tag answer $(git hash-object -w answer) &&\n+\trm -f answer\n+'\n+\n+test_expect_failure 'recursive --index-only in non-bare repo' '\n+\tgit reset --hard &&\n+\tgit checkout L2^0 &&\n+\n+\tgit merge --index-only -s recursive R2^0 &&\n+\n+\ttest 1 = $(git ls-files -s | wc -l) &&\n+\ttest 0 = $(git ls-files -u | wc -l) &&\n+\ttest 0 = $(git ls-files -o | wc -l) &&\n+\n+\ttest $(git rev-parse :contents) = $(git rev-parse answer) &&\n+\ttest $(git rev-parse L2:contents) = $(git hash-object contents)\n+'\n+\n+test_expect_failure 'recursive --index-only in bare repo' '\n+\tgit clone --bare . bare.clone &&\n+\t(cd bare.clone &&\n+\n+\t git update-ref --no-deref HEAD L2 &&\n+\t git read-tree HEAD &&\n+\n+\t git merge --index-only -s recursive R2^0 &&\n+\n+\t test 1 = $(git ls-files -s | wc -l) &&\n+\t test 0 = $(git ls-files -u | wc -l) &&\n+\n+\t test $(git rev-parse :contents) = $(git rev-parse answer) &&\n+\t test ! -f contents\n+\t)\n+'\n+\n+test_done\n-- \n2.8.0.18.gc685494\n"},{"id":"282932","messageId":"1460098726-5958-7-git-send-email-newren@gmail.com","threadId":"41962","inReplyTo":"1460098726-5958-1-git-send-email-newren@gmail.com","subject":"[RFC/PATCH 06/18] Add testcase for --index-only merges needing an ff update","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2016-04-08T06:58:34Z","receivedAt":"2016-04-08T06:58:34Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Signed-off-by: Elijah Newren <newren@gmail.com>\n---\n t/t6043-merge-index-only.sh | 70 +++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 70 insertions(+)\n\ndiff --git a/t/t6043-merge-index-only.sh b/t/t6043-merge-index-only.sh\nindex b8b22ab..67c8e92 100755\n--- a/t/t6043-merge-index-only.sh\n+++ b/t/t6043-merge-index-only.sh\n@@ -167,4 +167,74 @@ test_expect_failure 'recursive --index-only in bare repo' '\n \t)\n '\n \n+# Testcase for a simple ff update\n+#   A\n+#   o-----o E\n+#\n+#   Commit A: some file a\n+#   Commit E: renames a->subdir/a, adds subdir/e\n+\n+test_expect_success 'setup simple ff update' '\n+\tgit reset --hard &&\n+\tgit rm -rf . &&\n+\tgit clean -fdqx &&\n+\trm -rf .git &&\n+\tgit init &&\n+\n+\tseq 1 10 >a &&\n+\tgit add a &&\n+\ttest_tick && git commit -m A &&\n+\n+\tgit branch A &&\n+\tgit branch E &&\n+\n+\tgit checkout E &&\n+\tmkdir subdir &&\n+\tgit mv a subdir/a &&\n+\techo e >subdir/e &&\n+\tgit add subdir &&\n+\ttest_tick && git commit -m E\n+'\n+\n+test_expect_failure '--index-only ff update, non-bare' '\n+\tgit reset --hard &&\n+\tgit checkout A^0 &&\n+\n+\tgit merge --index-only --ff-only E^0 &&\n+\n+\tgit diff --staged --exit-code E &&\n+\ttest $(git hash-object a) = $(git rev-parse A:a) &&\n+\ttest ! -d subdir\n+'\n+\n+test_expect_failure '--index-only ff update, bare' '\n+\tgit clone --bare . bare.clone &&\n+\t(cd bare.clone &&\n+\n+\t git update-ref --no-deref HEAD A &&\n+\t git read-tree HEAD &&\n+\n+\t git merge --index-only --ff-only E^0 &&\n+\n+\t git diff --staged --exit-code E &&\n+\t test ! -f a &&\n+\t test ! -d subdir\n+\t)\n+'\n+\n+test_expect_failure '--index-only ff update, non-bare with uncommitted changes' '\n+\tgit clean -fdx &&\n+\tgit reset --hard &&\n+\tgit checkout A^0 &&\n+\n+\ttouch random_file && git add random_file &&\n+\n+\tgit merge --index-only --ff-only E^0 &&\n+\n+\ttest_must_fail git rev-parse HEAD:random_file &&\n+\ttest \"$(git diff --name-only --cached E)\" = \"random_file\" &&\n+\ttest $(git hash-object a) = $(git rev-parse A:a) &&\n+\ttest ! -d subdir\n+'\n+\n test_done\n-- \n2.8.0.18.gc685494\n"},{"id":"282936","messageId":"1460098726-5958-8-git-send-email-newren@gmail.com","threadId":"41962","inReplyTo":"1460098726-5958-1-git-send-email-newren@gmail.com","subject":"[RFC/PATCH 07/18] Add testcase for --index-only merges with the resolve strategy","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2016-04-08T06:58:35Z","receivedAt":"2016-04-08T06:58:35Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Including both tests which can be handled without calling the actual\ngit-merge-resolve program (because the merge is trivially resolvable\nwithout worrying about 3-way file merges), and ones that do need it to be\ninvoked.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n t/t6043-merge-index-only.sh | 104 ++++++++++++++++++++++++++++++++++++++++++--\n 1 file changed, 100 insertions(+), 4 deletions(-)\n\ndiff --git a/t/t6043-merge-index-only.sh b/t/t6043-merge-index-only.sh\nindex 67c8e92..4c3c40a 100755\n--- a/t/t6043-merge-index-only.sh\n+++ b/t/t6043-merge-index-only.sh\n@@ -167,14 +167,22 @@ test_expect_failure 'recursive --index-only in bare repo' '\n \t)\n '\n \n-# Testcase for a simple ff update\n+# Testcase for some simple merges\n #   A\n-#   o-----o E\n-#\n+#   o-----o B\n+#    \\\n+#     \\---o C\n+#      \\\n+#       \\-o D\n+#        \\\n+#         o E\n #   Commit A: some file a\n+#   Commit B: adds file b, modifies end of a\n+#   Commit C: adds file c\n+#   Commit D: adds file d, modifies beginning of a\n #   Commit E: renames a->subdir/a, adds subdir/e\n \n-test_expect_success 'setup simple ff update' '\n+test_expect_success 'setup simple merges' '\n \tgit reset --hard &&\n \tgit rm -rf . &&\n \tgit clean -fdqx &&\n@@ -186,8 +194,28 @@ test_expect_success 'setup simple ff update' '\n \ttest_tick && git commit -m A &&\n \n \tgit branch A &&\n+\tgit branch B &&\n+\tgit branch C &&\n+\tgit branch D &&\n \tgit branch E &&\n \n+\tgit checkout B &&\n+\techo b >b &&\n+\techo 11 >>a &&\n+\tgit add a b &&\n+\ttest_tick && git commit -m B &&\n+\n+\tgit checkout C &&\n+\techo c >c &&\n+\tgit add c &&\n+\ttest_tick && git commit -m C &&\n+\n+\tgit checkout D &&\n+\tseq 2 10 >a &&\n+\techo d >d &&\n+\tgit add a d &&\n+\ttest_tick && git commit -m D &&\n+\n \tgit checkout E &&\n \tmkdir subdir &&\n \tgit mv a subdir/a &&\n@@ -237,4 +265,72 @@ test_expect_failure '--index-only ff update, non-bare with uncommitted changes'\n \ttest ! -d subdir\n '\n \n+test_expect_failure '--index-only w/ resolve, trivial, non-bare' '\n+\tgit clean -fdx &&\n+\tgit reset --hard &&\n+\tgit checkout B^0 &&\n+\n+\tgit merge --index-only -s resolve C^0 | grep Wonderful &&\n+\n+\ttest \"$(git rev-list --count HEAD)\" -eq 4 &&\n+\ttest $(git rev-parse :a) = $(git rev-parse B:a) &&\n+\ttest $(git rev-parse :b) = $(git rev-parse B:b) &&\n+\ttest $(git rev-parse :c) = $(git rev-parse C:c) &&\n+\ttest ! -f c\n+'\n+\n+test_expect_failure '--index-only w/ resolve, trivial, bare' '\n+\trm -rf bare.clone &&\n+\tgit clone --bare . bare.clone &&\n+\t(cd bare.clone &&\n+\n+\t git update-ref --no-deref HEAD B &&\n+\t git read-tree HEAD &&\n+\n+\t git merge --index-only -s resolve C^0 | grep Wonderful &&\n+\n+\t test \"$(git rev-list --count HEAD)\" -eq 4 &&\n+\t test $(git rev-parse :a) = $(git rev-parse B:a) &&\n+\t test $(git rev-parse :b) = $(git rev-parse B:b) &&\n+\t test $(git rev-parse :c) = $(git rev-parse C:c) &&\n+\t test ! -f a &&\n+\t test ! -f b &&\n+\t test ! -f c\n+\t)\n+'\n+\n+test_expect_failure '--index-only w/ resolve, non-trivial, non-bare' '\n+\tgit reset --hard &&\n+\tgit checkout B^0 &&\n+\n+\tgit merge --index-only -s resolve D^0 &&\n+\n+\ttest \"$(git rev-list --count HEAD)\" -eq 4 &&\n+\ttest $(git rev-parse :a) != $(git rev-parse B:a) &&\n+\ttest $(git rev-parse :a) != $(git rev-parse D:a) &&\n+\ttest $(git rev-parse :b) = $(git rev-parse B:b) &&\n+\ttest $(git rev-parse :d) = $(git rev-parse D:d) &&\n+\ttest $(git hash-object a) = $(git rev-parse B:a) &&\n+\ttest ! -f d\n+'\n+\n+test_expect_failure '--index-only w/ resolve, non-trivial, bare' '\n+\trm -rf bare.clone &&\n+\tgit clone --bare . bare.clone &&\n+\t(cd bare.clone &&\n+\n+\t git update-ref --no-deref HEAD B &&\n+\t git read-tree HEAD &&\n+\n+\t git merge --index-only -s resolve D^0 &&\n+\n+\t test \"$(git rev-list --count HEAD)\" -eq 4 &&\n+\t test $(git rev-parse :a) != $(git rev-parse B:a) &&\n+\t test $(git rev-parse :a) != $(git rev-parse D:a) &&\n+\t test $(git rev-parse :b) = $(git rev-parse B:b) &&\n+\t test $(git rev-parse :d) = $(git rev-parse D:d) &&\n+\t test ! -f a\n+\t)\n+'\n+\n test_done\n-- \n2.8.0.18.gc685494\n"},{"id":"282937","messageId":"1460098726-5958-9-git-send-email-newren@gmail.com","threadId":"41962","inReplyTo":"1460098726-5958-1-git-send-email-newren@gmail.com","subject":"[RFC/PATCH 08/18] Add testcase for --index-only merges with the octopus strategy","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2016-04-08T06:58:36Z","receivedAt":"2016-04-08T06:58:36Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Signed-off-by: Elijah Newren <newren@gmail.com>\n---\n t/t6043-merge-index-only.sh | 41 +++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 41 insertions(+)\n\ndiff --git a/t/t6043-merge-index-only.sh b/t/t6043-merge-index-only.sh\nindex 4c3c40a..b01bf79 100755\n--- a/t/t6043-merge-index-only.sh\n+++ b/t/t6043-merge-index-only.sh\n@@ -333,4 +333,45 @@ test_expect_failure '--index-only w/ resolve, non-trivial, bare' '\n \t)\n '\n \n+test_expect_failure '--index-only octopus, non-bare' '\n+\tgit reset --hard &&\n+\tgit checkout B^0 &&\n+\n+\tgit merge --index-only -s octopus C^0 D^0 &&\n+\n+\ttest \"$(git rev-list --count HEAD)\" -eq 5 &&\n+\ttest $(git rev-parse :a) != $(git rev-parse B:a) &&\n+\ttest $(git rev-parse :a) != $(git rev-parse C:a) &&\n+\ttest $(git rev-parse :a) != $(git rev-parse D:a) &&\n+\ttest $(git rev-parse :b) = $(git rev-parse B:b) &&\n+\ttest $(git rev-parse :c) = $(git rev-parse C:c) &&\n+\ttest $(git rev-parse :d) = $(git rev-parse D:d) &&\n+\ttest $(git hash-object a) = $(git rev-parse B:a) &&\n+\ttest ! -f c &&\n+\ttest ! -f d\n+'\n+\n+test_expect_failure '--index-only octopus, bare' '\n+\trm -rf bare.clone &&\n+\tgit clone --bare . bare.clone &&\n+\t(cd bare.clone &&\n+\n+\t git update-ref --no-deref HEAD B &&\n+\t git read-tree HEAD &&\n+\n+\t git merge --index-only -s octopus C^0 D^0 &&\n+\n+\t test \"$(git rev-list --count HEAD)\" -eq 5 &&\n+\t test $(git rev-parse :a) != $(git rev-parse B:a) &&\n+\t test $(git rev-parse :a) != $(git rev-parse C:a) &&\n+\t test $(git rev-parse :a) != $(git rev-parse D:a) &&\n+\t test $(git rev-parse :b) = $(git rev-parse B:b) &&\n+\t test $(git rev-parse :c) = $(git rev-parse C:c) &&\n+\t test $(git rev-parse :d) = $(git rev-parse D:d) &&\n+\t test ! -f a &&\n+\t test ! -f c &&\n+\t test ! -f d\n+\t)\n+'\n+\n test_done\n-- \n2.8.0.18.gc685494\n"},{"id":"282929","messageId":"1460098726-5958-10-git-send-email-newren@gmail.com","threadId":"41962","inReplyTo":"1460098726-5958-1-git-send-email-newren@gmail.com","subject":"[RFC/PATCH 09/18] Add testcase for --index-only merges with the ours strategy","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2016-04-08T06:58:37Z","receivedAt":"2016-04-08T06:58:37Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"This is almost trivial to make work, because we already know that the\nworking tree will need no modifications, but lets test it for completeness\nanyway.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n t/t6043-merge-index-only.sh | 32 ++++++++++++++++++++++++++++++++\n 1 file changed, 32 insertions(+)\n\ndiff --git a/t/t6043-merge-index-only.sh b/t/t6043-merge-index-only.sh\nindex b01bf79..84c6640 100755\n--- a/t/t6043-merge-index-only.sh\n+++ b/t/t6043-merge-index-only.sh\n@@ -374,4 +374,36 @@ test_expect_failure '--index-only octopus, bare' '\n \t)\n '\n \n+test_expect_failure '--index-only ours, non-bare' '\n+\tgit reset --hard &&\n+\tgit checkout B^0 &&\n+\n+\tgit merge --index-only -s ours C^0 &&\n+\n+\ttest \"$(git rev-list --count HEAD)\" -eq 4 &&\n+\ttest $(git rev-parse :a) = $(git rev-parse B:a) &&\n+\ttest $(git rev-parse :b) = $(git rev-parse B:b) &&\n+\ttest_must_fail git rev-parse :c &&\n+\ttest ! -f c\n+'\n+\n+test_expect_failure '--index-only ours, bare' '\n+\trm -rf bare.clone &&\n+\tgit clone --bare . bare.clone &&\n+\t(cd bare.clone &&\n+\n+\t git update-ref --no-deref HEAD B &&\n+\t git read-tree HEAD &&\n+\n+\t git merge --index-only -s ours C^0 &&\n+\n+\t test \"$(git rev-list --count HEAD)\" -eq 4 &&\n+\t test $(git rev-parse :a) = $(git rev-parse B:a) &&\n+\t test $(git rev-parse :b) = $(git rev-parse B:b) &&\n+\t test_must_fail git rev-parse :c &&\n+\t test ! -f a &&\n+\t test ! -f c\n+\t)\n+'\n+\n test_done\n-- \n2.8.0.18.gc685494\n"},{"id":"282935","messageId":"1460098726-5958-11-git-send-email-newren@gmail.com","threadId":"41962","inReplyTo":"1460098726-5958-1-git-send-email-newren@gmail.com","subject":"[RFC/PATCH 10/18] Add testcase for --index-only merges with the subtree strategy","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2016-04-08T06:58:38Z","receivedAt":"2016-04-08T06:58:38Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Signed-off-by: Elijah Newren <newren@gmail.com>\n---\n t/t6043-merge-index-only.sh | 34 ++++++++++++++++++++++++++++++++++\n 1 file changed, 34 insertions(+)\n\ndiff --git a/t/t6043-merge-index-only.sh b/t/t6043-merge-index-only.sh\nindex 84c6640..5eda6b3 100755\n--- a/t/t6043-merge-index-only.sh\n+++ b/t/t6043-merge-index-only.sh\n@@ -406,4 +406,38 @@ test_expect_failure '--index-only ours, bare' '\n \t)\n '\n \n+test_expect_failure '--index-only subtree, non-bare' '\n+\tgit reset --hard &&\n+\tgit checkout B^0 &&\n+\n+\tgit merge --index-only -s subtree E^0 &&\n+\n+\ttest \"$(git rev-list --count HEAD)\" -eq 4 &&\n+\ttest $(git rev-parse :a) = $(git rev-parse B:a) &&\n+\ttest $(git rev-parse :b) = $(git rev-parse B:b) &&\n+\ttest $(git rev-parse :e) = $(git rev-parse E:subdir/e) &&\n+\ttest ! -d subdir &&\n+\ttest ! -f e\n+'\n+\n+test_expect_failure '--index-only subtree, bare' '\n+\trm -rf bare.clone &&\n+\tgit clone --bare . bare.clone &&\n+\t(cd bare.clone &&\n+\n+\t git update-ref --no-deref HEAD B &&\n+\t git read-tree HEAD &&\n+\n+\t git merge --index-only -s subtree E^0 &&\n+\n+\t test \"$(git rev-list --count HEAD)\" -eq 4 &&\n+\t test $(git rev-parse :a) = $(git rev-parse B:a) &&\n+\t test $(git rev-parse :b) = $(git rev-parse B:b) &&\n+\t test $(git rev-parse :e) = $(git rev-parse E:subdir/e) &&\n+\t test ! -d subdir &&\n+\t test ! -f a &&\n+\t test ! -f e\n+\t)\n+'\n+\n test_done\n-- \n2.8.0.18.gc685494\n"},{"id":"282939","messageId":"1460098726-5958-12-git-send-email-newren@gmail.com","threadId":"41962","inReplyTo":"1460098726-5958-1-git-send-email-newren@gmail.com","subject":"[RFC/PATCH 11/18] merge: Add a new --index-only option, not yet implemented","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2016-04-08T06:58:39Z","receivedAt":"2016-04-08T06:58:39Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Subsequent commits will add implementation for each relevant merge strategy\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n Documentation/git-merge.txt | 14 ++++++++++++++\n builtin/merge.c             |  7 +++++++\n git.c                       |  2 +-\n merge-recursive.c           |  1 +\n merge-recursive.h           |  1 +\n t/t6043-merge-index-only.sh |  4 ++--\n 6 files changed, 26 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt\nindex 07f7295..82a6d43 100644\n--- a/Documentation/git-merge.txt\n+++ b/Documentation/git-merge.txt\n@@ -86,6 +86,20 @@ invocations. The automated message can include the branch description.\n \tAllow the rerere mechanism to update the index with the\n \tresult of auto-conflict resolution if possible.\n \n+--index-only::\n+\tPerform merge on index only, leaving working tree alone.  Most\n+\tusers do NOT want to use this flag, as it will leave the\n+\tworking tree and the index completely out of sync, which is\n+\tvery likely to confuse users and prevent a subsequent 'git\n+\tmerge --abort' from working.  It is intended for script\n+\twriters to have a way to easily check whether a merge would\n+\tsucceed and which files would conflict, typically from bare\n+\tclones.\n++\n+Note that files other than the index in the git-dir may also be\n+modified when using this flag (e.g. MERGE_HEAD, MERGE_MSG, MERGE_RR,\n+HEAD, whatever HEAD points at, and new objects under objects/).\n+\n --abort::\n \tAbort the current conflict resolution process, and\n \ttry to reconstruct the pre-merge state.\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 101ffef..ce5be0a 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -63,6 +63,7 @@ static char *branch_mergeoptions;\n static int option_renormalize;\n static int verbosity;\n static int allow_rerere_auto;\n+static int index_only = 0;\n static int abort_current_merge;\n static int show_progress = -1;\n static int default_to_upstream = 1;\n@@ -221,6 +222,8 @@ static struct option builtin_merge_options[] = {\n \tOPT__VERBOSITY(&verbosity),\n \tOPT_BOOL(0, \"abort\", &abort_current_merge,\n \t\tN_(\"abort the current in-progress merge\")),\n+\tOPT_BOOL(0, \"index-only\", &index_only,\n+\t\tN_(\"perform merge on index only, leaving working tree alone\")),\n \tOPT_SET_INT(0, \"progress\", &show_progress, N_(\"force progress reporting\"), 1),\n \t{ OPTION_STRING, 'S', \"gpg-sign\", &sign_commit, N_(\"key-id\"),\n \t  N_(\"GPG sign commit\"), PARSE_OPT_OPTARG, NULL, (intptr_t) \"\" },\n@@ -663,6 +666,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,\n \t\t\to.subtree_shift = \"\";\n \n \t\to.renormalize = option_renormalize;\n+\t\to.index_only = index_only;\n \t\to.show_rename_progress =\n \t\t\tshow_progress == -1 ? isatty(2) : show_progress;\n \n@@ -1193,6 +1197,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \t\tparse_branch_merge_options(branch_mergeoptions);\n \targc = parse_options(argc, argv, prefix, builtin_merge_options,\n \t\t\tbuiltin_merge_usage, 0);\n+\tif (!index_only)\n+\t\tsetup_work_tree();\n+\n \tif (shortlog_len < 0)\n \t\tshortlog_len = (merge_log_config > 0) ? merge_log_config : 0;\n \ndiff --git a/git.c b/git.c\nindex 6cc0c07..b07485a 100644\n--- a/git.c\n+++ b/git.c\n@@ -427,7 +427,7 @@ static struct cmd_struct commands[] = {\n \t{ \"ls-tree\", cmd_ls_tree, RUN_SETUP },\n \t{ \"mailinfo\", cmd_mailinfo },\n \t{ \"mailsplit\", cmd_mailsplit },\n-\t{ \"merge\", cmd_merge, RUN_SETUP | NEED_WORK_TREE },\n+\t{ \"merge\", cmd_merge, RUN_SETUP },\n \t{ \"merge-base\", cmd_merge_base, RUN_SETUP },\n \t{ \"merge-file\", cmd_merge_file, RUN_SETUP_GENTLY },\n \t{ \"merge-index\", cmd_merge_index, RUN_SETUP },\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 06d31ed..b346ed6 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -2041,6 +2041,7 @@ void init_merge_options(struct merge_options *o)\n \to->merge_rename_limit = -1;\n \to->renormalize = 0;\n \to->detect_rename = 1;\n+\to->index_only = 0;\n \tmerge_recursive_config(o);\n \tif (getenv(\"GIT_MERGE_VERBOSITY\"))\n \t\to->verbosity =\ndiff --git a/merge-recursive.h b/merge-recursive.h\nindex 52f0201..7e9955f 100644\n--- a/merge-recursive.h\n+++ b/merge-recursive.h\n@@ -15,6 +15,7 @@ struct merge_options {\n \tconst char *subtree_shift;\n \tunsigned buffer_output : 1;\n \tunsigned renormalize : 1;\n+\tunsigned index_only : 1;\n \tlong xdl_opts;\n \tint verbosity;\n \tint detect_rename;\ndiff --git a/t/t6043-merge-index-only.sh b/t/t6043-merge-index-only.sh\nindex 5eda6b3..9bb64d8 100755\n--- a/t/t6043-merge-index-only.sh\n+++ b/t/t6043-merge-index-only.sh\n@@ -374,7 +374,7 @@ test_expect_failure '--index-only octopus, bare' '\n \t)\n '\n \n-test_expect_failure '--index-only ours, non-bare' '\n+test_expect_success '--index-only ours, non-bare' '\n \tgit reset --hard &&\n \tgit checkout B^0 &&\n \n@@ -387,7 +387,7 @@ test_expect_failure '--index-only ours, non-bare' '\n \ttest ! -f c\n '\n \n-test_expect_failure '--index-only ours, bare' '\n+test_expect_success '--index-only ours, bare' '\n \trm -rf bare.clone &&\n \tgit clone --bare . bare.clone &&\n \t(cd bare.clone &&\n-- \n2.8.0.18.gc685494\n"},{"id":"282938","messageId":"1460098726-5958-13-git-send-email-newren@gmail.com","threadId":"41962","inReplyTo":"1460098726-5958-1-git-send-email-newren@gmail.com","subject":"[RFC/PATCH 12/18] Add --index-only support for recursive merges","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2016-04-08T06:58:40Z","receivedAt":"2016-04-08T06:58:40Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"The merge-recursive logic already had the ability to ignore the working\ndirectory and operate entirely on the index -- it needed to do this when\ncreating a virtual merge base, i.e. when o->call_depth > 0.\n\nThe only trick here is that o->call_depth > 0 was also checked to\ndetermine whether all merges conflicts should be forcibly immediately\nresolved (typically by treating a copy of the code with conflict markers\nstill in it as the resolution) in order to get us some actual base tree\nto work with.\n\nIntroduce a new merge option o->index_only which is true whenver either\n--index-only is passed or o->call_depth > 0, and make the portions of\nmerge-recursive that are about operating only on the index look at\no->index_only, and the portions that are about forcibly immediately\nresolving conflicts check o->call_depth.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n merge-recursive.c           | 37 ++++++++++++++++++++++---------------\n t/t6043-merge-index-only.sh | 12 ++++++------\n 2 files changed, 28 insertions(+), 21 deletions(-)\n\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex b346ed6..6af37ed 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -406,10 +406,11 @@ static void record_df_conflict_files(struct merge_options *o,\n \tint i;\n \n \t/*\n-\t * If we're merging merge-bases, we don't want to bother with\n-\t * any working directory changes.\n+\t * If we're doing an index-only merge, we don't need to check for\n+\t * which files to remove from the working copy to make room for\n+\t * paths below directories of D/F conflicts; we can just exit early.\n \t */\n-\tif (o->call_depth)\n+\tif (o->index_only)\n \t\treturn;\n \n \t/* Ensure D/F conflicts are adjacent in the entries list. */\n@@ -581,7 +582,7 @@ static int remove_file(struct merge_options *o, int clean,\n \t\t       const char *path, int no_wd)\n {\n \tint update_cache = o->call_depth || clean;\n-\tint update_working_directory = !o->call_depth && !no_wd;\n+\tint update_working_directory = !o->index_only && !no_wd;\n \n \tif (update_cache) {\n \t\tif (remove_file_from_cache(path))\n@@ -622,7 +623,7 @@ static char *unique_path(struct merge_options *o, const char *path, const char *\n \tbase_len = newpath.len;\n \twhile (string_list_has_string(&o->current_file_set, newpath.buf) ||\n \t       string_list_has_string(&o->current_directory_set, newpath.buf) ||\n-\t       (!o->call_depth && file_exists(newpath.buf))) {\n+\t       (!o->index_only && file_exists(newpath.buf))) {\n \t\tstrbuf_setlen(&newpath, base_len);\n \t\tstrbuf_addf(&newpath, \"_%d\", suffix++);\n \t}\n@@ -742,7 +743,7 @@ static void update_file_flags(struct merge_options *o,\n \t\t\t      int update_cache,\n \t\t\t      int update_wd)\n {\n-\tif (o->call_depth)\n+\tif (o->index_only)\n \t\tupdate_wd = 0;\n \n \tif (update_wd) {\n@@ -813,7 +814,7 @@ static void update_file(struct merge_options *o,\n \t\t\tunsigned mode,\n \t\t\tconst char *path)\n {\n-\tupdate_file_flags(o, sha, mode, path, o->call_depth || clean, !o->call_depth);\n+\tupdate_file_flags(o, sha, mode, path, o->call_depth || clean, !o->index_only);\n }\n \n /* Low level file merging, update and removal */\n@@ -1017,7 +1018,7 @@ static void handle_change_delete(struct merge_options *o,\n \t\t\t\t const char *change, const char *change_past)\n {\n \tchar *renamed = NULL;\n-\tif (dir_in_way(path, !o->call_depth)) {\n+\tif (dir_in_way(path, !o->index_only)) {\n \t\trenamed = unique_path(o, path, a_sha ? o->branch1 : o->branch2);\n \t}\n \n@@ -1145,7 +1146,7 @@ static void handle_file(struct merge_options *o,\n \t\tremove_file(o, 0, rename->path, 0);\n \t\tdst_name = unique_path(o, rename->path, cur_branch);\n \t} else {\n-\t\tif (dir_in_way(rename->path, !o->call_depth)) {\n+\t\tif (dir_in_way(rename->path, !o->index_only)) {\n \t\t\tdst_name = unique_path(o, rename->path, cur_branch);\n \t\t\toutput(o, 1, _(\"%s is a directory in %s adding as %s instead\"),\n \t\t\t       rename->path, other_branch, dst_name);\n@@ -1234,8 +1235,8 @@ static void conflict_rename_rename_2to1(struct merge_options *o,\n \t       a->path, c1->path, ci->branch1,\n \t       b->path, c2->path, ci->branch2);\n \n-\tremove_file(o, 1, a->path, o->call_depth || would_lose_untracked(a->path));\n-\tremove_file(o, 1, b->path, o->call_depth || would_lose_untracked(b->path));\n+\tremove_file(o, 1, a->path, o->index_only || would_lose_untracked(a->path));\n+\tremove_file(o, 1, b->path, o->index_only || would_lose_untracked(b->path));\n \n \tmfi_c1 = merge_file_special_markers(o, a, c1, &ci->ren1_other,\n \t\t\t\t\t    o->branch1, c1->path,\n@@ -1619,7 +1620,7 @@ static int merge_content(struct merge_options *o,\n \t\t\t o->branch2 == rename_conflict_info->branch1) ?\n \t\t\tpair1->two->path : pair1->one->path;\n \n-\t\tif (dir_in_way(path, !o->call_depth))\n+\t\tif (dir_in_way(path, !o->index_only))\n \t\t\tdf_conflict_remains = 1;\n \t}\n \tmfi = merge_file_special_markers(o, &one, &a, &b,\n@@ -1639,7 +1640,7 @@ static int merge_content(struct merge_options *o,\n \t\tpath_renamed_outside_HEAD = !path2 || !strcmp(path, path2);\n \t\tif (!path_renamed_outside_HEAD) {\n \t\t\tadd_cacheinfo(mfi.mode, mfi.sha, path,\n-\t\t\t\t      0, (!o->call_depth), 0);\n+\t\t\t\t      0, (!o->index_only), 0);\n \t\t\treturn mfi.clean;\n \t\t}\n \t} else\n@@ -1767,7 +1768,7 @@ static int process_entry(struct merge_options *o,\n \t\t\tsha = b_sha;\n \t\t\tconf = _(\"directory/file\");\n \t\t}\n-\t\tif (dir_in_way(path, !o->call_depth)) {\n+\t\tif (dir_in_way(path, !o->index_only)) {\n \t\t\tchar *new_path = unique_path(o, path, add_branch);\n \t\t\tclean_merge = 0;\n \t\t\toutput(o, 1, _(\"CONFLICT (%s): There is a directory with name %s in %s. \"\n@@ -1819,7 +1820,7 @@ int merge_trees(struct merge_options *o,\n \t\treturn 1;\n \t}\n \n-\tcode = git_merge_trees(o->call_depth, common, head, merge);\n+\tcode = git_merge_trees(o->index_only, common, head, merge);\n \n \tif (code != 0) {\n \t\tif (show(o, 4) || o->call_depth)\n@@ -1899,6 +1900,7 @@ int merge_recursive(struct merge_options *o,\n \tstruct commit *merged_common_ancestors;\n \tstruct tree *mrtree = mrtree;\n \tint clean;\n+\tint prev_index_only_setting;\n \n \tif (show(o, 4)) {\n \t\toutput(o, 4, _(\"Merging:\"));\n@@ -1929,9 +1931,12 @@ int merge_recursive(struct merge_options *o,\n \t\tmerged_common_ancestors = make_virtual_commit(tree, \"ancestor\");\n \t}\n \n+\tprev_index_only_setting = o->index_only;\n+\n \tfor (iter = ca; iter; iter = iter->next) {\n \t\tconst char *saved_b1, *saved_b2;\n \t\to->call_depth++;\n+\t\to->index_only = 1;\n \t\t/*\n \t\t * When the merge fails, the result contains files\n \t\t * with conflict markers. The cleanness flag is\n@@ -1954,6 +1959,8 @@ int merge_recursive(struct merge_options *o,\n \t\t\tdie(_(\"merge returned no commit\"));\n \t}\n \n+\to->index_only = prev_index_only_setting;\n+\n \tdiscard_cache();\n \tif (!o->call_depth)\n \t\tread_cache();\ndiff --git a/t/t6043-merge-index-only.sh b/t/t6043-merge-index-only.sh\nindex 9bb64d8..2e1d953 100755\n--- a/t/t6043-merge-index-only.sh\n+++ b/t/t6043-merge-index-only.sh\n@@ -28,7 +28,7 @@ test_expect_success 'setup rename/modify merge' '\n \tgit commit -m C\n '\n \n-test_expect_failure '--index-only with rename/modify works in non-bare-clone' '\n+test_expect_success '--index-only with rename/modify works in non-bare-clone' '\n \tgit checkout B^0 &&\n \n \tgit merge --index-only -s recursive C^0 &&\n@@ -43,7 +43,7 @@ test_expect_failure '--index-only with rename/modify works in non-bare-clone' '\n \ttest $(git rev-parse B:a) = $(git rev-parse :b)\n '\n \n-test_expect_failure '--index-only with rename/modify works in a bare clone' '\n+test_expect_success '--index-only with rename/modify works in a bare clone' '\n \tgit clone --bare . bare.clone &&\n \t(cd bare.clone &&\n \n@@ -136,7 +136,7 @@ test_expect_success 'setup single-file criss-cross resolvable with recursive str\n \trm -f answer\n '\n \n-test_expect_failure 'recursive --index-only in non-bare repo' '\n+test_expect_success 'recursive --index-only in non-bare repo' '\n \tgit reset --hard &&\n \tgit checkout L2^0 &&\n \n@@ -150,7 +150,7 @@ test_expect_failure 'recursive --index-only in non-bare repo' '\n \ttest $(git rev-parse L2:contents) = $(git hash-object contents)\n '\n \n-test_expect_failure 'recursive --index-only in bare repo' '\n+test_expect_success 'recursive --index-only in bare repo' '\n \tgit clone --bare . bare.clone &&\n \t(cd bare.clone &&\n \n@@ -406,7 +406,7 @@ test_expect_success '--index-only ours, bare' '\n \t)\n '\n \n-test_expect_failure '--index-only subtree, non-bare' '\n+test_expect_success '--index-only subtree, non-bare' '\n \tgit reset --hard &&\n \tgit checkout B^0 &&\n \n@@ -420,7 +420,7 @@ test_expect_failure '--index-only subtree, non-bare' '\n \ttest ! -f e\n '\n \n-test_expect_failure '--index-only subtree, bare' '\n+test_expect_success '--index-only subtree, bare' '\n \trm -rf bare.clone &&\n \tgit clone --bare . bare.clone &&\n \t(cd bare.clone &&\n-- \n2.8.0.18.gc685494\n"},{"id":"282923","messageId":"1460098726-5958-14-git-send-email-newren@gmail.com","threadId":"41962","inReplyTo":"1460098726-5958-1-git-send-email-newren@gmail.com","subject":"[RFC/PATCH 13/18] Add --index-only support with read_tree_trivial merges, kind of","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2016-04-08T06:58:41Z","receivedAt":"2016-04-08T06:58:41Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"From: Elijah Newren <newren@palantir.com>\n\nThis almost works.  It creates the correct merge commit, updates the\nbranch, but then if the repo is bare it leaves the index as it was before\nthe merge (and if the repo is non-bare it updates the index).  I'm\ntotally lost as to why the testcase behaves differently in the bare and\nnon-bare cases.\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n builtin/merge.c             | 3 ++-\n t/t6043-merge-index-only.sh | 2 +-\n 2 files changed, 3 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex ce5be0a..5f463ad 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -606,7 +606,8 @@ static int read_tree_trivial(unsigned char *common, unsigned char *head,\n \topts.head_idx = 2;\n \topts.src_index = &the_index;\n \topts.dst_index = &the_index;\n-\topts.update = 1;\n+\topts.update = !index_only;\n+\topts.index_only = index_only;\n \topts.verbose_update = 1;\n \topts.trivial_merges_only = 1;\n \topts.merge = 1;\ndiff --git a/t/t6043-merge-index-only.sh b/t/t6043-merge-index-only.sh\nindex 2e1d953..f79782c 100755\n--- a/t/t6043-merge-index-only.sh\n+++ b/t/t6043-merge-index-only.sh\n@@ -265,7 +265,7 @@ test_expect_failure '--index-only ff update, non-bare with uncommitted changes'\n \ttest ! -d subdir\n '\n \n-test_expect_failure '--index-only w/ resolve, trivial, non-bare' '\n+test_expect_success '--index-only w/ resolve, trivial, non-bare' '\n \tgit clean -fdx &&\n \tgit reset --hard &&\n \tgit checkout B^0 &&\n-- \n2.8.0.18.gc685494\n"},{"id":"282931","messageId":"1460098726-5958-15-git-send-email-newren@gmail.com","threadId":"41962","inReplyTo":"1460098726-5958-1-git-send-email-newren@gmail.com","subject":"[RFC/PATCH 14/18] Add --index-only support for ff_only merges","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2016-04-08T06:58:42Z","receivedAt":"2016-04-08T06:58:42Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"From: Elijah Newren <newren@palantir.com>\n\nSigned-off-by: Elijah Newren <newren@gmail.com>\n---\n builtin/merge.c             | 1 +\n builtin/pull.c              | 4 ++--\n cache.h                     | 1 +\n merge.c                     | 4 +++-\n sequencer.c                 | 2 +-\n t/t6043-merge-index-only.sh | 6 +++---\n 6 files changed, 11 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 5f463ad..b791702 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -1443,6 +1443,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)\n \n \t\tif (checkout_fast_forward(head_commit->object.oid.hash,\n \t\t\t\t\t  commit->object.oid.hash,\n+\t\t\t\t\t  index_only,\n \t\t\t\t\t  overwrite_ignore)) {\n \t\t\tret = 1;\n \t\t\tgoto done;\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 10eff03..4f33a89 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -569,7 +569,7 @@ static int pull_into_void(const unsigned char *merge_head,\n \t * index/worktree changes that the user already made on the unborn\n \t * branch.\n \t */\n-\tif (checkout_fast_forward(EMPTY_TREE_SHA1_BIN, merge_head, 0))\n+\tif (checkout_fast_forward(EMPTY_TREE_SHA1_BIN, merge_head, 0, 0))\n \t\treturn 1;\n \n \tif (update_ref(\"initial pull\", \"HEAD\", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR))\n@@ -871,7 +871,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)\n \t\t\t\"fast-forwarding your working tree from\\n\"\n \t\t\t\"commit %s.\"), sha1_to_hex(orig_head));\n \n-\t\tif (checkout_fast_forward(orig_head, curr_head, 0))\n+\t\tif (checkout_fast_forward(orig_head, curr_head, 0, 0))\n \t\t\tdie(_(\"Cannot fast-forward your working tree.\\n\"\n \t\t\t\t\"After making sure that you saved anything precious from\\n\"\n \t\t\t\t\"$ git diff %s\\n\"\ndiff --git a/cache.h b/cache.h\nindex b829410..d51fcbc 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1785,6 +1785,7 @@ int try_merge_command(const char *strategy, size_t xopts_nr,\n \t\tconst char *head_arg, struct commit_list *remotes);\n int checkout_fast_forward(const unsigned char *from,\n \t\t\t  const unsigned char *to,\n+\t\t\t  int index_only,\n \t\t\t  int overwrite_ignore);\n \n \ndiff --git a/merge.c b/merge.c\nindex 5db7d56..a40307c 100644\n--- a/merge.c\n+++ b/merge.c\n@@ -46,6 +46,7 @@ int try_merge_command(const char *strategy, size_t xopts_nr,\n \n int checkout_fast_forward(const unsigned char *head,\n \t\t\t  const unsigned char *remote,\n+\t\t\t  int index_only,\n \t\t\t  int overwrite_ignore)\n {\n \tstruct tree *trees[MAX_UNPACK_TREES];\n@@ -72,7 +73,8 @@ int checkout_fast_forward(const unsigned char *head,\n \topts.head_idx = 1;\n \topts.src_index = &the_index;\n \topts.dst_index = &the_index;\n-\topts.update = 1;\n+\topts.update = !index_only;\n+\topts.index_only = index_only;\n \topts.verbose_update = 1;\n \topts.merge = 1;\n \topts.fn = twoway_merge;\ndiff --git a/sequencer.c b/sequencer.c\nindex e66f2fe..1b772fb 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -222,7 +222,7 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from,\n \tstruct strbuf err = STRBUF_INIT;\n \n \tread_cache();\n-\tif (checkout_fast_forward(from, to, 1))\n+\tif (checkout_fast_forward(from, to, 0, 1))\n \t\texit(128); /* the callee should have complained already */\n \n \tstrbuf_addf(&sb, \"%s: fast-forward\", action_name(opts));\ndiff --git a/t/t6043-merge-index-only.sh b/t/t6043-merge-index-only.sh\nindex f79782c..cb860f2 100755\n--- a/t/t6043-merge-index-only.sh\n+++ b/t/t6043-merge-index-only.sh\n@@ -224,7 +224,7 @@ test_expect_success 'setup simple merges' '\n \ttest_tick && git commit -m E\n '\n \n-test_expect_failure '--index-only ff update, non-bare' '\n+test_expect_success '--index-only ff update, non-bare' '\n \tgit reset --hard &&\n \tgit checkout A^0 &&\n \n@@ -235,7 +235,7 @@ test_expect_failure '--index-only ff update, non-bare' '\n \ttest ! -d subdir\n '\n \n-test_expect_failure '--index-only ff update, bare' '\n+test_expect_success '--index-only ff update, bare' '\n \tgit clone --bare . bare.clone &&\n \t(cd bare.clone &&\n \n@@ -250,7 +250,7 @@ test_expect_failure '--index-only ff update, bare' '\n \t)\n '\n \n-test_expect_failure '--index-only ff update, non-bare with uncommitted changes' '\n+test_expect_success '--index-only ff update, non-bare with uncommitted changes' '\n \tgit clean -fdx &&\n \tgit reset --hard &&\n \tgit checkout A^0 &&\n-- \n2.8.0.18.gc685494\n"},{"id":"282934","messageId":"1460098726-5958-16-git-send-email-newren@gmail.com","threadId":"41962","inReplyTo":"1460098726-5958-1-git-send-email-newren@gmail.com","subject":"[RFC/PATCH 15/18] merge: Pass --index-only along to external merge strategy programs","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2016-04-08T06:58:43Z","receivedAt":"2016-04-08T06:58:43Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Signed-off-by: Elijah Newren <newren@gmail.com>\n---\n builtin/merge-index.c | 9 ++++++++-\n builtin/merge.c       | 2 +-\n cache.h               | 2 +-\n merge.c               | 4 +++-\n sequencer.c           | 2 +-\n 5 files changed, 14 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/merge-index.c b/builtin/merge-index.c\nindex 1c3427c..53cc285 100644\n--- a/builtin/merge-index.c\n+++ b/builtin/merge-index.c\n@@ -4,14 +4,17 @@\n static const char *pgm;\n static int one_shot, quiet;\n static int err;\n+static int index_only;\n \n static int merge_entry(int pos, const char *path)\n {\n \tint found;\n-\tconst char *arguments[] = { pgm, \"\", \"\", \"\", path, \"\", \"\", \"\", NULL };\n+\tconst char *arguments[] = { pgm, \"\", \"\", \"\", path, \"\", \"\", \"\", \"--index-only\", NULL };\n \tchar hexbuf[4][GIT_SHA1_HEXSZ + 1];\n \tchar ownbuf[4][60];\n \n+\tif (!index_only)\n+\t\targuments[8] = NULL;\n \tif (pos >= active_nr)\n \t\tdie(\"git merge-index: %s not in the cache\", path);\n \tfound = 0;\n@@ -96,6 +99,10 @@ int cmd_merge_index(int argc, const char **argv, const char *prefix)\n \t\t\t\tforce_file = 1;\n \t\t\t\tcontinue;\n \t\t\t}\n+\t\t\tif (!strcmp(arg, \"--index-only\")) {\n+\t\t\t\tindex_only = 1;\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t\tif (!strcmp(arg, \"-a\")) {\n \t\t\t\tmerge_all();\n \t\t\t\tcontinue;\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex b791702..d1fe3f9 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -690,7 +690,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,\n \t\trollback_lock_file(&lock);\n \t\treturn clean ? 0 : 1;\n \t} else {\n-\t\treturn try_merge_command(strategy, xopts_nr, xopts,\n+\t\treturn try_merge_command(strategy, index_only, xopts_nr, xopts,\n \t\t\t\t\t\tcommon, head_arg, remoteheads);\n \t}\n }\ndiff --git a/cache.h b/cache.h\nindex d51fcbc..d017155 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1780,7 +1780,7 @@ extern struct startup_info *startup_info;\n \n /* merge.c */\n struct commit_list;\n-int try_merge_command(const char *strategy, size_t xopts_nr,\n+int try_merge_command(const char *strategy, int index_only, size_t xopts_nr,\n \t\tconst char **xopts, struct commit_list *common,\n \t\tconst char *head_arg, struct commit_list *remotes);\n int checkout_fast_forward(const unsigned char *from,\ndiff --git a/merge.c b/merge.c\nindex a40307c..705a8d7 100644\n--- a/merge.c\n+++ b/merge.c\n@@ -15,7 +15,7 @@ static const char *merge_argument(struct commit *commit)\n \t\treturn EMPTY_TREE_SHA1_HEX;\n }\n \n-int try_merge_command(const char *strategy, size_t xopts_nr,\n+int try_merge_command(const char *strategy, int index_only, size_t xopts_nr,\n \t\t      const char **xopts, struct commit_list *common,\n \t\t      const char *head_arg, struct commit_list *remotes)\n {\n@@ -24,6 +24,8 @@ int try_merge_command(const char *strategy, size_t xopts_nr,\n \tstruct commit_list *j;\n \n \targv_array_pushf(&args, \"merge-%s\", strategy);\n+\tif (index_only)\n+\t\targv_array_pushf(&args, \"--index-only\");\n \tfor (i = 0; i < xopts_nr; i++)\n \t\targv_array_pushf(&args, \"--%s\", xopts[i]);\n \tfor (j = common; j; j = j->next)\ndiff --git a/sequencer.c b/sequencer.c\nindex 1b772fb..2fa1101 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -570,7 +570,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)\n \n \t\tcommit_list_insert(base, &common);\n \t\tcommit_list_insert(next, &remotes);\n-\t\tres = try_merge_command(opts->strategy, opts->xopts_nr, opts->xopts,\n+\t\tres = try_merge_command(opts->strategy, 0, opts->xopts_nr, opts->xopts,\n \t\t\t\t\tcommon, sha1_to_hex(head), remotes);\n \t\tfree_commit_list(common);\n \t\tfree_commit_list(remotes);\n-- \n2.8.0.18.gc685494\n"},{"id":"282928","messageId":"1460098726-5958-17-git-send-email-newren@gmail.com","threadId":"41962","inReplyTo":"1460098726-5958-1-git-send-email-newren@gmail.com","subject":"[RFC/PATCH 16/18] git-merge-one-file.sh: support --index-only option","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2016-04-08T06:58:44Z","receivedAt":"2016-04-08T06:58:44Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Signed-off-by: Elijah Newren <newren@gmail.com>\n---\n git-merge-one-file.sh | 44 +++++++++++++++++++++++++++++++++++---------\n 1 file changed, 35 insertions(+), 9 deletions(-)\n\ndiff --git a/git-merge-one-file.sh b/git-merge-one-file.sh\nindex 424b034..78efa00 100755\n--- a/git-merge-one-file.sh\n+++ b/git-merge-one-file.sh\n@@ -18,16 +18,25 @@\n \n USAGE='<orig blob> <our blob> <their blob> <path>'\n USAGE=\"$USAGE <orig mode> <our mode> <their mode>\"\n-LONG_USAGE=\"usage: git merge-one-file $USAGE\n+LONG_USAGE=\"usage: git merge-one-file $USAGE [--index-only]\n \n Blob ids and modes should be empty for missing files.\"\n \n+update_worktree=t\n+expected_argcount=7\n+if test \"$8\" = \"--index-only\"; then\n+\tupdate_worktree=\n+\texpected_argcount=8\n+fi\n+\n SUBDIRECTORY_OK=Yes\n . git-sh-setup\n cd_to_toplevel\n-require_work_tree\n+if test -n \"$update_worktree\"; then\n+\trequire_work_tree\n+fi\n \n-if test $# != 7\n+if test $# != $expected_argcount\n then\n \techo \"$LONG_USAGE\"\n \texit 1\n@@ -59,8 +68,10 @@ case \"${1:-.}${2:-.}${3:-.}\" in\n \tfi\n \tif test -f \"$4\"\n \tthen\n-\t\trm -f -- \"$4\" &&\n-\t\trmdir -p \"$(expr \"z$4\" : 'z\\(.*\\)/')\" 2>/dev/null || :\n+\t\tif test -n \"$update_worktree\"; then\n+\t\t\trm -f -- \"$4\" &&\n+\t\t\trmdir -p \"$(expr \"z$4\" : 'z\\(.*\\)/')\" 2>/dev/null || :\n+\t\tfi\n \tfi &&\n \t\texec git update-index --remove -- \"$4\"\n \t;;\n@@ -80,8 +91,11 @@ case \"${1:-.}${2:-.}${3:-.}\" in\n \t\techo \"ERROR: untracked $4 is overwritten by the merge.\" >&2\n \t\texit 1\n \tfi\n-\tgit update-index --add --cacheinfo \"$7\" \"$3\" \"$4\" &&\n+\tgit update-index --add --cacheinfo \"$7\" \"$3\" \"$4\"\n+\tif test -n \"$update_worktree\"; then\n \t\texec git checkout-index -u -f -- \"$4\"\n+\tfi\n+\texit 0\n \t;;\n \n #\n@@ -95,8 +109,11 @@ case \"${1:-.}${2:-.}${3:-.}\" in\n \t\texit 1\n \tfi\n \techo \"Adding $4\"\n-\tgit update-index --add --cacheinfo \"$6\" \"$2\" \"$4\" &&\n+\tgit update-index --add --cacheinfo \"$6\" \"$2\" \"$4\"\n+\tif test -n \"$update_worktree\"; then\n \t\texec git checkout-index -u -f -- \"$4\"\n+\tfi\n+\texit 0\n \t;;\n \n #\n@@ -139,7 +156,11 @@ case \"${1:-.}${2:-.}${3:-.}\" in\n \n \t# Create the working tree file, using \"our tree\" version from the\n \t# index, and then store the result of the merge.\n-\tgit checkout-index -f --stage=2 -- \"$4\" && cat \"$src1\" >\"$4\" || exit 1\n+\tif test -n \"$update_worktree\"; then\n+\t\tgit checkout-index -f --stage=2 -- \"$4\" && cat \"$src1\" >\"$4\" || exit 1\n+\telse\n+\t\tsha1=$(git hash-object -w -- \"$src1\")\n+\tfi\n \trm -f -- \"$orig\" \"$src1\" \"$src2\"\n \n \tif test \"$6\" != \"$7\"\n@@ -157,7 +178,12 @@ case \"${1:-.}${2:-.}${3:-.}\" in\n \t\techo \"ERROR: $msg in $4\" >&2\n \t\texit 1\n \tfi\n-\texec git update-index -- \"$4\"\n+\tif test -n \"$update_worktree\"; then\n+\t\texec git update-index -- \"$4\"\n+\telse\n+\t\tprintf \"$6 $sha1\\t$4\" | git update-index --index-info\n+\t\texit $?\n+\tfi\n \t;;\n \n *)\n-- \n2.8.0.18.gc685494\n"},{"id":"282925","messageId":"1460098726-5958-18-git-send-email-newren@gmail.com","threadId":"41962","inReplyTo":"1460098726-5958-1-git-send-email-newren@gmail.com","subject":"[RFC/PATCH 17/18] git-merge-resolve.sh: support --index-only option","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2016-04-08T06:58:45Z","receivedAt":"2016-04-08T06:58:45Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Signed-off-by: Elijah Newren <newren@gmail.com>\n---\n git-merge-resolve.sh        | 12 ++++++++++--\n t/t6043-merge-index-only.sh |  4 ++--\n 2 files changed, 12 insertions(+), 4 deletions(-)\n\ndiff --git a/git-merge-resolve.sh b/git-merge-resolve.sh\nindex c9da747..ed4a25b 100755\n--- a/git-merge-resolve.sh\n+++ b/git-merge-resolve.sh\n@@ -5,6 +5,14 @@\n #\n # Resolve two trees, using enhanced multi-base read-tree.\n \n+read_tree_update=\"-u\"\n+index_only=\n+if test \"$1\" = \"--index-only\"; then\n+\tread_tree_update=\"-i\"\n+\tindex_only=\"--index-only\"\n+\tshift\n+fi\n+\n # The first parameters up to -- are merge bases; the rest are heads.\n bases= head= remotes= sep_seen=\n for arg\n@@ -38,14 +46,14 @@ then\n fi\n \n git update-index -q --refresh\n-git read-tree -u -m --aggressive $bases $head $remotes || exit 2\n+git read-tree $read_tree_update -m --aggressive $bases $head $remotes || exit 2\n echo \"Trying simple merge.\"\n if result_tree=$(git write-tree 2>/dev/null)\n then\n \texit 0\n else\n \techo \"Simple merge failed, trying Automatic merge.\"\n-\tif git-merge-index -o git-merge-one-file -a\n+\tif git-merge-index -o git-merge-one-file $index_only -a\n \tthen\n \t\texit 0\n \telse\ndiff --git a/t/t6043-merge-index-only.sh b/t/t6043-merge-index-only.sh\nindex cb860f2..c0a553b 100755\n--- a/t/t6043-merge-index-only.sh\n+++ b/t/t6043-merge-index-only.sh\n@@ -299,7 +299,7 @@ test_expect_failure '--index-only w/ resolve, trivial, bare' '\n \t)\n '\n \n-test_expect_failure '--index-only w/ resolve, non-trivial, non-bare' '\n+test_expect_success '--index-only w/ resolve, non-trivial, non-bare' '\n \tgit reset --hard &&\n \tgit checkout B^0 &&\n \n@@ -314,7 +314,7 @@ test_expect_failure '--index-only w/ resolve, non-trivial, non-bare' '\n \ttest ! -f d\n '\n \n-test_expect_failure '--index-only w/ resolve, non-trivial, bare' '\n+test_expect_success '--index-only w/ resolve, non-trivial, bare' '\n \trm -rf bare.clone &&\n \tgit clone --bare . bare.clone &&\n \t(cd bare.clone &&\n-- \n2.8.0.18.gc685494\n"},{"id":"282940","messageId":"1460098726-5958-19-git-send-email-newren@gmail.com","threadId":"41962","inReplyTo":"1460098726-5958-1-git-send-email-newren@gmail.com","subject":"[RFC/PATCH 18/18] git-merge-octopus.sh: support --index-only option","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2016-04-08T06:58:46Z","receivedAt":"2016-04-08T06:58:46Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Signed-off-by: Elijah Newren <newren@gmail.com>\n---\n git-merge-octopus.sh        | 14 +++++++++++---\n t/t6043-merge-index-only.sh |  4 ++--\n 2 files changed, 13 insertions(+), 5 deletions(-)\n\ndiff --git a/git-merge-octopus.sh b/git-merge-octopus.sh\nindex a1d7702..e7915dc 100755\n--- a/git-merge-octopus.sh\n+++ b/git-merge-octopus.sh\n@@ -13,6 +13,14 @@ die () {\n     exit 1\n }\n \n+read_tree_update=\"-u\"\n+index_only=\n+if test \"$1\" = \"--index-only\"; then\n+\tread_tree_update=\"-i\"\n+\tindex_only=\"--index-only\"\n+\tshift\n+fi\n+\n # The first parameters up to -- are merge bases; the rest are heads.\n bases= head= remotes= sep_seen=\n for arg\n@@ -89,7 +97,7 @@ do\n \t\t# We still need to count this as part of the parent set.\n \n \t\techo \"Fast-forwarding to: $pretty_name\"\n-\t\tgit read-tree -u -m $head $SHA1 || exit\n+\t\tgit read-tree $read_tree_update -m $head $SHA1 || exit\n \t\tMRC=$SHA1 MRT=$(git write-tree)\n \t\tcontinue\n \tfi\n@@ -97,12 +105,12 @@ do\n \tNON_FF_MERGE=1\n \n \techo \"Trying simple merge with $pretty_name\"\n-\tgit read-tree -u -m --aggressive  $common $MRT $SHA1 || exit 2\n+\tgit read-tree $read_tree_update -m --aggressive  $common $MRT $SHA1 || exit 2\n \tnext=$(git write-tree 2>/dev/null)\n \tif test $? -ne 0\n \tthen\n \t\techo \"Simple merge did not work, trying automatic merge.\"\n-\t\tgit-merge-index -o git-merge-one-file -a ||\n+\t\tgit-merge-index -o git-merge-one-file $index_only -a ||\n \t\tOCTOPUS_FAILURE=1\n \t\tnext=$(git write-tree 2>/dev/null)\n \tfi\ndiff --git a/t/t6043-merge-index-only.sh b/t/t6043-merge-index-only.sh\nindex c0a553b..080e03d 100755\n--- a/t/t6043-merge-index-only.sh\n+++ b/t/t6043-merge-index-only.sh\n@@ -333,7 +333,7 @@ test_expect_success '--index-only w/ resolve, non-trivial, bare' '\n \t)\n '\n \n-test_expect_failure '--index-only octopus, non-bare' '\n+test_expect_success '--index-only octopus, non-bare' '\n \tgit reset --hard &&\n \tgit checkout B^0 &&\n \n@@ -351,7 +351,7 @@ test_expect_failure '--index-only octopus, non-bare' '\n \ttest ! -f d\n '\n \n-test_expect_failure '--index-only octopus, bare' '\n+test_expect_success '--index-only octopus, bare' '\n \trm -rf bare.clone &&\n \tgit clone --bare . bare.clone &&\n \t(cd bare.clone &&\n-- \n2.8.0.18.gc685494\n"},{"id":"282955","messageId":"5707ABBF.2050701@drmicha.warpmail.net","threadId":"41962","inReplyTo":"1460098726-5958-1-git-send-email-newren@gmail.com","subject":"Re: [RFC/PATCH 00/18] Add --index-only option to git merge","fromName":"Michael J Gruber","fromEmail":"git@drmicha.warpmail.net","sentAt":"2016-04-08T13:01:51Z","receivedAt":"2016-04-08T13:01:51Z","isPatch":true,"sender":{"key":"git@grubix.eu","avatar":"https://avatars.githubusercontent.com/u/233215?v=4"},"body":"Elijah Newren venit, vidit, dixit 08.04.2016 08:58:\n> This patch series adds an --index-only flag to git merge, the idea\n> being to allow a merge to be performed entirely in the index without\n> touching (or even needing) a working tree.\n> \n> The core fix, to merge-recursive, was actually quite easy.  The\n> recursive merge logic already had the ability to ignore the working\n> directory and operate entirely on the index -- it needed to do this\n> when creating a virtual merge base, i.e. when o->call_depth > 0.  It's\n> just that o->call_depth was also used for other purposes, so I just\n> needed to introduce a new flag to disambiguate and switch all the\n> necessary index-only-related call sites to use it.  It actually seems\n> to make the code slightly easier to read too, which is a nice bonus.\n> That was all done in patch 12 of this series.\n> \n> Adding all the necessary testcases and switching over the other merge\n> strategies turned out to be the harder part...and still has a problem,\n> as I'll mention below.\n\nI haven't looked at your series thoroughly but immediately had to think\nof 'tr/remerge-diff' (on 'pu'), see\nhttp://permalink.gmane.org/gmane.comp.version-control.git/256591\n\nThere, Thomas used index-only merge to reproduce an automatic merge as\nthe base for a useful \"remerge-diff\".\n\nI've been rebasing (and using) that series on 'next' for a while now\nwithout any problems; some reasons kept it from being merged on next,\nsee the thread.\n\nSo, it would be interesting whether you solve the same problem\ndifferently, or face the same problems ;)\n\nMichael\n"},{"id":"282970","messageId":"xmqqlh4ovuqv.fsf@gitster.mtv.corp.google.com","threadId":"41962","inReplyTo":"1460098726-5958-1-git-send-email-newren@gmail.com","subject":"Re: [RFC/PATCH 00/18] Add --index-only option to git merge","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-04-08T18:08:24Z","receivedAt":"2016-04-08T18:08:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n> This patch series adds an --index-only flag to git merge, the idea\n> being to allow a merge to be performed entirely in the index without\n> touching (or even needing) a working tree.\n\nThe goal is stated rather vaguely--when you have a working tree and\nperform this \"in index\" merge, you would obviously update the index\nwith the merge result and write it out as a tree to record the merge,\nand update HEAD to point at the new commit, but \"without touching\"\nwould imply that you would not be checking out the result to the\nworking tree.  That sounds crazy and probably you didn't mean it\nthat way.\n\nOne worry immediately comes to mind is what should happen when\nconflicts occur.  A knee-jerk reaction only after reading the above\nexplanation is to error out; there could be other sensible\nalternatives, but I do not think of any offhand.\n\nAnother thing that comes to mind is if that should be an option to\nthe end-user facing \"git merge\" command that can be run in a\nrepository _with_ a working tree.  When used in a repository with a\nworking tree, I am not sure what the next step the end user wants to\nmake after the \"only in index\" merge succeeds.  The index has been\nupdated with the merge result at that point and I am guessing that\nthe merge would also have created a new commit and updated the HEAD\nto point at it.  The contents of the working tree for paths that\nwere not changed when the user initiated the merge can be safely\nchecked out, but what should happen to the ones that were dirty wrt\nthe index (I am again guessing that you do not allow a \"git merge\nonly in index\" if the index already has changes wrt the HEAD)?\n\nI can understand if the option only worked in a bare repository,\nthough.\n\n> The core fix, to merge-recursive, was actually quite easy.  The\n> recursive merge logic already had the ability to ignore the working\n> directory and operate entirely on the index -- it needed to do this\n> when creating a virtual merge base, i.e. when o->call_depth > 0.\n\nYou need to be careful here, though.  The creation of a virtual\nparent accepts (actually it wants to have) conflicted blobs as if\nthat is a valid merge result--\"git merge but only in index\" probably\ndoes not want that behaviour.\n\n> A brief-ish summary of the series:\n>\n> * Patches 1 and 2 are unrelated cleanups, which could be submitted\n>   independently.  However, submitting them as separate patches would\n>   result in a minor conflict, so I'm just including them together.\n>\n> * Patches 3 and 4 add some testcases and a fix for a separate issue\n>   found while testing this series which could be split off and\n>   submitted separately, but fixing this problem and enforcing the\n>   starting state I expected permitted me to reduce the range of\n>   testcases I needed to consider for the --index-only merges.  So I\n>   thought it made sense to include in this series.\n\nI'd prefer these as a separate series if they are truly unrelated\ncleanups, so that we can quickly review and have them graduated\nwithout waiting for the remainder.  You can still submit the main\nseries with a comment that says it applies on that separate clean-up\nseries and the right things should happen ;-)\n\n>   In particular, I was worried about how git merge behaved when the\n>   index differed from HEAD prior to the merge starting.  I discovered\n>   that it wasn't just allowed for fast-forward merges (where behavior\n>   is well-defined) but that it was also (accidentally I'm assuming)\n>   allowed for octopus merges with weird/surprising behavior.\n\nI briefly looked at these tests and I agree with your expectation,\ni.e. we want to allow a merge as long as there is no conflicts with\nthe change already in the index (i.e. the changed paths have the\nsame contents in ORIG_HEAD and HEAD).\n\n> Some things I am concerned about:\n>\n> * The option name (--index-only) may be slightly misleading since the\n>   index isn't the only thing modified within the git directory, other\n>   normal things under there are still modified (e.g. writing MERGE_*\n>   files, sticking blobs/trees/commits in objects/*, and updating refs\n>   and reflogs).  I personally prefer this name and think the confusion\n>   would be minor, but I'm a bit unsure and wanted some other opinions\n>   on this.\n\nWhen you say \"index only\", I'd say people would understand that to\nbe saying \"as opposed to using and updating both the index and the\nworking tree\", so I do not think there is no confusion.\n\nAn established convention to spell \"index only\" found in \"apply\" and\n\"grep\" is to say \"--cached\", though (cf. \"git help cli\").\n\n> * I didn't add this option to the separate git-merge-recursive\n>   executable and make the worktree-optional modification to the git\n>   subcommands merge-recursive, merge-recursive-ours,\n>   merge-recursive-theirs, and merge-subtree in git.c.  Should I, or\n>   are these separate binaries and subcommands just present for\n>   historical backward compatibility reasons with people expected to\n>   call e.g. \"git merge -s recursive\" these days?\n\nIf you have to assume, assume that there are people who use these\nprograms in their scripts and workflows, because it is a relatively\nnew development to make \"git merge\" directly calling into the\nrecursive machinery bypassing these commands.\n\n> * Expanding on the last item, git-merge-one-file.sh is of particular\n>   concern; it seemed to strongly assume exactly seven arguments and\n>   that the position of each mattered.  I didn't want to break that, so\n>   I added --index-only as an optional 8th argument, even though it\n>   seems slightly odd force an option argument to always come after\n>   positional ones (and it made the changes to merge_entry in\n>   merge-index.c slightly easier to implement).  Does that seem okay?\n\nI have a suspicion that this would become moot, as the conclusion\nmay become that \"git merge\" that works only in index does not make\nsense unless you are in a bare repository---in which case, these\nexternal merge drivers has to be given a temporary working tree\nanyway, and they can keep writing their result out to what they\nconsider is the working tree (i.e. via GIT_WORK_TREE or something).\nYou read the result of them from that temporary working tree into\nthe index before cleaning the temporary working tree.  That way,\nyou do not have to touch them at all, no?  In fact, the temporary\nworking tree trick may be applicable even when you are working in a\nrepository with a tree working tree.\n"},{"id":"282980","messageId":"xmqq4mbbx5go.fsf@gitster.mtv.corp.google.com","threadId":"41962","inReplyTo":"1460098726-5958-5-git-send-email-newren@gmail.com","subject":"Re: [RFC/PATCH 04/18] merge-octopus: Abort if index not clean","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-04-08T19:31:35Z","receivedAt":"2016-04-08T19:31:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n> Signed-off-by: Elijah Newren <newren@gmail.com>\n> ---\n>  git-merge-octopus.sh                     |  5 +++++\n>  t/t6044-merge-unrelated-index-changes.sh | 21 ++++++---------------\n>  2 files changed, 11 insertions(+), 15 deletions(-)\n>\n> diff --git a/git-merge-octopus.sh b/git-merge-octopus.sh\n> index 8643f74..a1d7702 100755\n> --- a/git-merge-octopus.sh\n> +++ b/git-merge-octopus.sh\n> @@ -44,6 +44,11 @@ esac\n>  # MRC is the current \"merge reference commit\"\n>  # MRT is the current \"merge result tree\"\n>  \n> +if ! git diff-index --quiet --cached HEAD --; then\n\nStyle.\n\n> +    echo \"Error: Your local changes to the following files need to be resolved before merge\"\n> +    git diff-index --cached --name-only HEAD -- | sed -e 's/^/    /'\n> +    exit 2\n> +fi\n\nUnless they are existing conflicts, the word \"resolve\" is misleading.\n\nErroring it out may not strictly be necessary (i.e. as Octopus is\nmeant to be used only when changes from the branches do not overlap,\nit is likely that all of them are small simple changes that do not\ntouch any path that you currently have local modifications in the\nindex), but I do not think it is worth the effort to identify safe\ncases that can continue, so I agree that this change to abort the\nmerge is a sensible one.\n\n\n\n>  MRC=$(git rev-parse --verify -q $head)\n>  MRT=$(git write-tree)\n>  NON_FF_MERGE=0\n> diff --git a/t/t6044-merge-unrelated-index-changes.sh b/t/t6044-merge-unrelated-index-changes.sh\n> index 726c898..4c1dc5a 100755\n> --- a/t/t6044-merge-unrelated-index-changes.sh\n> +++ b/t/t6044-merge-unrelated-index-changes.sh\n> @@ -105,43 +105,34 @@ test_expect_success 'recursive' '\n>  \ttest_must_fail git merge -s recursive C^0\n>  '\n>  \n> -test_expect_failure 'octopus, unrelated file touched' '\n> +test_expect_success 'octopus, unrelated file touched' '\n>  \tgit reset --hard &&\n>  \tgit checkout B^0 &&\n>  \n>  \ttouch random_file && git add random_file &&\n>  \n>  \techo \"I think the next line should fail, but maybe it was intended...\" &&\n> -\ttest_might_fail git merge C^0 D^0 &&\n> -\n> -\techo \"No matter what, random_file should NOT be part of HEAD\" &&\n> -\ttest_must_fail git rev-parse HEAD:random_file\n> +\ttest_must_fail git merge C^0 D^0\n>  '\n>  \n> -test_expect_failure 'octopus, related file removed' '\n> +test_expect_success 'octopus, related file removed' '\n>  \tgit reset --hard &&\n>  \tgit checkout B^0 &&\n>  \n>  \tgit rm b &&\n>  \n>  \techo \"I think the next line should fail, but maybe it was intended...\" &&\n> -\ttest_might_fail git merge C^0 D^0 &&\n> -\n> -\techo \"No matter what, b should still be in HEAD\" &&\n> -\tgit cat-file -p HEAD:b | grep b$\n> +\ttest_must_fail git merge C^0 D^0\n>  '\n>  \n> -test_expect_failure 'octopus, related file modified' '\n> +test_expect_success 'octopus, related file modified' '\n>  \tgit reset --hard &&\n>  \tgit checkout B^0 &&\n>  \n>  \techo 12 >>a && git add a &&\n>  \n>  \techo \"I think the next line should fail, but maybe it was intended...\" &&\n> -\ttest_might_fail git merge C^0 D^0 &&\n> -\n> -\techo \"No matter what, 12 should NOT be in the copy of a from HEAD\" &&\n> -\tgit cat-file -p HEAD:a | test_must_fail grep 12\n> +\ttest_must_fail git merge C^0 D^0\n>  '\n>  \n>  test_expect_success 'ours' '\n"},{"id":"282981","messageId":"xmqqzit3vqma.fsf@gitster.mtv.corp.google.com","threadId":"41962","inReplyTo":"1460098726-5958-6-git-send-email-newren@gmail.com","subject":"Re: [RFC/PATCH 05/18] Add testcase for --index-only merges needing the recursive strategy","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-04-08T19:37:33Z","receivedAt":"2016-04-08T19:37:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n> +test_expect_failure '--index-only with rename/modify works in non-bare-clone' '\n> +\tgit checkout B^0 &&\n> +\n> +\tgit merge --index-only -s recursive C^0 &&\n> +\n> +\techo \"Making sure the working copy was not updated\" &&\n> +\ttest ! -f b &&\n> +\ttest -f a &&\n> +\ttest $(git rev-parse B:a) = $(git hash-object a) &&\n> +\n> +\techo \"Making sure the index was updated\" &&\n> +\ttest 1 -eq $(git ls-files -s | wc -l) &&\n> +\ttest $(git rev-parse B:a) = $(git rev-parse :b)\n\nThe most crucial test that is missing (hence prevents reviewers from\njudging if --index-only is a good idea at all) is the test on HEAD.\nDoes it record a merge between B and C and move HEAD there, i.e.\n\n\ttest $(git rev-parse HEAD^1) = $(git rev-parse B) &&\n\ttest $(git rev-parse HEAD^2) = $(git rev-parse C)\n\nor does it make a merge but does not advance HEAD, i.e.\n\n\ttest $(git rev-parse HEAD) = $(git rev-parse B)\n\nI fear that it may give a great headache to end users if you move\nHEAD in a repository with a working tree to point at the merge\nresult--how do they reconcile the difference between the working\ntree (which was based on B) and the index and HEAD (which is now\nbased on the result of the merge)?  The next \"git commit -a\" would\nappear that it would revert the changes brought in by this merge,\nwouldn't it?\n"},{"id":"282989","messageId":"xmqqtwjbvox1.fsf@gitster.mtv.corp.google.com","threadId":"41962","inReplyTo":"xmqqzit3vqma.fsf@gitster.mtv.corp.google.com","subject":"Re: [RFC/PATCH 05/18] Add testcase for --index-only merges needing the recursive strategy","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-04-08T20:14:18Z","receivedAt":"2016-04-08T20:14:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Elijah Newren <newren@gmail.com> writes:\n>\n>> +test_expect_failure '--index-only with rename/modify works in non-bare-clone' '\n>> +\tgit checkout B^0 &&\n>> +\n>> +\tgit merge --index-only -s recursive C^0 &&\n>> +\n>> +\techo \"Making sure the working copy was not updated\" &&\n>> +\ttest ! -f b &&\n>> +\ttest -f a &&\n>> +\ttest $(git rev-parse B:a) = $(git hash-object a) &&\n>> +\n>> +\techo \"Making sure the index was updated\" &&\n>> +\ttest 1 -eq $(git ls-files -s | wc -l) &&\n>> +\ttest $(git rev-parse B:a) = $(git rev-parse :b)\n>\n> The most crucial test that is missing (hence prevents reviewers from\n> judging if --index-only is a good idea at all) is the test on HEAD.\n> Does it record a merge between B and C and move HEAD there, i.e.\n>\n> \ttest $(git rev-parse HEAD^1) = $(git rev-parse B) &&\n> \ttest $(git rev-parse HEAD^2) = $(git rev-parse C)\n>\n> or does it make a merge but does not advance HEAD, i.e.\n>\n> \ttest $(git rev-parse HEAD) = $(git rev-parse B)\n>\n> I fear that it may give a great headache to end users if you move\n> HEAD in a repository with a working tree to point at the merge\n> result--how do they reconcile the difference between the working\n> tree (which was based on B) and the index and HEAD (which is now\n> based on the result of the merge)?  The next \"git commit -a\" would\n> appear that it would revert the changes brought in by this merge,\n> wouldn't it?\n\nAnd if the expectation that is not spelled out by this new test is\nthat the merge does not advance HEAD, the command does not make much\nsense either, because there is no way for you to find out the merge\nresult.\n\nLet's step back a bit.  This may or may not be what you are aiming\nat, but I think I can buy a series whose title is \"merge without\nworking tree\" if the big-picture objective of the topic were to\nsupport this workflow:\n\n    0. I am working on my own branch that is not 'master'.\n\n    1. I see somebody else worked on and finished a good topic\n       'en/topic'.  My work is not yet in a good shape, and I do not\n       want to switch to 'master' only to merge 'en/topic' to\n       'master', but 'en/topic' is so good and my urge to merge it\n       to 'master' is strong.\n\n    2. Hence I want a way to merge 'en/topic' to 'master' and\n       advance the tip of 'master', without using my working tree\n       (or the index or my HEAD for that matter), without disrupting\n       my current state.\n\n    3. For simplicity, I can live with it if the early version of\n       that \"merge en/topic to master\" operation is incapable of\n       handling conflicts.  Then I'd say \"huh, I'd need to manually\n       inspect the merge result so I'll do that later\" and continue\n       what I was doing, or I'd say \"OK, let's really have a look\",\n       save what I was doing in a temporary commit and switch to\n       'master' to do the usual \"git merge en/topic\".\n\n    4. But it would be even better if I could give a temporary\n       directory to it as a scratch-pad area, and if I was asked to\n       help it resolving conflicts by editing conflicted files in\n       the scratch-pad area.\n\nIn any case, what is in my current index (or HEAD) is no use for\nmerging en/topic into master, so the command should not care or\ntouch not just what is in my working tree (which gives the operation\nthe same requirement as your series, i.e. \"do not touch or require a\nworking tree\") but also what is in my index or where my HEAD is.\n\nThe low-level ingredients that support \"git merge\", namely, \"git\nread-tree -m COMMON OURS THEIRS\", actually has a support to make\nthis kind of \"merge without fully populated working tree\" possible,\nby considering the lack of working tree file an equivalent to having\nan unmodified working tree file (I suspect that merge-recursive is\nprobably broken with this regard, as it was mostly done without\nknowing that we anticipated such a need in early life of Git long\nbefore merge-recursive was invented).  We can give an empty\n\"scratch-pad area\" as a temporary working tree, create a temporary\nindex out of 'master' (in the above example), perform the usual\nthree-way merge with\n    \n    GIT_DIR=... point at the real thing ...\n    GIT_INDEX_FILE=... point at a temporary file ...\n    export GIT_DIR GIT_INDEX_FILE\n    rm -fr /var/tmp/scratchpad && mkdir /var/tmp/scratchpad\n    cd /var/tmp/scratchpad\n    base=$(git merge-base master en/topic)\n    git read-tree -m $base master en/topic\n\nand run \"git merge -s resolve\" to drive \"git merge-one-file\".  This\nwould populate /var/tmp/scratchpad with files that are involved in\nthe merge, without wasting diskspace for files in 'master' that does\nnot change, and you can edit them, \"git add\" these paths, and then\ndo a \"git write-tree\" to create the tree that represents the merge\nresult.  You obviously need to avoid touching HEAD when recording\nthat tree as a merge commit and advance the tip of 'master' (as HEAD\nis still on my own unrelated branch), but that part is trivial\n(i.e. we have written \"git commit\" at least twice).\n\nOf course, if you are not going to support merges that need manual\nconflict resolution, you do not need the scratchpad and the end user\nexperience would be simpler (i.e. you do not have to think about how\nthey resolve, record the resolution, etc.)  And your \"merge only in\nindex\" topic would fit well in that picture.\n\nBut then, using the current index and the HEAD as the place to merge\ninto is still wrong.  I think this is better done as a separate\ncommand, not \"git merge\" but perhaps something like\n\n    $ git merge-to en/topic master\n\nor something.  \"git merge --into=master en/topic\" is also a\npossibility.\n\nBut as I said in the early part of this message, this may or may not\nbe what you are aiming at.  The above is the most sensible tangent\nthat I can think of that may be related to \"merge only in index\".\n"},{"id":"282998","messageId":"xmqqzit3u3x5.fsf@gitster.mtv.corp.google.com","threadId":"41962","inReplyTo":"1460098726-5958-12-git-send-email-newren@gmail.com","subject":"Re: [RFC/PATCH 11/18] merge: Add a new --index-only option, not yet implemented","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-04-08T22:33:10Z","receivedAt":"2016-04-08T22:33:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n> +--index-only::\n> +\tPerform merge on index only, leaving working tree alone.  Most\n> +\tusers do NOT want to use this flag, as it will leave the\n> +\tworking tree and the index completely out of sync, which is\n> +\tvery likely to confuse users and prevent a subsequent 'git\n> +\tmerge --abort' from working.\n\nThis whole paragraph is a strong sign that this feature as-is is not\na good addition.\n\nOn the other hand ...\n\n> +\tIt is intended for script\n> +\twriters to have a way to easily check whether a merge would\n> +\tsucceed and which files would conflict, typically from bare\n> +\tclones.\n\n... this may be a good goal to aim for, but I think you are\nunderstating.  You seem to be wanting a lot more than \"easily check\nwhether...\"; isn't this more like \"It is for scripts to create a\nmerge and advance the branch when no need for manual conflict\nresolution in a bare repository, and to learn which paths would\nrequire manual conflicts when it fails\", no?\n\nI'd think either (1) limit this to bare repository only, or (2) do\nthe \"git merge --into master en/topic\" I outlined in the other\nmessage, (or both) would be a sensible alternative for a feature\nwhose description has to begin with \"Most users do not want this\".\n"},{"id":"282999","messageId":"xmqqvb3ru13r.fsf@gitster.mtv.corp.google.com","threadId":"41962","inReplyTo":"1460098726-5958-2-git-send-email-newren@gmail.com","subject":"Re: [RFC/PATCH 01/18] Remove duplicate code","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-04-08T23:34:00Z","receivedAt":"2016-04-08T23:34:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n> Subject: Re: [RFC/PATCH 01/18] Remove duplicate code\n\nSubject: [RFC/PATCH 01/18] merge-recursive: remove duplicate code\n\n> In commit 51931bf (merge-recursive: Improve handling of rename target vs.\n> directory addition, 2011-08-11) I apparently added two lines of code that\n> were immediately duplicated a few lines later.  No idea why, other than\n> it seems pretty clear this was a mistake: there is no need to remove the\n> same file twice; removing it once is sufficient...especially since the\n> intervening line was working with a different file entirely.\n\n\nInteresting.\n\nI briefly wondered if the update_files() in the middle may be\nprevented by D/F conflict if you didn't remove the path beforehand,\nbut path and new_path are both in the same directory, so that would\nnot be the reason.  Checking out 51931bf and running two tests (6022\nand 6042) that were modified to protect the fix after applying this\npartial revert does not seem to break, either, so...\n\nReviewed-by: Junio C Hamano <gitster@pobox.com>\n\n>\n> Signed-off-by: Elijah Newren <newren@gmail.com>\n> ---\n>  merge-recursive.c | 2 --\n>  1 file changed, 2 deletions(-)\n>\n> diff --git a/merge-recursive.c b/merge-recursive.c\n> index b880ae5..d4292de 100644\n> --- a/merge-recursive.c\n> +++ b/merge-recursive.c\n> @@ -1773,8 +1773,6 @@ static int process_entry(struct merge_options *o,\n>  \t\t\toutput(o, 1, _(\"CONFLICT (%s): There is a directory with name %s in %s. \"\n>  \t\t\t       \"Adding %s as %s\"),\n>  \t\t\t       conf, path, other_branch, path, new_path);\n> -\t\t\tif (o->call_depth)\n> -\t\t\t\tremove_file_from_cache(path);\n>  \t\t\tupdate_file(o, 0, sha, mode, new_path);\n>  \t\t\tif (o->call_depth)\n>  \t\t\t\tremove_file_from_cache(path);\n"},{"id":"283000","messageId":"CABPp-BGpBDsn_ZZh8iot7qbgpWn0cUcdWTypRqKOKFMm9Y-E4w@mail.gmail.com","threadId":"41962","inReplyTo":"xmqqlh4ovuqv.fsf@gitster.mtv.corp.google.com","subject":"Re: [RFC/PATCH 00/18] Add --index-only option to git merge","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2016-04-09T02:35:07Z","receivedAt":"2016-04-09T02:35:07Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"Hi,\n\nOn Fri, Apr 8, 2016 at 11:08 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Elijah Newren <newren@gmail.com> writes:\n>\n> The goal is stated rather vaguely--when you have a working tree and\n> perform this \"in index\" merge, you would obviously update the index\n> with the merge result and ...\n\nYes, I think you hit the nail on the head with your suggestions\nelsewhere to (1) limit this to bare repository only, and/or (2) do the\n\"git merge --into master en/topic\"\n\nI'll fix this.\n\n>> The core fix, to merge-recursive, was actually quite easy.  The\n>> recursive merge logic already had the ability to ignore the working\n>> directory and operate entirely on the index -- it needed to do this\n>> when creating a virtual merge base, i.e. when o->call_depth > 0.\n>\n> You need to be careful here, though.  The creation of a virtual\n> parent accepts (actually it wants to have) conflicted blobs as if\n> that is a valid merge result--\"git merge but only in index\" probably\n> does not want that behaviour.\n\nYes, precisely.  :-)\n\nA close look at the merge-recursive code showed that the code to\nhandle the index-only behavior was already nicely separated from the\nother things triggered by o->call_depth (e.g. the conflicted blobs\ncase you mention above, among others), making it really easy to modify\nit so we could get the index-only behavior without the other stuff.\n\n> I'd prefer these as a separate series if they are truly unrelated\n> cleanups, so that we can quickly review and have them graduated\n> without waiting for the remainder.  You can still submit the main\n> series with a comment that says it applies on that separate clean-up\n> series and the right things should happen ;-)\n\nWill do.\n\n> When you say \"index only\", I'd say people would understand that to\n> be saying \"as opposed to using and updating both the index and the\n> working tree\", so I do not think there is no confusion.\n>\n> An established convention to spell \"index only\" found in \"apply\" and\n> \"grep\" is to say \"--cached\", though (cf. \"git help cli\").\n\nI'm assuming your double negative was unintentional, i.e. that you do\nnot think there is any confusion.  Let me know if I misread.\n\nThe pointer is helpful, but I struggle a bit with the name '--cached'.\nThe past tense in that word suggests to me that it should be a\nread-only operation on the cache (which works for grep), rather than a\nwrite operation (such as for merge or apply).  It could also be\nmisconstrued in merge's case to think that the index is one of the\nthings being merged (rather than erroring out if the index doesn't\nmatch HEAD).  I know apply already uses --cached, but if others don't\nmind, I personally would rather not use it here.  Is this something\nyou feel strongly about, or are you okay with --index-only?\n\n> If you have to assume, assume that there are people who use these\n> programs in their scripts and workflows, because it is a relatively\n> new development to make \"git merge\" directly calling into the\n> recursive machinery bypassing these commands.\n\nSo, my question wasn't so much about whether the git-merge-* programs\nare used, as to whether they should support all the same options that\ne.g. \"git merge -s recursive\" does.  I'm not sure if having the old\nmerge-* programs support fewer options is considered good (\"we want to\ntoss them eventually\", or \"they are less tested these days so we want\nto take less risk with modifying them\") or bad (\"we don't want there\nto be any difference in ability or behavior\").\n\nIt should be easy to add the --index-only option to each of these, I'm\njust unsure whether it matters or if it's wanted.\n\n> I have a suspicion that this would become moot, as the conclusion\n> may become that \"git merge\" that works only in index does not make\n> sense unless you are in a bare repository---in which case, these\n> external merge drivers has to be given a temporary working tree\n> anyway, and they can keep writing their result out to what they\n> consider is the working tree (i.e. via GIT_WORK_TREE or something).\n> You read the result of them from that temporary working tree into\n> the index before cleaning the temporary working tree.  That way,\n> you do not have to touch them at all, no?  In fact, the temporary\n> working tree trick may be applicable even when you are working in a\n> repository with a tree working tree.\n\nI'm a little confused; you seem to be suggesting git-merge-one-file\nwill always need to have a working tree of some sort, though I don't\nsee why.\n\ngit-merge-one-file doesn't currently read from the working tree,\nexcept to check for untracked files in the way; it instead gets file\ncontents from git unpack-file, which pulls it from the object store.\ngit-merge-one-file currently records the merge resolution, if clean,\nin both the index and the working copy, so my modifications are about\nmaking it able to write to only the index. That's the only\nmodification I think it needs to avoid requiring a working tree at\nall.\n\n(git-merge-one-file does create some temporary files in the current\nworking directory via git unpack-file, but it doesn't need a full\nworking tree for that.  I should also make it a little cleaner by\nhaving it not check for the presence of untracked-and-in-the-way files\nin the --index-only case.)\n"},{"id":"283001","messageId":"CABPp-BG49Gr3Kf8Q3E6Vc=GF9MG+m10HkhkwbaOBfzs1cFcgVw@mail.gmail.com","threadId":"41962","inReplyTo":"5707ABBF.2050701@drmicha.warpmail.net","subject":"Re: [RFC/PATCH 00/18] Add --index-only option to git merge","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2016-04-09T03:09:55Z","receivedAt":"2016-04-09T03:09:55Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Fri, Apr 8, 2016 at 6:01 AM, Michael J Gruber\n<git@drmicha.warpmail.net> wrote:\n> I haven't looked at your series thoroughly but immediately had to think\n> of 'tr/remerge-diff' (on 'pu'), see\n> http://permalink.gmane.org/gmane.comp.version-control.git/256591\n>\n> There, Thomas used index-only merge to reproduce an automatic merge as\n> the base for a useful \"remerge-diff\".\n>\n> I've been rebasing (and using) that series on 'next' for a while now\n> without any problems; some reasons kept it from being merged on next,\n> see the thread.\n>\n> So, it would be interesting whether you solve the same problem\n> differently, or face the same problems ;)\n\nThanks for the link.  Looks like a very interesting series, even if\nwe're solving slightly different problems (I don't want conflicts\nauto-resolved; I want to be able to look at what conflicted and why\nwith commands like 'git ls-files -u' afterward.)\n\nBut the problem he's trying to solve is interesting to me too.  We\nhave one patch from each of our series that does overlap, the\nindex-only modification to merge-recursive, though implemented\nslightly differently.  I think mine's a little clearer, and I have a\nhunch that he might be able to use the idea in mine to dramatically\nsimplify some of the other stuff he's doing.  In particular,\nmerge-recursive already has code to auto resolve conflicts that he\ncould just re-use instead of reimplementing, which I believe would\ndramatically simplify his patch #8.\n\nI didn't read all his code super closely, so I might be missing\nsomething important, but I got the feeling that he didn't need\ndifferent behavior than what merge-recursive already implements for\nvirtual merge bases, and that even he wasn't certain whether he had\nhandled all cases (e.g. not only conflict markers and modify/delete,\nbut also rename/delete, rename/add, rename/rename (both 1to2 and\n2to1), D/F conflicts, and perhaps others I'm not thinking of at the\nmoment).  We already have well reviewed and tested code for all those\ncases; it's just a subset of the things triggered by o->call_depth for\nvirtual merges bases.  Also, like the index-only stuff triggered by\no->call_depth, the auto-resolve-conflict stuff is pretty well\nseparated so it should be easy to add a flag to trigger just this\nportion without getting all the other stuff that o->call_depth\nnormally does.\n\nAs far as I could tell, his series stalled out both because of\nconcerns surrounding whether all the automatic-conflict-resolutions\nwere correct, and because it made git log no longer be a read-only\noperation.  Neither of those concerns are applicable to my patchset; I\ninvented totally new problems and concerns instead.  :-)  I don't have\na good solution to the second concern for his patchset, but I think\nthere's probably an easy solution to the first.  Once I get my\npatchset cleaned up, I may take a look at reviving his (if he doesn't\nbeat me to it.)\n"},{"id":"283003","messageId":"xmqqr3eftmpy.fsf@gitster.mtv.corp.google.com","threadId":"41962","inReplyTo":"CABPp-BGpBDsn_ZZh8iot7qbgpWn0cUcdWTypRqKOKFMm9Y-E4w@mail.gmail.com","subject":"Re: [RFC/PATCH 00/18] Add --index-only option to git merge","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-04-09T04:44:41Z","receivedAt":"2016-04-09T04:44:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Elijah Newren <newren@gmail.com> writes:\n\n> On Fri, Apr 8, 2016 at 11:08 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Elijah Newren <newren@gmail.com> writes:\n>>\n>> The goal is stated rather vaguely--when you have a working tree and\n>> perform this \"in index\" merge, you would obviously update the index\n>> with the merge result and ...\n>\n> Yes, I think you hit the nail on the head with your suggestions\n> elsewhere to (1) limit this to bare repository only, and/or (2) do the\n> \"git merge --into master en/topic\"\n>\n> I'll fix this.\n> ...\n> The pointer is helpful, but I struggle a bit with the name '--cached'.\n> The past tense in that word suggests to me that it should be a\n> read-only operation on the cache (which works for grep), rather than a\n> write operation (such as for merge or apply).  It could also be\n> misconstrued in merge's case to think that the index is one of the\n> things being merged (rather than erroring out if the index doesn't\n> match HEAD).  I know apply already uses --cached, but if others don't\n> mind, I personally would rather not use it here.  Is this something\n> you feel strongly about, or are you okay with --index-only?\n\n\"cached\" is not in past tense, but is used an adjective.  You can\nupdate what is in the index and that is to update the \"cached\"\ninformation.\n\nBut I do not think we need to discuss this, because we do not need\nsuch an option, as I understand that your updated direction is to do\none or both of (1) do the \"merge and update HEAD only when no manual\nconflict resolution is needed\" thing in a bare repository, (2) \"git\nmerge --into=master en/topic\" does the \"merge master and en/topic\nand update master only when no manual conflict resolution is needed\nand do so without touching HEAD, index or the working tree\".\n\nBecause the current \"git merge\" refuses to work without the working\ntree, (1) can be done without adding any new option---if you are run\nin a bare repository, by definition, you are being asked to do that\nnew thing, and because you will not do the new thing in a repository\nwith a working tree, there is no need for an option to tell the\ncommand, \"no, don't do the usual thing but do this new thing\".  And\nobviously (2) already has a new option --into and no other new option\nis needed to tell the command that it needs to do that new thing.\n\n>> I have a suspicion that this would become moot, as the conclusion\n>> may become that \"git merge\" that works only in index does not make\n>> sense unless you are in a bare repository---in which case, these\n>> external merge drivers has to be given a temporary working tree\n>> anyway, and they can keep writing their result out to what they\n>> consider is the working tree (i.e. via GIT_WORK_TREE or something).\n>> You read the result of them from that temporary working tree into\n>> the index before cleaning the temporary working tree.  That way,\n>> you do not have to touch them at all, no?  In fact, the temporary\n>> working tree trick may be applicable even when you are working in a\n>> repository with a tree working tree.\n>\n> I'm a little confused; you seem to be suggesting git-merge-one-file\n> will always need to have a working tree of some sort, though I don't\n> see why.\n\nI was loosely referring to the working tree, which includes things\nlike having a place you can safely check out temporary files as if\nthey were one of the working tree files, i.e. the arrangement in\nwhich \"merge-one-file\" is designed to operate.  Even if they are\n\"tmpname\" generated files, you do not want to clobber your $GIT_DIR\nwith these random files when driving these blob level merge drivers\nin a bare repository.\n"},{"id":"283062","messageId":"CABPp-BHrsRXu8_9oj=srATO51yaDBVUMUUGKqgL16_b6HN+-Yw@mail.gmail.com","threadId":"41962","inReplyTo":"1460098726-5958-1-git-send-email-newren@gmail.com","subject":"Re: [RFC/PATCH 00/18] Add --index-only option to git merge","fromName":"Elijah Newren","fromEmail":"newren@gmail.com","sentAt":"2016-04-10T05:33:24Z","receivedAt":"2016-04-10T05:33:24Z","isPatch":true,"sender":{"key":"newren@gmail.com","avatar":"https://avatars.githubusercontent.com/u/5455730?v=4"},"body":"On Thu, Apr 7, 2016 at 11:58 PM, Elijah Newren <newren@gmail.com> wrote:\n>   Luckily, I figured out that bug.  So, that leaves just one case left\n>   that I can't seem to figure out: read_tree_trivial.  So much better,\n>   right?  Even it's name is sitting there, mocking me.  \"Ha ha, I'm\n>   read_tree_*trivial* and you can't figure me out.\"  read_tree_trivial\n>   is a jerk.\n\nIt turns out this wasn't a bug in my index-only handling of the\ntrivial merge case; it was a pre-existing bug in git handling of\ntrivial merges that probably no one else had ever triggered before.\nI'm submitting a fix with my other miscellaneous merge fixups...\n"}]}