Web lists-archives.com

Re: [GSoC] [RFC] Proposal: Teach git stash to handle unmerged index entries.




Kapil Jain <jkapil.cs@xxxxxxxxx> writes:

> Plan to implement the project.
>
> Objective:
>
> Description:
>
> Implementation Idea:
>
> Relevant Discussions:
>
> Idea Execution Plan: Divided into 2 parts.

Two things missing before implementation idea are design, and more
importantly, the success criteria.  What lets you and your mentor
declare victory?

As to the design, it does not quite matter if you add four or more
separate trees to represent stage #[0123] entries in the index to
the already octopus merge commit that represents a stash entry
(i.e. when keeping the untracked ones, I think the stash entry's
"result of the merge" tree records the state of the tracked files in
the working tree, and the "result of the merge" commit records the
the-current HEAD, a commit that records the state of the index and
anothre commit that records the state of the untracked files, as its
parents---that's already a 3-parent octopus).

The fact that a stash entry is represented as a merge commit is a
mere implementation detail, and there is *NO* need to worry about
resolving merge conflicts while recording a stash.  If the result of
this GSoC task is to be any usable together with the current version
in a backward compatible way, you must record these extra states as
extra parents of the merge, so it is sort of given already that
you'd be using some form of an octopus merge.

The real challenge would be how the unstashing part of such a stash
entry that records unmerged state should work.  Personally I do not
think it will be very useful to allow unstashing such a stash entry
on top of any arbitrary commit---rather, I suspect that the user
would want to come back to the exact HEAD the user had trouble
resolving conflicts at, without having to first checking it out.
IOW, a usual way to use "git stash" is

	$ git checkout topic
	$ edit edit edit
	... I am happily hacking away ...
	... the boss appears with an ultra-urgent task ...
	$ git stash save -m WIP
	$ git checkout master
	$ edit-and-build-and-test
	$ git commit
	... now the emergency is over ...
	$ git checkout topic
	... sync with the work others may have done on topic
	... while I was dealing with the boss
	$ git pull --rebase origin topic
	$ git stash pop

IOW, it is expected to be applied on top of an updated commit.

But I have a moderately strong suspicion that a stash that holds
unmerged state (i.e. a conflicted merge in progress) is created with
a use case, which is very different from the normal use case, in
mind.  When creating such a stash entry, the above sequence would go
more like this:

	$ git checkout topic
	$ git merge ...
	... oops, conflicted, and it takes time to resolve ...
	$ edit edit inspect edit
	... the boss appears
	$ git stash save -m "Merge in progress"
	$ git checkout master
	... deal with the emergency the same way ...
	$ git checkout topic
	... go back to the conflict resolution first without
	... touching what may have happened on the branch in
	... the meantime---a human brain cannot afford to deal
	... with two or more parallel conflicts at the same
	... time.
	$ git stash pop
	... now deal with the conflict we were looking at
	... before the boss interrupted us.
	$ edit inspect edit
	... be satisfied with the result
	$ git commit
	... now let's see if others have something else that
	... is interesting
	$ git pull --rebase origin topic

And if we assume that the primary use of a stash for a conflicted
state is to bring us back to the exact state (rather than allowing
us to pretend as if we started form a different HEAD), it might even
make sense to teach "git stash pop" step to barf if HEAD does not
match the first parent of the merge commit that represents the stash
entry being applied (again, stash^{tree} is the working tree,
stash^1 is then-current HEAD).  That would make the application side
a lot simpler and manageable by developers who are not intimately
familiar with the code.

Others may disagree with the above assumption (i.e. "a stash for a
conflicted state does not have to be applicable), though, making
your task a lot harder ;-).

Quite honestly, I do not think you can design a system that attempts
to "stash apply/pop" a recorded unmerged state on top of any
arbitrary HEAD and leave a state useful for the end user to deal with
when the "stash apply/pop" step itself introduces _new_ conflicts
due to the differences between the then-current HEAD the stash entry
is based on and the HEAD the "stash apply" is attempted on top of.
Even the current "stash apply/pop with the change between the HEAD
and the index" does punt when it cannot make a clean application,
and that is without any unmerged entries in the recorded index
state.

The key point is "a state useful for the end user"---it is easy to
build a system that claims to leave a state created from the updated
HEAD and what's recorded in a stash entry that the end users cannot
use as a stating point to make progress, but that is not something
our users would want.

Have fun.