[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