[PATCH] Make fast forward merges more like normal merges.

Peter Baumann siprbaum at stud.informatik.uni-erlangen.de
Thu Jun 7 03:13:22 CDT 2007


On 2007-06-07, Alexis S. L. Carvalho <alexis at cecm.usp.br> wrote:
> Thus spake Eric M. Hopper:
>> On Wed, 2007-06-06 at 17:46 -0700, Brendan Cully wrote:
>> > On Wednesday, 06 June 2007 at 19:44, Nathan Jones wrote:
>> > > I have been using named branches and fast forward merges lately, but I
>> > > have found that fast forward merges work differently than normal merges:
>> > > 
>> > > 1. Parents are not listed.
>> > > 2. hg log -M displays fast forward merges.
>> > > 3. hg log -p does not display a diff like regular merges.
>> > 
>> > I don't have a super-strong opinion about it, but I'm not sure these
>> > are bad things. It seems like you're turning fast-forward merges back
>> > into plain old merges.
>> 
>> What are fast forward merges, and why were they created?
>
> "Fast-forward merge" is a term that comes from git.  I think the "merge"
> here means the act of merging, not a merge revision (i.e. a revision
> with more than one parent).
>
> Forget about named branches for a moment.  Say the revision graph looks
> originally like:
>
>         a
> o---o---o
>
>
> Now suppose I clone that and start committing and now I have
>
>         a
> o---o---o
>          \          b
>           --o---o---o
>
> If you pull my changes and try to merge, you'll get a "there is nothing to
> merge, just use 'hg update'".  And indeed, there's no need to merge
> them, since one revision is an ancestor of the other.
>
> (In git, if you do the equivalent operations, the system would just move
> the current branch name[1] (which is pointing at "a") to "b",
> effectively "fast-forwarding" it.  Note that in this case, there's no
> commit involved in this "merge", so it gets this special name.)
>
> Enter named branches.  Suppose you were using the branch "default", and
> I used a branch "issue1024":
>
>         a
> D---D---D
>          \          b
>           --I---I---I
>
> You reviewed my changes and want to include them in the "default"
> branch (i.e. you want a "hg pull -r default" to include my changes).
>
> Because we store the branch name in the changeset itself, you /have/ to
> add another commit on top of "b".  Right now the only parent of this new
> commit is "b" (since there's no actual need to merge):
>
>         a               c
> D---D---D               D
>          \          b  /
>           --I---I---I--
>
> But even though we say that "this is a merge; don't forget to commit",
> it's not /really/ a merge, because the new revision has only 1 parent.
> The patch is essentially suggesting we do a real merge:
>
>         a               c
> D---D---D---------------D
>          \          b  /
>           --I---I---I--
>
> Personal opinion: since we have to add a new revision anyway, I think we
> might as well make it a real merge.
>
> I haven't tried the patch, but I think just setting adding the second
> parent in the dirstate should be enough; no need to change the status of
> other files... but maybe I missed something there.
>
> Alexis
>
> [1] - in git, a branch name is essentially a pointer to a revision,
> which is moved every time you commit (or do a fast-forward merge...)

(bare with me, I have no clue about hg, but I know why fastforward
merges are _ESSENTIAL_ to git)

In git, a commit is identified by a SHA1 and if 2 commits have the same
SHA1, the _are_ identicall. So lets have a look at your example:

0)
	Repo1:                              Repo2:

        D---D---A                           D---D---A
                                                     \
                                                      --I---I---B

1) Repo1 merges Repo2 (using fastforward):

	Repo1:                              Repo2:

        D---D---A                           D---D---A
                 \                                   \
                  --I---I---B                         --I---I---B


2) Repo2 wants to merge everyting done in Repo1:

	As the commit B in repo1 has the exact same SHA1 as the commit B
	in repo2, git recognizes that repo2 is uptodate. No need to do
	anything

===========================================================================

Now suppose we are starting again at (0), but now we are using a real
merge

1')	Repo1:                              Repo2:

        D---D---A------------M              D---D---A
                 \          /                         \
                  --I--I--B                            --I--I--B

2') Repo2 wants to merge everyting done in Repo1:

        D---D---A------------M               D---D---A-----------M
                 \         /                          \         / \
                  --I--I--B                            --I--I--B---N

	As one can see, another commit (N) which is represented as a merge
	(2 parents) is created. So you add an useless commit which just
	says that you updated/merged everything which was in repo1
	without adding anything new, because we were already uptodate.

3') Repo1 wants to merge everyting done in Repo2:

        D---D---A-----------M---O             D---D---A-----------M
                 \         / \ /                       \         / \
                  --I--I--B---N                         --I--I--B---N


So without doing a fastfoward merge, you never get to the situation
where you the HEADs of the repos are identicall, where the SHA1 of HEAD
commit in repo1 == SHA1 of HEAD commit in repo2, so you are per
definition (in git) never uptodate. As I said, I have no clue about hg
and if this would become a problem, but in git, it really _IS_ a problem
if you don't do fastforward merges.

(This was explained by Linus in a very longish flame^W^Wdiscussion with
the bzr people on the git ML)

Greetings,
  Peter Baumann



More information about the Mercurial-devel mailing list