> Why is that bad? At my work we are using svn and I have like 8 > branches (not all of them are currently active I confess.) I put my > branches not into /branches, but into /users/fbirbacher. This way > noone has to care about them.
Because once you pushed it's public and you cannot rewrite public history. Means you cannot fix all the "oops" commits before merging with production.
> With git/hg, when you do a mistake, you simply cancel your local > > merge and redo it again until you did the right thing, then you > > push.
> This suggests 1st mistakes you do in svn cannot be repaired, and 2nd > you will spot every mistake in git/hg before you push. As I > understand, once you push to a public repo and then discover a mistake > your mistakes will be just as visible as with svn. I agree there is a > chance to find some errors before publishing, but with svn I spot > errors in commands I run on my working copy easily before committing.
You're right. I forgot that merges were local with svn and that the result wasn't commited yet. I was a bit heated about the discussion and went emotional, which is a bad thing to do. Anyway, what I really was after is the inability to fix "oops" commits before they go public.
To be honest I think all the pros/cons that had to be made have been made now, and eventually it's the people who really work on boost that should decide because they're the firsts affected.
> Running the above as a script takes some time: > real 0m10.433s > user 0m0.160s > sys 0m0.253s
First I want to point out that such a mircobenchmark is probably not very reliable, in part because this is a purely local repository. That said, I think it's incredible that svn is taking ten seconds to perform such trivial operations on such a tiny local test repository. Perhaps you should repeat your measurement to make sure this wasn't an outlier.
For the sake of completeness I've still written what I believe to be the closest git equivalent, which you can find at the bottom of this email. Again it takes the same number of commands but with a larger fraction of them in the initial setup (suggesting that the real work will ultimately take fewer commands in git), and again fewer keystrokes per line on average. Time on an iMac from 2007:
real 0m0.319s user 0m0.045s sys 0m0.124s
This is fairly consistent over multiple runs.
> 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, as Martin explained that can be done quite conveniently with a second working copy.
-Julian
-----
# setup: # cd /tmp mkdir testrepo cd testrepo git init # creating standard branches (branches don't get created without content) git co -b master echo "old" > old.txt git add old.txt git ci -m "first commit" git co -b develop
# create and switch to a branch: git co -b branch # edit branch: echo "new" > new.txt git add new.txt git ci -m "new file" # switch back to develop and merge+commit: git co develop git merge branch # insert hypothetical conflict resolution here
# REPEATING: git co branch echo "further" >> new.txt git ci -am "added content" # switch and merge+commit: git co develop git merge branch # inspection: git status, git log, etcetera
On Thu, Mar 29, 2012 at 11:41 AM, Julian Gonggrijp
<j.gonggr...@gmail.com> wrote: > Yes, as Martin explained that can be done quite conveniently with a second working copy.
You can also have multiple branches in one working copy. Git calls that concept "tracked branches". You can have as many branches in a (local) repository as you want and some of them, if not all, can be tracking branches, which means when they created, they point to a remote branch in the repo you've cloned from (aka 'origin') or indeed any other, and pull in changes from there.
<stephan.men...@gmail.com> wrote: > You can also have multiple branches in one working copy. Git calls > that concept "tracked branches". You can have as many branches in a > (local) repository as you want and some of them, if not all, can be > tracking branches, which means when they created, they point to a > remote branch in the repo you've cloned from (aka 'origin') or indeed > any other, and pull in changes from there.
The reason why I bring that up is because in all this discussion here, there'a a lot of comparing handling of branches in SVN and git and what is easier and whatnot. This is not really a sensible thing to do as the very concept of 'branches', despite equal naming, is in fact very different in git and svn. So different, that direct comparison does not make sense in my opinion, as above example shows.
Thank you all for the thorough explanations. I really enjoy the feedback here.
Am 29.03.12 10:12, schrieb Martin Geisler:
> 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:
Correct.
> 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).
My approach with svn without --reintegrate is: merge t into the trunk and use --record-only. This way the files stay unchanged, but the metadata (mergeinfo) will be updated to reflect that trunk now contains t. This is somewhat arkward, but in the end it enables a merge of the branch into the trunk. svn will then by itself patch r, s, and u into the trunk. So no three-way-merge here, but (maybe) a series of diffs that will get applied. The --reintegrate option will instead employ a three-way merge.
I see how a merge of all of trunk into the branch just before merging the branch into trunk will help to reduce conflicts. So I consider - --reintegrate now. Maybe this is the point where git and hg have better handling of merges. What will happen in the above example with git or hg when merging the branch into trunk? Do you have to do a final merge from mainline into the branch? What will happen if you skip this step? I'm asking because I might want to cherry pick changes from either side and merge them into the other: some changes from trunk into the branch some other from the branch into trunk and at the end merge the whole branch into trunk. How is that supported?
> 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.
Is that supported on Windows as well?
> 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.
Correct. And you will have to pay for duplicate files. SVN will keep a pristine copy of all files in its hidden directory. So every working copy will have its own set of pristine files, no hardlinks. With the pristine files you can view the current changes (svn diff) or revert files without contact to the repo.
> Notice a fundamental difference in design here: Mercurial (and Git) > have branches. Subversion don't:
> 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.
Yes, I always thought the emulation was an advantage of svn because you don't have to learn another concept. Just copying directories to create branches allows to employ whatever organization of branches and tags you like: create /branches/releases to hold release branches if you like, create /users/myusername to supply everyone with their own sandbox, or create /proj1/trunk and /proj2/trunk in the same repo.
> This in turn allows SVN to represent a richer history than Git and > Mercurial. That is, I can do
> 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 :)
At least they disallow committing to tags by design. With svn handling of branches and tags is pure convention. Sometimes people don't adhere to the convention.
> Am 29.03.12 10:12, schrieb Martin Geisler: >> 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:
> Correct.
>> 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).
> My approach with svn without --reintegrate is: merge t into the trunk > and use --record-only. This way the files stay unchanged, but the > metadata (mergeinfo) will be updated to reflect that trunk now > contains t. This is somewhat arkward, but in the end it enables a > merge of the branch into the trunk. svn will then by itself patch r, > s, and u into the trunk. So no three-way-merge here, but (maybe) a > series of diffs that will get applied. The --reintegrate option will > instead employ a three-way merge.
> I see how a merge of all of trunk into the branch just before merging > the branch into trunk will help to reduce conflicts. So I consider > --reintegrate now. Maybe this is the point where git and hg have > better handling of merges. What will happen in the above example with > git or hg when merging the branch into trunk? Do you have to do a > final merge from mainline into the branch?
When you merge u into e, you start a three-way merge with
ancestor: c local: e remote: u
Files from these three snapshots are compared with the normal three-way merge logic: a hunk that has only been changed in one way from c to e or from c to u is copied to the result. If a hunk has been changed in different ways it's a conflict -- you have to resolve this in your merge tool of choice.
> What will happen if you skip this step?
If you don't merge, then the branches remain diverged.
> I'm asking because I might want to cherry pick changes from either > side and merge them into the other: some changes from trunk into the > branch some other from the branch into trunk and at the end merge the > whole branch into trunk. How is that supported?
When cherry-picking you're copying changes from one branch onto another. Let's say we have two long-running branches like this:
default: ... a --- b --- c --- d --- e / / stable: ... --- x --------- y
New features go onto the default branch and bugfixes go into the stable branch. The stable branch is always a *subset* of the default branch since the default branch has more features plus the bugfixes that we continuously merge in from the stable branch.
If a bugfix ends up on the default branch by mistake, then we can cherry pick it onto the stable branch. Let's say c is such a bugfix. We run
hg update stable # checkout stable branch (y) in working copy hg transplant c # re-apply b to c delta on top of y
This gives us
default: ... a --- b --- c --- d --- e / / stable: ... --- x --------- y --- z
The diff between y and z is like the diff between b and c.
We then merge stable into default again so that stable is a subset of default:
default: ... a --- b --- c --- d --- e --- f / / / stable: ... --- x --------- y --------- z
This merge is a no-op since a three-way merge doesn't care if a change has been copied into both branches. That is, the merge sees a hunk that changed from 'old' to 'new' in both branches. The merge result is naturally the 'new' hunk and there's no conflict.
Blocking changes is harder because we always use three-way merges instead of re-playing patches. If you know that you don't need a particular changeset again, then you can back it out. This is just a way of applying the reverse patch from that changeset.
+x +y -x a --- b --- c --- d --- f
Here you can think of "+x" as meaning insert a line with "x" and "-x" as meaning remove the line. The buggy changeset c is backed out and this just means that you apply "-x" on top of d. The "y" line is still part of f. Since three-way merges only consider the final states of the branches, this can be used to block a changeset.
>> 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.
> Is that supported on Windows as well?
Yeah -- I was surprised too :-) NTFS supports hardlinks and has done so for more than a decade. But people still come and ask me about this when I give a Mercurial talk :)
>> 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.
> Correct. And you will have to pay for duplicate files. SVN will keep a > pristine copy of all files in its hidden directory. So every working > copy will have its own set of pristine files, no hardlinks. With the > pristine files you can view the current changes (svn diff) or revert > files without contact to the repo.
Indeed. I measured the space taken up by the OpenOffice Mercurial repository: the working copy is 2.0 GB and the .hg/ folder is 2.3 GB.
This means that you pay a 15% overhead for storing all 270,000 changesets locally -- compared to storing just one pristine copy like SVN done. The delta compression is amazingly efficient!
>> 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.
> Yes, I always thought the emulation was an advantage of svn because > you don't have to learn another concept. Just copying directories to > create branches allows to employ whatever organization of branches and > tags you like: create /branches/releases to hold release branches if > you like, create /users/myusername to supply everyone with their own > sandbox, or create /proj1/trunk and /proj2/trunk in the same repo.
I think it's very clever that you can use a cheap server-side copy mechanism for this -- it gives you some extra freedom.
> Blocking changes is harder because we always use three-way merges > instead of re-playing patches. If you know that you don't need a > particular changeset again, then you can back it out. This is just > a way of applying the reverse patch from that changeset.
> +x +y -x a --- b --- c --- d --- f
Ok, this means removing the changes from a branch. But what for the following: consider a stable and a development branch, just as in your example. Bugfixes go to stable and features go to development. Once in a week someone merges the stable branch into dev to bring all bugfixes into dev. Now someone fixes a bug on stable which shall be fixed differently on dev, here shown as z and z':
dev: ... a --- b --- c --- d --- z' / / stable: ... --- x --------- y --- z
The z and z' are logically the same fix, but syntactically they are different. With svn you could block z from being merged into dev (effective when the merge will happen sometime in the future.) With a three-way merge this seems not easily possible. A merge from will reproduce the z in dev (ancestor is y currently.) So you will have to produce a new common ancestor of dev and stable right on the spot, meaning to do a merge and somehow remove z from it.
You might ask why can z and z' be different in the first place. Well, it may be the coding on dev has changed due to new library functions, new compiler capabilities, or new design of something.
> Yeah -- I was surprised too :-) NTFS supports hardlinks and has > done so for more than a decade. But people still come and ask me > about this when I give a Mercurial talk :)
:)
> This means that you pay a 15% overhead for storing all 270,000 > changesets locally -- compared to storing just one pristine copy > like SVN done. The delta compression is amazingly efficient!
Frank Birbacher <bloodymir.c...@gmx.net> writes: > Hi!
> Am 29.03.12 22:18, schrieb Martin Geisler: >> Blocking changes is harder because we always use three-way merges >> instead of re-playing patches. If you know that you don't need a >> particular changeset again, then you can back it out. This is just >> a way of applying the reverse patch from that changeset.
>> +x +y -x a --- b --- c --- d --- f
> Ok, this means removing the changes from a branch. But what for the > following: consider a stable and a development branch, just as in your > example. Bugfixes go to stable and features go to development. Once in > a week someone merges the stable branch into dev to bring all bugfixes > into dev. Now someone fixes a bug on stable which shall be fixed > differently on dev, here shown as z and z':
> dev: ... a --- b --- c --- d --- z' > / / > stable: ... --- x --------- y --- z
> The z and z' are logically the same fix, but syntactically they are > different. With svn you could block z from being merged into dev > (effective when the merge will happen sometime in the future.) With a > three-way merge this seems not easily possible. A merge from will > reproduce the z in dev (ancestor is y currently.) So you will have to > produce a new common ancestor of dev and stable right on the spot, > meaning to do a merge and somehow remove z from it.
> You might ask why can z and z' be different in the first place. Well, > it may be the coding on dev has changed due to new library functions, > new compiler capabilities, or new design of something.
Yeah, this situation is not uncommon with long-lived branches where a bugfix in version 1.x looks quite different when you forward-port it to the 2.x and 3.x series.
With Git or Mercurial you do the merge of z' and z and then resolve the conflicts in favour of z'. There's no direct way to block a changeset from "flowing" into a given branch -- since changesets don't "flow" anywhere when you merge.
With a simple scenario like the above it's not a big problem: you merge stable into dev after every one or two changes on stable. So you can do the merge, revert back to dev and then port the bugfix by hand:
hg update dev hg merge stable hg revert --all --rev dev # now do the bugfix by hand hg commit -m "merge with stable, hand-ported bugfix #123"
The advantage of doing a such a "dummy merge" where you throw away all changes from the other branch is that you record the merge in the history. So future three-way merges will not re-merge this change.
> With Git or Mercurial you do the merge of z' and z and then resolve > the conflicts in favour of z'. There's no direct way to block a > changeset from "flowing" into a given branch -- since changesets > don't "flow" anywhere when you merge.
> With a simple scenario like the above it's not a big problem: you > merge stable into dev after every one or two changes on stable. So > you can do the merge, revert back to dev and then port the bugfix > by hand:
This means the person to do the fix has to do a merge, too. This means to educate everyone on the team to do proper merges, right? Ok, merging should be part of daily work, especially with hg/git, but this is not true for development teams in general.
> The advantage of doing a such a "dummy merge" where you throw away > all changes from the other branch is that you record the merge in > the history. So future three-way merges will not re-merge this > change.
Correct. But it is a somewhat more complex workflow compared to a svn blocking merge. So a disadvantage of git/hg compared to svn. A rather strong one IMHO, but maybe not for boost development.
> Am 01.04.12 12:09, schrieb Martin Geisler: >> With Git or Mercurial you do the merge of z' and z and then resolve >> the conflicts in favour of z'. There's no direct way to block a >> changeset from "flowing" into a given branch -- since changesets >> don't "flow" anywhere when you merge.
>> With a simple scenario like the above it's not a big problem: you >> merge stable into dev after every one or two changes on stable. So >> you can do the merge, revert back to dev and then port the bugfix by >> hand:
> This means the person to do the fix has to do a merge, too.
Not necessarily: I can commit a bugfix to stable without being the one who merges stable in dev.
In practice, I'm probably the one who are in the best position to do the merge since I understand my bugfix and so I can decide how to apply it on dev. But I can delay the merge or ask someone else to do it instead.
> This means to educate everyone on the team to do proper merges, right? > Ok, merging should be part of daily work, especially with hg/git, but > this is not true for development teams in general.
If you're using branches (with any system) then I think you should educate the entire team about them.
But you're right that people can use a CVCS for years without knowing about branches. That's a big change with DVCS: branches are first-class concepts in the system and people use them every day. This means that people stop being afraid of merges.
But even with CVSC people are normally not afraid of merges:
svn update
is *merging* your changes into the latest changes on the server. Even
svn commit
is doing a merge -- this time it's a server-side merge between files you touched and files I touched.
With a DVCS those merges are explicit and so people are more aware of them. But if you could do 'svn update' and 'svn commit' before, then you can also run 'hg merge' or 'git pull' -- it's the same thing.
Because people learn about branches and merges with a DVCS they suddenly realize that they can pull of all sorts of "advanced" strategies like keeping long-term bugfix branches around. They are not really advanced, since it's just more of the same everyday commands that people are used to at that point: 'hg update default', hg merge stable'.
>> The advantage of doing a such a "dummy merge" where you throw away >> all changes from the other branch is that you record the merge in the >> history. So future three-way merges will not re-merge this change.
> Correct. But it is a somewhat more complex workflow compared to a svn > blocking merge. So a disadvantage of git/hg compared to svn. A rather > strong one IMHO, but maybe not for boost development.
I don't know enough about how you organize the development to comment on that. I think you should try it out on a subsystem first and see if this is a problem.
> Frank Birbacher <bloodymir.c...@gmx.net> writes: >> This means the person to do the fix has to do a merge, too.
> Not necessarily: I can commit a bugfix to stable without being the > one who merges stable in dev.
> In practice, I'm probably the one who are in the best position to > do the merge since I understand my bugfix and so I can decide how > to apply it on dev. But I can delay the merge or ask someone else > to do it instead.
Well, if there is one guy to do the merge of stable into dev and everyone on the team would send an email request that states which changes not to merge then this guy will have a hard time.
In svn the blocking merge is recorded in the system. So whenever anyone will merge the stable into dev svn will know which changes not to merge. How does hg/git help in communicating this? Imagine a branch where many developers do bugfixes and someone will once a week merge things into dev. How shall he know which changes to skip?
I can hardly imaging how such a workflow would be feasible with hg/git.
Frank Birbacher <bloodymir.c...@gmx.net> writes: > Hi!
> Am 02.04.12 10:49, schrieb Martin Geisler: >> Frank Birbacher <bloodymir.c...@gmx.net> writes: >>> This means the person to do the fix has to do a merge, too.
>> Not necessarily: I can commit a bugfix to stable without being the >> one who merges stable in dev.
>> In practice, I'm probably the one who are in the best position to do >> the merge since I understand my bugfix and so I can decide how to >> apply it on dev. But I can delay the merge or ask someone else to do >> it instead.
> Well, if there is one guy to do the merge of stable into dev and > everyone on the team would send an email request that states which > changes not to merge then this guy will have a hard time.
> In svn the blocking merge is recorded in the system. So whenever > anyone will merge the stable into dev svn will know which changes not > to merge. How does hg/git help in communicating this? Imagine a branch > where many developers do bugfixes and someone will once a week merge > things into dev. How shall he know which changes to skip?
You cannot skip changes -- you really must merge them from stable into dev (really 'default' when using Mercurial). What you can do is to merge-and-ignore:
The last step reverts all files to how they looked on default. You can then re-implement the bugfix the right way and commit the merge.
In my experience, there isn't that many bugfix commits and/or the branches haven't drifted that far away from each other.
The whole workflows obviously builds on the idea that you can normally merge stable into default and benefit from the merge, i.e., that the branches are close enough for this to make sense so that you don't get enormous merge conflicts on every stable->default merge.
We've used this workflow for several years in Mercurial itself and it's very smooth. You can read a bit about it here:
I'll be on vacation for a week, so I'll probably reply very slowly, if at all. You're welcome to ask about this on mercur...@selenic.com where many other guys will be able to talk about this for hours :)
on Mon Apr 02 2012, Martin Geisler <mg-AT-aragost.com> wrote:
>> In svn the blocking merge is recorded in the system. So whenever >> anyone will merge the stable into dev svn will know which changes not >> to merge. How does hg/git help in communicating this? Imagine a branch >> where many developers do bugfixes and someone will once a week merge >> things into dev. How shall he know which changes to skip?
> You cannot skip changes -- you really must merge them from stable into > dev (really 'default' when using Mercurial). What you can do is to > merge-and-ignore: