Boost logo

Boost :

Subject: Re: [boost] [git] Mercurial? easy merging in svn, how about git/hg?
From: Martin Geisler (mg_at_[hidden])
Date: 2012-03-29 04:12:04


Frank Birbacher <bloodymir.crap_at_[hidden]> writes:

> Hi!
>
> Thanks very much for your feedback! Both of you.
>
> Am 28.03.12 22:49, schrieb Martin Geisler:
>> So, it's very similar for this example.
>
> Yes, so to me there is no significant difference in the commands that
> create and merge branches.

Agreed, the differences are just syntax! Btw, I consider SVN a super
centralized version control system, so this is not surprising.

> In the commands that is! You provided an example where a rename leads
> to a conflict in svn, but not in hg or git. That's a drawback on svn
> of course. On the other hand it is a reported bug and likely to be
> fixed (I wonder why this hasn't been fixed already, v1.5 has already
> been a while on stage.)

I know Boost is C++ and not, say, Java, so maybe you don't run into this
very often? When I talk to people using Java it sounds like they're
moving files around all day :) So it's really important for them that
they can merge a branch back to trunk even when files have been renamed.

>> But don't you need to add a --reintegrate flag if you want to merge
>> your branch several times? It's mentioned here
> [snip]
>> Neither Git nor Mercurial has has such a flag and both let you keep
>> working with your branches after merging them (in any direction).
>
> I do not need the "--reintegrate" flag. svn 1.6 keeps track of merge
> information, and even before that I knew when the branch was created.
> I've been working with branches in svn for some time and only lately
> it occurred to me what the workflow using "--reintegrate" is.

I had to re-read this section:

http://svnbook.red-bean.com/en/1.7/svn-book.html#svn.branchemerge.basicmerging.reintegrate

It points out that the --reintegrate flag is critical for reintegrating
changes from a branch back into trunk. They are talking about a scenario
where you've continuously kept the branch up to date with changes from
trunk and now want to merge back:

  trunk: a --- b --- c --- d --- e
            \ \
  branch: r --- s --- t --- u

Because of the t revision, you cannot just replay the r:u changes on top
of e: the u revision already contain some of the changes that are in e
(the b and c changes).

> I guess this is easier in hg or git.

Yes, in those systems you would do a final merge from the mainline into
your branch:

  default: a --- b --- c --- d --- e
            \ \ \
  branch: r --- s --- t --- u --- v

Merging back to default is now a no-op! Technically you try to do a
merge between e and v. You find their common ancestor (e!) and you now
have a degenerate three-way merge where the state in v wins every time.
So you can create

  default: a --- b --- c --- d --- e - f
            \ \ \ /
  branch: r --- s --- t --- u --- v

where the files in f look exactly like they do in v. There have often
been a few changes on default since the last branch synchronization so
you really start with

  default: a --- b --- c --- d --- e --- f --- g
            \ \ \
  branch: r --- s --- t --- u --- v

and merge g and v. Here the ancestor is e (close by!) so you only have
to consider changes made in f and g. The final state is then:

  default: a --- b --- c --- d --- e --- f --- g --- h
            \ \ \ /
  branch: r --- s --- t --- u --- v ------------'

Three-way merges are *symmetric* in Git and Mercurial and so it doesn't
matter which way you merge -- you get the same amount of conflicts.

>> Could you try repeating the above commands with a rename in your
>> branch and an edit in trunk?
>
> I already tried your example before, and yes, I got a conflict. And I
> was not able to work around this.
>
> BTW, as I understand git: the working copy contains a hidden directory
> that stores all of the repository data. And the checkout will be
> placed at top-level. Is there a way to checkout multiple branches at
> the same time?

Yes, you can do that with both systems. With Mercurial you make a new
clone based on an existing local repository:

  hg clone my-repo my-second-repo
  cd my-second-repo
  hg update my-favorite-branch

That gives you two independent working copies.

By making a local clone you avoid downloading anything again.
Furthermore, a local clone will make *hardlinks* between the files in
the .hg/ directory. This means that both clones share the disk space:
you only pay for creating a new working copy.

So you end up with two checkouts and both have the full history inside
their .hg/ directories. But the disk space is shared so the overhead is
very low.

With SVN you would have to make a new 'svn checkout' -- or I guess you
can copy an existing checkout with 'cp' and then 'svn switch'? That way
you avoid downloading the files that aren't affected by the switch.

Notice a fundamental difference in design here: Mercurial (and Git) have
branches. Subversion don't:

  http://svnbook.red-bean.com/en/1.7/svn-book.html#svn.branchmerge.using.concepts

Instead, SVN has a cheap server-side copy mechanism and SVN allows you
to checkout a single subdirectory at a time. SVN also allows you to
merge changes made in a subdirectory into another subdirectory. These
features let you "emulate" branches and tags, but they are not
first-class citizens in the system.

This in turn allows SVN to represent a richer history than Git and
Mercurial. That is, I can do

  svn cp ^/trunk/foo/bar ^/tags/bar-1.0 -m "branch"

to "tag" a random subdirectory. That operation doesn't make any sense in
the other systems: there a tag references a commit and that's that.
Depending on your viewpoint, you can say that Git and Mercurial models
the history in a more clean way. You can also say that they lack a
crucial feature :)

-- 
Martin Geisler
aragost Trifork
Professional Mercurial support
http://www.aragost.com/mercurial/

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk