All of lore.kernel.org
 help / color / mirror / Atom feed
From: Elijah Newren <newren@gmail.com>
To: Phil Hord <phil.hord@gmail.com>
Cc: Tassilo Horn <tsdh@gnu.org>, Jeff King <peff@peff.net>,
	Git <git@vger.kernel.org>
Subject: Re: [Bug] Stashing during merge loses MERGING state
Date: Thu, 11 Mar 2021 23:02:24 -0800	[thread overview]
Message-ID: <CABPp-BF9sfqDfj=4885n_xwMr=2=jtOZOACtnPi=QYdC3G-pgQ@mail.gmail.com> (raw)
In-Reply-To: <CABURp0pFdHAx_+-e+O35Qxtbe3_+cZy9SZcOSeR2R7v_neRwKg@mail.gmail.com>

On Thu, Mar 11, 2021 at 9:21 PM Phil Hord <phil.hord@gmail.com> wrote:
>
> On Thu, Mar 11, 2021 at 12:45 PM Tassilo Horn <tsdh@gnu.org> wrote:
> > >> Or that popping the stash again would also restore the MERGING state.
> > >
> > > This would make more sense: the stash records that part of the state,
> > > and then we make it available again later when the stash is applied.
> > > However, that feature doesn't exist yet.
> >
> > Too bad.
>
> Consider also what happens when `git stash apply` results in a merge
> conflict because of differences between your current index and the one
> you had when you originally saved the stash.  This results in the
> usual merge conflict markers that then need to be cleaned up.
>
> Could we sanely deal with this in a world where we also tried to
> restore .git/MERGE_HEAD when the stash was applied. Something like
> `git stash apply --continue`, possibly after resolving the stash
> conflicts?  But what if we stashed the merge conflict that resulted
> from the stash apply?  I guess it would still work, but the stash
> history would be, um, interesting.
>
> I wonder if a fix could be as simple as recording the MERGE_HEAD as
> the third parent commit of the stash ref. I think that would provide
> all the information needed to put things back, except possibly for
> things like the rerere state, which is also set up during a conflict,
> and other incidentals like .git/MERGE_MSG.  (And it feels like it
> might break compatibility with older versions that don't expect a
> third parent.)

Third parent is already reserved by --untracked to store a "parent"
commit that has all the untracked files added.  So, it'd be a fourth
parent.  Except when --untracked isn't passed we don't need the third
parent, and if it wasn't a merge commit we don't need the fourth...fun
times.

> I would be a bit concerned about the possibility of silently creating
> an "evil merge".  Suppose you stash this conflict on some branch and
> then pop it onto a different one.  I expect we would then be prepared
> to store all those changes from a different branch including existing
> resolved merge conflicts into the new one.  That could be surprising
> and subtle.
>
> But maybe I'm overthinking it.  Wouldn't the stash apply result in
> merge conflicts that would catch out all the troubling parts?

I don't think you're overthinking it; I think this is highly
problematic.  Let's consider the following history (from the
git-rebase manpage):

               o---o---o---o---o  master
                    \
                     o---o---o---o---o  next
                                      \
                                       o---o---o  topic

First, let's consider the case where you don't record MERGE_HEAD when
stashing.  So, you start out on next and run:

   $ git merge topic

but you hit some conflicts and stash it:

   $ git stash push -m "in middle of merging topic"

Note that this would record applying the changes new to "topic" since
"next", with whatever conflicts still exist.  Then sometime later you
check out master, and decide to apply the stash:

   $ git stash apply

What would you get?  Just the changes in topic new relative to next,
but nothing in next that preceded it.  If you record that as a merge
commit with topic as the second parent, you've created an evil merge.


Of course, that was saying if you didn't store MERGE_HEAD somewhere.
But now let's say you did store MERGE_HEAD (== topic) when you
stashed.  What then happens when you go to apply the stash?  If you
really want a merge with MERGE_HEAD, then you need to redo the merge.
So you invoke the merge machinery.  That gives you conflicts anew.
How do you use your stash at this point to resolve the set of
conflicts, even if they were the same as what you got in your original
merge?

Personally, I think we should make stash check for
$GIT_DIR/MERGE_HEAD, and error out if it exists.  I don't see an easy
way to make this behave reasonably.  (Which isn't to say I don't see
ways, but it'd probably involve being able to do a diff since the
point when the merge stopped and recording it, then attempting to
reapply _that_ patch, one that removes conflict markers and such, on
top of a redone merge, possibly complete with nested conflict markers.
But I'm not sure this is sounding like a reasonable level of
difficulty for users to deal with.)

> I think being able to stash during a merge conflict could be very
> useful.  I do sometimes need to get back to a working state
> momentarily and a merge conflict represents a long pole to doing so.
> Similarly, it could be useful to stash a conflicted `git rebase` so I
> could return to it later and pick up where I left off.  Now we really
> would need to store some extra metadata, though, like the todo-list
> and ORIG_HEAD.  And we would definitely need some extra command line
> switch to tell stash (or rebase) that I want to include all the rebase
> state and also "pause" the rebase by restoring to my starting point.
>
> Thanks for raising the issue, Tassilo.  This has obviously given me
> more ideas for things I forgot were missing.
>
> > > I can't offhand think of a reason it couldn't be implemented. It's
> > > possible that it would mess with somebody else's workflow (e.g., they
> > > think it's useful to stash some changes independent of the merging
> > > state, and then apply it later, perhaps while replaying the same or a
> > > similar merge). So it might need to be tied to a command-line option
> > > or similar.
> >
> > Everything breakes someones workflow [1], so an option would be fine.
> >
> > However, I'd suggest to protect users shooting in their foot with a
> > warning and confirmation query for the time being.  I consider myself a
> > quite experienced git user but this stash trouble today came totally
> > unexpected.  And I've asked on #git@irc.freenode.net and got no answer
> > which is totally uncommon.  So I guess that this stash during merge
> > thing is pretty much a gray area.
>
> I don't think we could easily add the warning when the stash is
> applied since we have forgotten the merge existed in the first place.
> So we would have to do it during stash save.
>
> "Warning: You are stashing during a merge conflict and your merge
> state will not be restored by stash apply."
>
> Seems reasonable.

I think it should be an error and abort the stash operation; this is
way too much of a footgun.

      parent reply	other threads:[~2021-03-12  7:03 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-03-11 14:00 [Bug] Stashing during merge loses MERGING state Tassilo Horn
2021-03-11 19:25 ` Jeff King
2021-03-11 20:31   ` Tassilo Horn
2021-03-12  5:00     ` Phil Hord
2021-03-12  6:09       ` Junio C Hamano
2021-03-12  7:04         ` Elijah Newren
2021-03-12  7:02       ` Chris Torek
2021-03-12  7:17         ` Elijah Newren
2021-03-12  7:02       ` 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

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

  git send-email \
    --in-reply-to='CABPp-BF9sfqDfj=4885n_xwMr=2=jtOZOACtnPi=QYdC3G-pgQ@mail.gmail.com' \
    --to=newren@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=peff@peff.net \
    --cc=phil.hord@gmail.com \
    --cc=tsdh@gnu.org \
    /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.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.