git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH v2 6/6] merge: do not exit restore_state() prematurely

From
Elijah Newren <newren@gmail.com>
Date
Jul 21, 2022, 03:27 UTC
Message-ID
<CABPp-BEywagXQtVQVxZgTCaH+TxrA9VBrMr2Lyv_3uADoqYY+Q@mail.gmail.com>
In-Reply-To
<xmqqzgh466ia.fsf@gitster.g>
On Tue, Jul 19, 2022 at 4:13 PM Junio C Hamano <gitster@pobox.com> wrote:
Show 52 quoted lines
>
> "Elijah Newren via GitGitGadget" <gitgitgadget@gmail.com> writes:
>
> > Fix the main problem by making sure that restore_state() only skips the
> > stash application if the stash is null rather than skipping the whole
> > function.
>
> OK.
>
>
> > However, there is a secondary problem -- since merge.c forks
> > subprocesses to do the cleanup, the in-memory index is left out-of-sync.
> > While there was a refresh_cache(REFRESH_QUIET) call that attempted to
> > correct that, that function would not handle cases where the previous
> > merge strategy added conflicted entries.  We need to drop the index and
> > re-read it to handle such cases.
>
> Absolutely right.
>
> > diff --git a/builtin/merge.c b/builtin/merge.c
> > index aaee8f6a553..a21dece1b55 100644
> > --- a/builtin/merge.c
> > +++ b/builtin/merge.c
> > @@ -385,11 +385,11 @@ static void restore_state(const struct object_id *head,
> >  {
> >       const char *args[] = { "stash", "apply", "--index", NULL, NULL };
> >
> > -     if (is_null_oid(stash))
> > -             return;
> > -
> >       reset_hard(head, 1);
> >
> > +     if (is_null_oid(stash))
> > +             goto refresh_cache;
> > +
> >       args[3] = oid_to_hex(stash);
> >
> >       /*
> > @@ -398,7 +398,9 @@ static void restore_state(const struct object_id *head,
> >        */
> >       run_command_v_opt(args, RUN_GIT_CMD);
> >
> > -     refresh_cache(REFRESH_QUIET);
> > +refresh_cache:
> > +     if (discard_cache() < 0 || read_cache() < 0)
> > +             die(_("could not read index"));
>
> Don't we need refresh_cache() after re-reading the on-disk index, or
> do we have nothing to do further after restore_state() returns and
> the stat-info being stale does not matter?  Given that [3/6] exists,
> I suspect that we do want to make sure the in-core index is refreshed
> before we go ahead and run the next merge, no?
I don't think so; the situation for [3/6] is different.  The basic
timeline is as follows:
    1. <User does lots of stuff over weeks and months>
    2. User decides to merge one or more branches
    3. merge does save_state() [i.e. "git stash create"]
    4. The first strategy fails
    5. We restore the state before trying the next strategy

The current code is dealing with step 5. Patch [3/6] was to prevent failures in step 3 from users creating stat-dirty files in step 1. Once step 3 runs, the only way to become stat-dirty again is if the user simultaneously messes with their checkout while the "git merge" command is running. Attempting to preventatively handle users modifying the working tree simultaneously with concurrent git commands like `git merge` seems like a losing proposition to me; it'd be a huge can of worms and have a million holes. I don't think that's worth it.

Show 9 quoted lines
> >  }
> >
> >  /* This is called when no merge was necessary. */
>
> > diff --git a/t/t7607-merge-state.sh b/t/t7607-merge-state.sh
> > new file mode 100755
>
> As long we are adding a brand-new script for new tests, probably we
> should add tests for other steps (like [4/6]) here, perhaps?
Yes.
Show 18 quoted lines
>
> > index 00000000000..655478cd0b3
> > --- /dev/null
> > +++ b/t/t7607-merge-state.sh
> > @@ -0,0 +1,25 @@
> > +#!/bin/sh
> > +
> > +test_description="Test that merge state is as expected after failed merge"
> > +
> > +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
> > +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
> > +. ./test-lib.sh
> > +
> > +test_expect_success 'set up custom strategy' '
> > +     test_commit --no-tag "Initial" base base &&
> > +git show-ref &&
>
> Is this part of the test, or a leftover debugging aid?
Looks like part of a leftover debugging aid; sorry about that.  Will clean up.
Show 16 quoted lines
> > +
> > +     for b in branch1 branch2 branch3
> > +     do
> > +             git checkout -b $b main &&
> > +             test_commit --no-tag "Change on $b" base $b
> > +     done &&
> > +
> > +     git checkout branch1 &&
> > +     test_must_fail git merge branch2 branch3 &&
> > +     git diff --exit-code --name-status &&
> > +     test_path_is_missing .git/MERGE_HEAD
> > +'
>
> Hmph, I am not sure if the new behaviour is not too pessimistic.
> When octopus fails after successfully merging branch2 and then
> failing the merge of branch3 (i.e. the last one) due to conflict,
That's not what's happening here.  It is not failing due to conflict,
octopus is reporting that it cannot even leave things in a conflicted
state for the user, and is actually incapable of handling this
particular type of merge.  Part of the output seen when attempting
this merge includes:
    fatal: merge program failed
    Should not be doing an octopus.

See previous discussion at https://lore.kernel.org/git/xmqq35hdd205.fsf@gitster.g/. To make this clearer, perhaps I should use "test_expect_code 2" instead of test_must_fail, and also grep the output/error for the above messages.

> I think octpus users are used to be able to resolve it manually
> and make a commit.  Are we making it impossible by doing the
> reset-restore dance here?

No, we are not changing what octopus can handle here. The code above is not triggered when octopus returns that it ran into conflicts for the user to resolve. It is only triggered when octopus says it cannot handle the merge in question. See your commit 98efc8f3d8 ("octopus: allow manual resolve on the last round.", 2006-01-13), and note that the the "exit 2" code path is the one that we are hitting. I'll add some comments to the testcase and rewrite the commit message to try to make this clearer.

Previous: Elijah NewrenNext: Elijah Newren via GitGitGadget
Message 28 of 87 in “Fix merge restore state”
  1. 0/2 Fix merge restore stateElijah Newren via GitGitGadget, May 19, 2022
  2. 1/2 merge: remove unused variableElijah Newren via GitGitGadget, May 19, 2022
  3. Junio C HamanoMay 19, 2022
  4. 2/2 merge: make restore_state() do as its name saysElijah Newren via GitGitGadget, May 19, 2022
  5. Junio C HamanoMay 19, 2022
  6. Junio C HamanoMay 19, 2022
  7. Elijah NewrenJun 12, 2022
  8. ZheNing HuJun 12, 2022
  9. 0/6 Fix merge restore stateElijah Newren via GitGitGadget, Jun 19, 2022
  10. 1/6 t6424: make sure a failed merge preserves local changesJunio C Hamano via GitGitGadget, Jun 19, 2022
  11. 2/6 merge: remove unused variableElijah Newren via GitGitGadget, Jun 19, 2022
  12. Junio C HamanoJul 19, 2022
  13. 4/6 merge: make restore_state() restore staged state tooElijah Newren via GitGitGadget, Jun 19, 2022
  14. ZheNing HuJul 17, 2022
  15. Junio C HamanoJul 19, 2022
  16. Junio C HamanoJul 19, 2022
  17. Elijah NewrenJul 21, 2022
  18. 3/6 merge: fix save_state() to work when there are racy-dirty filesElijah Newren via GitGitGadget, Jun 19, 2022
  19. ZheNing HuJul 17, 2022
  20. Junio C HamanoJul 19, 2022
  21. Elijah NewrenJul 21, 2022
  22. Junio C HamanoJul 19, 2022
  23. 6/6 merge: do not exit restore_state() prematurelyElijah Newren via GitGitGadget, Jun 19, 2022
  24. ZheNing HuJul 17, 2022
  25. Junio C HamanoJul 19, 2022
  26. Eric SunshineJul 20, 2022
  27. Elijah NewrenJul 21, 2022
  28. Elijah NewrenJul 21, 2022
  29. 5/6 merge: ensure we can actually restore pre-merge stateElijah Newren via GitGitGadget, Jun 19, 2022
  30. ZheNing HuJul 17, 2022
  31. Junio C HamanoJul 19, 2022
  32. Elijah NewrenJul 21, 2022
  33. 0/7 Fix merge restore stateElijah Newren via GitGitGadget, Jul 21, 2022
  34. 1/7 merge-ort-wrappers: make printed message match the one from recursiveElijah Newren via GitGitGadget, Jul 21, 2022
  35. Junio C HamanoJul 21, 2022
  36. Elijah NewrenJul 21, 2022
  37. Junio C HamanoJul 21, 2022
  38. Elijah NewrenJul 21, 2022
  39. 2/7 merge-resolve: abort if index does not match HEADElijah Newren via GitGitGadget, Jul 21, 2022
  40. 3/7 merge: do not abort early if one strategy fails to handle the mergeElijah Newren via GitGitGadget, Jul 21, 2022
  41. Junio C HamanoJul 21, 2022
  42. Ævar Arnfjörð BjarmasonJul 25, 2022
  43. Elijah NewrenJul 26, 2022
  44. Ævar Arnfjörð BjarmasonJul 26, 2022
  45. 4/7 merge: fix save_state() to work when there are stat-dirty filesElijah Newren via GitGitGadget, Jul 21, 2022
  46. 6/7 merge: ensure we can actually restore pre-merge stateElijah Newren via GitGitGadget, Jul 21, 2022
  47. Junio C HamanoJul 21, 2022
  48. Ben HumphreysMar 2, 2023
  49. Elijah NewrenMar 2, 2023
  50. Junio C HamanoMar 2, 2023
  51. Rudy RigotMar 4, 2023
  52. Ben HumphreysMar 6, 2023
  53. 5/7 merge: make restore_state() restore staged state tooElijah Newren via GitGitGadget, Jul 21, 2022
  54. Junio C HamanoJul 21, 2022
  55. Junio C HamanoJul 21, 2022
  56. Elijah NewrenJul 21, 2022
  57. 7/7 merge: do not exit restore_state() prematurelyElijah Newren via GitGitGadget, Jul 21, 2022
  58. Junio C HamanoJul 21, 2022
  59. 0/7 Fix merge restore stateElijah Newren via GitGitGadget, Jul 22, 2022
  60. 1/7 merge-ort-wrappers: make printed message match the one from recursiveElijah Newren via GitGitGadget, Jul 22, 2022
  61. 2/7 merge-resolve: abort if index does not match HEADElijah Newren via GitGitGadget, Jul 22, 2022
  62. Ævar Arnfjörð BjarmasonJul 22, 2022
  63. Elijah NewrenJul 23, 2022
  64. Ævar Arnfjörð BjarmasonJul 23, 2022
  65. Elijah NewrenJul 26, 2022
  66. Ævar Arnfjörð BjarmasonJul 26, 2022
  67. 3/7 merge: do not abort early if one strategy fails to handle the mergeElijah Newren via GitGitGadget, Jul 22, 2022
  68. Ævar Arnfjörð BjarmasonJul 22, 2022
  69. Elijah NewrenJul 23, 2022
  70. 4/7 merge: fix save_state() to work when there are stat-dirty filesElijah Newren via GitGitGadget, Jul 22, 2022
  71. 5/7 merge: make restore_state() restore staged state tooElijah Newren via GitGitGadget, Jul 22, 2022
  72. Ævar Arnfjörð BjarmasonJul 22, 2022
  73. Elijah NewrenJul 23, 2022
  74. 6/7 merge: ensure we can actually restore pre-merge stateElijah Newren via GitGitGadget, Jul 22, 2022
  75. 7/7 merge: do not exit restore_state() prematurelyElijah Newren via GitGitGadget, Jul 22, 2022
  76. 0/8 Fix merge restore stateElijah Newren via GitGitGadget, Jul 23, 2022
  77. 2/8 merge-resolve: abort if index does not match HEADElijah Newren via GitGitGadget, Jul 23, 2022
  78. 1/8 merge-ort-wrappers: make printed message match the one from recursiveElijah Newren via GitGitGadget, Jul 23, 2022
  79. 4/8 merge: do not abort early if one strategy fails to handle the mergeElijah Newren via GitGitGadget, Jul 23, 2022
  80. 3/8 merge: abort if index does not match HEAD for trivial mergesElijah Newren via GitGitGadget, Jul 23, 2022
  81. 5/8 merge: fix save_state() to work when there are stat-dirty filesElijah Newren via GitGitGadget, Jul 23, 2022
  82. 6/8 merge: make restore_state() restore staged state tooElijah Newren via GitGitGadget, Jul 23, 2022
  83. 7/8 merge: ensure we can actually restore pre-merge stateElijah Newren via GitGitGadget, Jul 23, 2022
  84. 8/8 merge: do not exit restore_state() prematurelyElijah Newren via GitGitGadget, Jul 23, 2022
  85. Junio C HamanoJul 25, 2022
  86. Elijah NewrenJul 26, 2022
  87. ZheNing HuJul 26, 2022

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.