Local branches are really an artifact of Git having a garbage collector. That means that any commit that you want to keep must be attached to a branch so that it may not be inadvertently lost. Without a garbage collector, that is not necessary.
In particular, in many other VCSs, you just simply commit without having to create a branch in the first place. Labeling them (via whichever mechanisms your VCS provides) is typically optional with modern version control systems.
You can argue that you prefer having a garbage collector over having to explicitly delete commits/branches that you do not want any longer, but that would be a different issue.
> 2. Mercurial, without extensions, doesn't have the git shelf.
This is sort of incorrect -- or rather, probably a misunderstanding. Shelve is part of standard Mercurial and has been for a while (same goes for MQ). While it is disabled by default and enabled in the extensions section, it's no more an extension in any meaningful sense of the word than git stash (which technically calls out to a shell script [1]). Mercurial's shelf is a python module [2] that is loaded when you flip a switch.
> Both these points make hg easier to learn (branch == clone is very easy to wrap your head around, commit behaves basically like svn/cvs).
This is really more a feature of Bazaar, not Mercurial.
> In particular, in many other VCSs, you just simply commit without having to create a branch in the first place. Labeling them (via whichever mechanisms your VCS provides) is typically optional with modern version control systems.
Since, for me, local branches are a massive part of why I prefer git, I'd be interested in examples of how to do the same thing in other VCSs.
It doesn't make sense to me that they're a byproduct of having a garbage collector, since (1) git-stash exists, (2) local branches are a common selling point, and (3) git-clean honestly seems tacked on rather than a core piece of the design.
For Mercurial, you create a new local branch by committing at a point that's not a tip (or not HEAD, if there's just a single branch).
By default, it doesn't have a special name (like a git branch does). You can give it a name via "hg bookmark".
The feature set is honestly fairly similar, it's just different in the way it is presented. In git you go into a "detached HEAD" and have to explicitly name a branch or commits will get lost. In hg you commit and it creates a new tip, but it doesn't have a name unless you explicitly name it via bookmark.
> In git you go into a "detached HEAD" and have to explicitly name a branch or commits will get lost.
Maybe I'm missing something, but this seems like a fairly academical scenario.
When would you checkout a specific commit, keep working, keep committing, but not attach it to a name or a branch? How is "67846237864237846" a nicer reference than "bugfix" or whatever?
To me this seems like people using git in a way it's specifically designed not to support and then go complaining when things go wrong.
>When would you checkout a specific commit, keep working, keep committing, but not attach it to a name or a branch? How is "67846237864237846" a nicer reference than "bugfix" or whatever?
You enter a commit message when you make a commit, right? So I am not sure I see your point..
Now Commit 3 is the HEAD of your current branch (master, trunk, default, whatever). The way I understand your "problem" is if you want to create a new branch based on an older, non-HEAD commit.
For instance you checkout the current branch on based on the state of the branch as per "Commit 2", and make a new Commit "Commit 4". This creates a new implicit branch:
- Commit 1 -> Commit 2 +-> Commit 3 (master/default)
+-> Commit 4 (implicit branch) - new current branch
Now you have two branches. How do you switch between them? How do you operate on those branches? Do you cycle them? Do you have to remember the last commit-message of a branch to find it again?
To be absolutely clear: What I'm curious about is how do you relate to and navigate between branches when they are all anonymous? What mechanisms do you have to aid identification?
It is actually pretty simple. Mercurial automatically assigns a _local_, sequential revision number to every commit in your repository clone. In fact those revision numbers almost match the numbers in your examples (Commit 1 would have revision number 0, Commit 2 would be revision number 1, etc).
So in your example you would do:
"hg update 2" to checkout Commit 3, and
"hg update 3" to checkout Commit 4
Note that these revision numbers are _local_ to your repository. That is, another clone of the same repository may have different revision numbers assigned to different revisions. The revision numbers are assigned in the order in which commits have been added to that particular clone.
You could of course also use the revision id (i.e. the SHA1 hash) to identify them. You do not need to use the whole revision id, just the part that makes it unique, usually the first few (e.g. 6-8) characters of the hash.
In addition Mercurial has a very rich mini-language to query and identify revisions in your repository. These queries are called "revsets". Most mercurial commands accept revsets wherever they would need to receive a revision identifier. With it you can identify revisions in many ways such as by date, by belonging to a given branch, commited by a certain author, containing a certain file, etc (and any combination of those and many others).
Finally, if you use a good mercurial GUI (such as TortoiseHg) the whole thing is moot because the GUI will show you the whole DAG and you can just click on the revision that you want and click the "Update" button.
I actually find the ability to create these "anonymous" branches really useful. Naming things is hard so I find that coming up with a name for each new short lived branch is annoying.
Anonymous heads are often short lived. You either merge them right away (Making them not a head), or bookmark it (Making them not anonymous) so that you can come back to it later.
But say, you forget to merge it or didn't bookmark it, Mercurial does not hide the revision from you. The hg log will list it along with all the other commits.
Even easier is to use the hg heads command, which will show all branch heads for you (Branch heads are change sets that have no descendants on the same branch), along with the commit summaries. You can use the commit summary to select the revision and use a revision id (You don't have to use the full length hash, instead you can use a 4 or 5 character prefix of the revision id) to switch to that commit.
I think it's funny how you say hg is much easier and git has a much more complex model, and I'm still struggling to understand all this.
To me Git is clear and simple. It's a linked commit-graph, and branches and tags are just references to leaf nodes on the graph. That's all. That's the entire model.
With hg you have local commit IDs, you have revsets, you have bookmarks, and then since you aren't forced to name a graph/branch you have to remember commit-IDs or who did what when to what file. Some commits show up on the commit-log, but you can't be sure if it's in the current branch. I'm totally confused as to what to use where and how I can be certain about what I'm working with.
I'm not saying hg is bad per se, but looking at it from the outside it looks helluva more complex than Git. I think this all boils down to what you're used to.
From the discussion in this thread here, I'm pretty certain I wont be trying mercurial anytime soon. It just seems too complex and confusing for me ;)
>To me Git is clear and simple. It's a linked commit-graph..
Same in Mercurial. But it lets you store a bit more meta data (Bookmarks, branch names, local revision numbers etc) which enables more powerful queries based on them. I am not sure that is a bad thing. But I can certainly understand why it appears complex to when presented with all these information at once.
But trust me, when you are trying to make sense of what went wrong with a merge done by an in experienced co-worker, you want all the metadata you can get..
>and then since you aren't forced to name a graph/branch you have to remember commit-IDs or who did what when to what file. Some commits show up on the commit-log, but you can't be sure if it's in the current branch.
Now you are talking FUD, no offence.
>you have to remember commit-IDs...
??, I just told you how to manage such cases. Which part you didn't get? In Git you would have to dig up reflogs for the commit hash for a' DETACHED HEAD' that you just made, Right?
>Some commits show up on the commit-log, but you can't be sure if it's in the current branch.
Not sure what you mean by there or where you got that idea. Every commit you make will be displayed by the hg log, regardless of the branch it is on. If you want to filter log by a branch, there is a -b option accepts branch name as an argument, that you can use to limit the display to changesets in that branch only.
>From the discussion in this thread here, I'm pretty certain I wont be trying mercurial anytime soon. It just seems too complex and confusing for me ;)
If Mercurial was actually 'complex and confusing' it would have been dead a long time ago (Thanks to git). The simple fact that it survives (and even grows in popularity) despite git, IMHO is a sure sign of how simple/straightforward it actually is for common things. So do yourself a favor and try it sometimes. You just might like it.
> Maybe I'm missing something, but this seems like a fairly academical scenario.
No, you aren't missing anything. I merely used the example to illustrate the difference between the two VCS's. Like I said: they actually have a fairly equivalent set of features. The prime user-visible difference is in interface and implementation.
> Since, for me, local branches are a massive part of why I prefer git, I'd be interested in examples of how to do the same thing in other VCSs.
You simply commit a new changeset based on the desired parent. The branch would be created implicitly by having a revision with two or more children. It does not have to carry a name; naming revisions (or sets of revisions) is an orthogonal concept for the purpose of navigating the revision DAG [1], whereas in Git branches have the additional purpose of keeping revisions from being GCed. Any revision that is not referenced by a ref (directly or indirectly) will eventually be GCed (once it is removed from the reflog and the grace period has expired).
> It doesn't make sense to me that they're a byproduct of having a garbage collector
It is a byproduct. Because if you did the above in Git (which Git won't let you without being explicit about it, but it's possible), then one or the other child would get GCed sooner or later. That's because there's only one HEAD commit, and that can prevent only one child from being GCed, so you have to create a ref to the other (generally via a branch) to keep it alive.
> since (1) git-stash exists [...] and (3) git-clean honestly seems tacked on rather than a core piece of the design.
This has nothing to do with git stash or git clean. I'm talking about one of Git's architectural principles, where the repository is essentially a purely [2] functional data structure and revisions that are not reachable from one of the roots (refs/branches) are subject to garbage collection.
[1] And if you look (aside from Mercurial) at Bazaar, Fossil, Monotone, or Veracity, you'll find that there are multiple ways to go about that.
> You simply commit a new changeset based on the desired parent. The branch would be created implicitly by having a revision with two or more children.
And then, how do you later track these commits? How do you change or merge branches if they have no names? Do you go around remembering revision-IDs?
I'm not trying to be judgemental and say this is wrong or anything, but I'm curious how this works out in practice.
First, let me note that neither Git nor Mercurial is my preferred VCS, so you'd ideally want to ask somebody else about common practices (my own preferences are naturally influenced by other VCSs).
My own preferred approach is to use hg share, which allows me to have multiple checkouts of the same repository, each with a different current revision (HEAD in Git parlance). This is because when I'm doing some local branching, I generally like to have two different sets of files to work with (so as not having to rebuild when switching back and forth, being able to easily run side-by-side comparisons, etc.). This is sort of like Git's git-new-workdir, except that git-new-workdir isn't safe; it is also the default mode of operation of pretty much any DVCS other than Git and Mercurial. This, I note, is not necessarily what the majority of Mercurial users do -- especially since this feature first has to be switched on --, but is one of the major reasons why, if I have to pick either Git or Mercurial, I'll generally go with Mercurial. Multiple checkouts are simply too important a feature for me to want to deal with Git's workarounds; Mercurial's support of them is imperfect, but still better than what Git has.
More commonly, in Mercurial you will simply not need to name temporary branches. You will have named branches for your major features etc., and you can use hg heads -r <branch> to list the 1-3 heads you may at any time have per major branch. This makes naming the heads sort of superfluous for typical development. Also, if you have only two heads in a branch, hg merge and hg rebase will automatically pick the other head to merge/rebase against.
Alternatively, you can use bookmarks. Bookmarks are essentially like Git branches in that they are pointers to commits.
Finally, there's also nothing inherently wrong with simply using named branches, even for temporary work. It's up to you or your organization whether you want persistent or temporary names for temporary work.
>whereas in Git branches have the additional purpose of keeping revisions from being GCed..
Not only that, but it is also used to decide what all commits to show to the user. In that way branch names act as little windows through which you can look at the DAG of history. Commits that you cannot see through this window does not really belong in the history according to git, and will be GC'ed.
So if you make a commit on top of a revision that is not a branch head, then it becomes a DETACHED HEAD. Actually there is nothing 'detached' about it. It is linked to the parent commit just fine. But git just does not consider it part of the history unless you give it a name. Here you can see the problem with git naming. 'DETACHED HEAD'. If you understand a head revision as a last revision in a sequence of connected commits, how can there be a 'DETACHED HEAD' when a 'HEAD', by definition is part of a sequence of commits..
I wonder why someone would call it a 'DETACHED HEAD' instead of something like 'UNNAMED HEAD' or 'ANONYMOUS HEAD'....
Local branches are really an artifact of Git having a garbage collector. That means that any commit that you want to keep must be attached to a branch so that it may not be inadvertently lost. Without a garbage collector, that is not necessary.
In particular, in many other VCSs, you just simply commit without having to create a branch in the first place. Labeling them (via whichever mechanisms your VCS provides) is typically optional with modern version control systems.
You can argue that you prefer having a garbage collector over having to explicitly delete commits/branches that you do not want any longer, but that would be a different issue.
> 2. Mercurial, without extensions, doesn't have the git shelf.
This is sort of incorrect -- or rather, probably a misunderstanding. Shelve is part of standard Mercurial and has been for a while (same goes for MQ). While it is disabled by default and enabled in the extensions section, it's no more an extension in any meaningful sense of the word than git stash (which technically calls out to a shell script [1]). Mercurial's shelf is a python module [2] that is loaded when you flip a switch.
> Both these points make hg easier to learn (branch == clone is very easy to wrap your head around, commit behaves basically like svn/cvs).
This is really more a feature of Bazaar, not Mercurial.
[1] https://github.com/git/git/blob/master/git-stash.sh
[2] http://selenic.com/repo/hg-stable/file/default/hgext/shelve....