git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: Elijah Newren <newren@gmail.com>
To: git@vger.kernel.org
Cc: gitster@pobox.com, pclouds@gmail.com, Elijah Newren <newren@gmail.com>
Subject: [PATCH v2 9/9] merge: fix misleading pre-merge check documentation
Date: Sat, 30 Jun 2018 18:25:03 -0700	[thread overview]
Message-ID: <20180701012503.14382-10-newren@gmail.com> (raw)
In-Reply-To: <20180701012503.14382-1-newren@gmail.com>

builtin/merge.c contains this important requirement for merge strategies:

    ...the index must be in sync with the head commit.  The strategies are
    responsible to ensure this.

However, Documentation/git-merge.txt says:

    ...[merge will] abort if there are any changes registered in the index
    relative to the `HEAD` commit.  (One exception is when the changed
    index entries are in the state that would result from the merge
    already.)

Interestingly, prior to commit c0be8aa06b85 ("Documentation/git-merge.txt:
Partial rewrite of How Merge Works", 2008-07-19),
Documentation/git-merge.txt said much more:

    ...the index file must match the tree of `HEAD` commit...
    [NOTE]
    This is a bit of a lie.  In certain special cases [explained
    in detail]...
    Otherwise, merge will refuse to do any harm to your repository
    (that is...your working tree...and index are left intact).

So, this suggests that the exceptions existed because there were special
cases where it would case no harm, and potentially be slightly more
convenient for the user.  While the current text in git-merge.txt does
list a condition under which it would be safe to proceed despite the index
not matching HEAD, it does not match what is actually implemented, in
three different ways:

    * The exception is written to describe what unpack-trees allows.  Not
      all merge strategies allow such an exception, though, making this
      description misleading.  'ours' and 'octopus' merges have strictly
      enforced index==HEAD for a while, and the commit previous to this
      one made 'recursive' do so as well.

    * If someone did a three-way content merge on a specific file using
      versions from the relevant commits and staged it prior to running
      merge, then that path would technically satisfy the exception listed
      in git-merge.txt.  unpack-trees.c would still error out on the path,
      though, because it defers the three-way content merge logic to other
      parts of the code (resolve, octopus, or recursive) and has no way of
      checking whether the index entry from before the merge will match
      the end result of the merge.

    * The exception as implemented in unpack-trees actually only checked
      that the index matched the MERGE_HEAD version of the file and that
      HEAD matched the merge base.  Assuming no renames, that would indeed
      provide cases where the index matches the end result we'd get from a
      merge.  But renames means unpack-trees is checking that it instead
      matches something other than what the final result will be, risking
      either erroring out when we shouldn't need to, or not erroring out
      when we should and overwriting the user's staged changes.

In addition to the wording behind this exception being misleading, it is
also somewhat surprising to see how many times the code for the special
cases were wrong or the check to make sure the index matched head was
forgotten altogether:

* Prior to commit ee6566e8d70d ("[PATCH] Rewrite read-tree", 2005-09-05),
  there were many cases where an unclean index entry was allowed (look for
  merged_entry_allow_dirty()); it appears that in those cases, the merge
  would have simply overwritten staged changes with the result of the
  merge.  Thus, the merge result would have been correct, but the user's
  uncommitted changes could be thrown away without warning.

* Prior to commit 160252f81626 ("git-merge-ours: make sure our index
  matches HEAD", 2005-11-03), the 'ours' merge strategy did not check
  whether the index matched HEAD.  If it didn't, the resulting merge
  would include all the staged changes, and thus wasn't really an 'ours'
  strategy.

* Prior to commit 3ec62ad9ffba ("merge-octopus: abort if index does not
  match HEAD", 2016-04-09), 'octopus' merges did not check whether the
  index matched HEAD, also resulting in any staged changes from before
  the commit silently being folded into the resulting merge.  commit
  a6ee883b8eb5 ("t6044: new merge testcases for when index doesn't match
  HEAD", 2016-04-09) was also added at the same time to try to test to
  make sure all strategies did the necessary checking for the requirement
  that the index match HEAD.  Sadly, it didn't catch all the cases, as
  evidenced by the remainder of this list...

* Prior to commit 65170c07d466 ("merge-recursive: avoid incorporating
  uncommitted changes in a merge", 2017-12-21), merge-recursive simply
  relied on unpack_trees() to do the necessary check, but in one special
  case it avoided calling unpack_trees() entirely and accidentally ended
  up silently including any staged changes from before the merge in the
  resulting merge commit.

* The commit immediately before this one in this series noted that the
  exceptions were written in a way that assumed no renames, making it
  unsafe for merge-recursive to use.  merge-recursive was modified to
  use its own check to enforce that index==HEAD.

This history makes it very tempting to go into builtin/merge.c and replace
the comment that strategies must enforce that index matches HEAD with code
that just enforces it.  At this point, that would only affect the
'resolve' strategy; all other strategies have each been modified to
manually enforce it.  (However, note that index==HEAD is not strictly
enforced for fast-forward merges, as those are not considered a merge
strategy and they trigger in builtin/merge.c before the section in the
code where the relevant comment is found.)

But, even if we don't take the step of just fixing these problems by
enforcing index==HEAD for all strategies, we at least need to update this
misleading documentation in git-merge.txt.  For now, just modify the claim
in Documentation/git-merge.txt to fix the error.  The precise details
around combination of merges strategies and special cases probably is not
relevant to most users, so simply state that exceptions may exist but are
narrow and vary depending upon which merge strategy is in use.

Signed-off-by: Elijah Newren <newren@gmail.com>
---
 Documentation/git-merge.txt | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 6a5c00e2c..b050c8c2e 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -122,9 +122,9 @@ merge' may need to update.
 
 To avoid recording unrelated changes in the merge commit,
 'git pull' and 'git merge' will also abort if there are any changes
-registered in the index relative to the `HEAD` commit.  (One
-exception is when the changed index entries are in the state that
-would result from the merge already.)
+registered in the index relative to the `HEAD` commit.  (Special
+narrow exceptions to this rule may exist depending on which merge
+strategy is in use, but generally, the index must match HEAD.)
 
 If all named commits are already ancestors of `HEAD`, 'git merge'
 will exit early with the message "Already up to date."
-- 
2.18.0.137.g2a11d05a6.dirty


      parent reply	other threads:[~2018-07-01  1:25 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-06-03  6:58 [RFC PATCH 0/7] merge requirement: index matches head Elijah Newren
2018-06-03  6:58 ` [RFC PATCH 1/7] t6044: verify that merges expected to abort actually abort Elijah Newren
2018-06-03  6:58 ` [RFC PATCH 2/7] t6044: add a testcase for index matching head, when head doesn't match HEAD Elijah Newren
2018-06-03  6:58 ` [RFC PATCH 3/7] merge-recursive: make sure when we say we abort that we actually abort Elijah Newren
2018-06-03  6:58 ` [RFC PATCH 4/7] merge-recursive: fix assumption that head tree being merged is HEAD Elijah Newren
2018-06-03 13:52   ` Ramsay Jones
2018-06-03 23:37     ` brian m. carlson
2018-06-04  2:26       ` Ramsay Jones
2018-06-04  3:19   ` Junio C Hamano
2018-06-05  7:14     ` Elijah Newren
2018-06-11 16:15       ` Elijah Newren
2018-06-03  6:58 ` [RFC PATCH 5/7] t6044: add more testcases with staged changes before a merge is invoked Elijah Newren
2018-06-03  6:58 ` [RFC PATCH 6/7] merge-recursive: enforce rule that index matches head before merging Elijah Newren
2018-06-03  6:58 ` [RFC PATCH 7/7] merge: fix misleading pre-merge check documentation Elijah Newren
2018-06-07  5:27   ` Elijah Newren
2018-07-01  1:24 ` [PATCH v2 0/9] Fix merge issues with index not matching HEAD Elijah Newren
2018-07-01  1:24   ` [PATCH v2 1/9] read-cache.c: move index_has_changes() from merge.c Elijah Newren
2018-07-01  1:24   ` [PATCH v2 2/9] index_has_changes(): avoid assuming operating on the_index Elijah Newren
2018-07-03 19:44     ` Junio C Hamano
2018-07-01  1:24   ` [PATCH v2 3/9] t6044: verify that merges expected to abort actually abort Elijah Newren
2018-07-01  1:24   ` [PATCH v2 4/9] t6044: add a testcase for index matching head, when head doesn't match HEAD Elijah Newren
2018-07-10 20:39     ` SZEDER Gábor
2018-07-11  3:09       ` [RFC PATCH 2/7] " Elijah Newren
2018-07-01  1:24   ` [PATCH v2 5/9] merge-recursive: make sure when we say we abort that we actually abort Elijah Newren
2018-07-01  1:25   ` [PATCH v2 6/9] merge-recursive: fix assumption that head tree being merged is HEAD Elijah Newren
2018-07-03 19:57     ` Junio C Hamano
2018-07-01  1:25   ` [PATCH v2 7/9] t6044: add more testcases with staged changes before a merge is invoked Elijah Newren
2018-07-01  1:25   ` [PATCH v2 8/9] merge-recursive: enforce rule that index matches head before merging Elijah Newren
2018-07-03 20:05     ` Junio C Hamano
2018-07-01  1:25   ` Elijah Newren [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180701012503.14382-10-newren@gmail.com \
    --to=newren@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=pclouds@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).