Re: [PATCH] technical doc: add a design doc for the evolve command
- Date: Tue, 20 Nov 2018 12:19:34 -0800
- From: Stefan Xenos <sxenos@xxxxxxxxxx>
- Subject: Re: [PATCH] technical doc: add a design doc for the evolve command
> This explains why we have 'origin' fields in the meta commits, it might
> be worth putting a forward reference or note earlier on to explain why
> recording the origin is useful. (I didn't find gerrit needs it very
> convincing on its own but it is actually more general than gerrit's
> specific use case)
I'll add the forward reference.
TBH, gerrit is the main reason I added it - so I'm interested in why
you didn't find the gerrit use-case convincing. Can you elaborate? (If
there's some other way around the gerrit requirement, we might not
need the origin parents)
> Should this be meta/mychange:refs/for/master or have I missed something?
It should be metas/mychange/.... It's already fixed in the v2 patch.
I really wanted to use the namespace "changes", but gerrit is
squatting on that. I tried "change", but that brakes the plural naming
scheme and may get confused with gerrit's namespace, so I settled on
> I think it would make sense to have this next to the sections on commit
> --amend and merge I was wondering what about rebase when I was reading
> those sections.
> I'm a bit confused why it is creating a meta ref per commit rather than
> one for the current branch.
I tried to explain that later in the doc. meta refs serve two purposes
- they act as stable names for changes (or at least the commits at the
head of each change) and they point to the metacommits that are
currently in use. For both purposes, we need a ref per commit. For the
"stable name" case, this should be obvious - something that just
points to a branch couldn't provide different names for each commit on
that branch. The metacommit case is less obvious - the set of
metacommits for one change aren't connected to the metacommits for any
other change. The "parents" of a metacommit are older versions of the
same change. They don't point to the metacommits from the parent
change. That means that there is no single ref we could create for a
branch that would reach all the necessary metacommits.
> I got the impression they had put quite a lot of effort
> into having evolve automatically run and resolve divergences when
> pulling and rebasing, is there a long term plan for git to do the same?
IMO, we should add anything to the plan if doing so improves the
workflow of our users... but it sounds like you're referring to
mercurial features I've never used. Could you point me to specific
docs on the feature you want and/or make a concrete suggestion about
how it might work?
I never use pull so it slipped my mind. It would probably make sense
to have the option of doing an automatic evolve after pull (actually,
once the feature is stable, most users would probably want it to be
the default). How do you think it should be triggered? "git pull
--evolve"? or perhaps "git pull --rebase=evolve"? We should probably
also introduce a new "evolve" enum value to branch.<name>.rebase
config value. I'll use "--evolve" for now. If may make sense to add
"--evolve" to every git command that performs an automatic evolve when
> What happens if the original commit are currently checked out with local
For a start, I'll probably just display an error message if the
current working tree is dirty ("Please stash"). Long term, I'd like it
to work like rebase --autostash. It should stash your changes, do the
evolve, return to the evolved version of the original change, and
reapply the stash. I'll add this to the doc.
> Can I suggest using refs/remote/<remotenome>/metas. I
Ooh! Great idea! I'll update the doc.
> I think this could be useful (although I guess you can get the branches
> you've been working on recently from HEAD's reflog quite easily).
The changes list is different from the reflog. It's a list of all your
unsubmitted patches - regardless of their age or what branch they're
on. They may not have corresponding branches: you may have been
working on them with a detached head, or there may be multiple changes
on the same branch. You might not have visited them recently, in which
case they wouldn't be in the reflog at all. You may have reset to an
older version of the change, in which case they'd be in the reflog but
the reflog and change point to different places. If you've used gerrit
before, the "changes" list will contain pretty much the same content
as the gerrit dashboard, except that it works locally.
>> +Much like a merge conflict, divergence is a situation that requires user
>> +intervention to resolve. The evolve command will stop when it encounters
>> +divergence and prompt the user to resolve the problem. Users can solve the
>> +problem in several ways:
>> +- Discard one of the changes (by deleting its change branch).
>> +- Merge the two changes (producing a single change branch).
>I assume this wont create merge commits for the actual commits though,
>just merge the meta branches and create some new commits that are each
>the result of something like 'merge-recursive original-commit
It depends on which version of merge you use. I've proposed a new
"merge --amend" argument specifically for resolving divergence. It
avoids creating merge commits as long as there's only one parent
remaining after combining the parents of the commits being merged.
Basically, if the two things being merged are divergent commits, it
would resolve the divergence without creating a new merge commit...
but if the divergent commits had different parents or were themselves
merge commits, the result may still be a merge commit.
If you run the normal version of merge, it *would* create a merge
commit and leave the changes divergent. However, one of the
transformations on the evolve command will look for this situation and
resolve it. Specifically, if it encounters two divergent changes but
exactly one child change contains a merge that would resolve that
divergence, the transformation will merge all three changes, squash
them together, and make all three changes point to the result. I'm not
sure what to call this transformation, but it serves a useful purpose:
it allows users to use either form of merge to resolve the divergence.
If they use the "--amend" version of merge, no merge commit is created
and the divergence is resolved immediately. If they use the normal
version of merge, a merge commit is created (as it is now) and the
evolve command figures out later whether that merge was intended to
resolve divergence. This avoids putting any magic in the merge command
itself, avoids changing the existing behavior of the merge command,
and it means that most users won't need to learn about "merge --amend"
and can't accidentally paint themselves into a corner by accidentally
using the wrong kind of merge. Power users can disable this
transformation and resolve their divergence explicitly using --amend.
Novices can just use the defaults and things will probably work.
It can get more complex, though. If there are two or more child
changes containing merge commits that resolve divergence, this
transformation would happen separately for each one and the resulting
merges would themselves become divergent (since they are two
conflicting solutions to the same problem). This may happen if the
user unnecessarily resolved the same divergence multiple times with
different merge commits. At that point, one of several things would
happen. If after rebasing the merge, the result automerges to exactly
the same thing (which would happen if both merges were the result of
running the automerger on incremental versions of the same two
changes), the divergence would instantly resolve itself because the
two changes are aliases. Otherwise, this new divergence would be
treated like any other and evolve would eventually try to apply the
same algorithm recursively on the new divergent changes.
I'll elaborate more on the supported transformations in the doc for
the evolve command.