In "bookmarks are not enough", the author claims that that the commits just stay there after deleting the bookmark - the horror! Actually, the same thing happens in git. Commit unreachable via names will stay around until you run garbage collection. You can still see them in reflog.
"Anonymous heads" - yes, that exactly why `hg push` warns you. Exactly the same reason rebase warns you that it's destructive and pull rebase isn't the default. So you don't make a mess by accident. If you want to see what's available in the remote repository, like after `git fetch`, you can see the new commits via `hg incoming`.
I can't agree with "Nothing is lost" either - the tree he presents is trivial. Give it a go on a repository with 10+ topic branches which are re-synched to trunk a number of times and with more than 2 commits. Suddenly getting everything labelled with branch names is much more useful.
I'm not against one system or another, but having a fair explanation of both systems would be nicer than a blog saying pretty much "it's different than git, therefore it's bad". In the end it's down to personal preferences. Some people find "branch labels" more comfortable to work with, some prefer "branch bookmarks". That's it. Yet every once it a while we get a long "but this is so much worse than git!" explanation. Great, I'll keep using hg because I like it better, thank you very much.
This goes a long way towards explaining some of the things I don't like about Mercurial but haven't ever fully understood. I didn't even realize the git and mercurial concepts of branches were different!
me neither, but now I understand why some discourage the concept of creating many tiny branches in hg. [you can't delete them -- i still find this hard to believe]
I've never really understood the way that you can rewrite history in Git. It seems really odd to me - completely counter to the idea of having a repository in the first place.
From the article:
That is why git branches are so useful; you can do
absolutely anything that you want with them. When you
are done with the ‘quick-fix’ branch, you can just
remove it, and nobody has to know it existed
That actually sounds quite horrible. What if you make a mistake and remove a branch that you didn't want to remove? What if you remove the branch and nobody has any idea where change 'x' came from or why?
> What if you make a mistake and remove a branch that you didn't want to remove?
In Git, a branch is just another name, like an alias, for a specific commit. Think of it like DNS, where the branch is the domain name and the commit is the IP address.
'quick-fix' => d34db33f
When you commit to a branch, it just updates the name to point to the new commit. The new commit points to the old commit, that one points to its previous one, etc., until you reach the beginning. It's a linked list.
When you 'delete' a branch, you're just deleting the alias named 'quick-fix'. You're not deleting the commit, d34db33f, or any of the other commits that it points to (assuming that d34db33f is still pointed to by other commits, i.e., you merged it into master or another long-lived branch). You can get it back again at any time:
git branch quick-fix d34db33f
This will create a new branch named 'quick-fix' that points to the commit d3adb33f - the same commit the previously deleted 'quick-fix' branch was pointing to - which effectively restores the branch exactly as it was before it was deleted.
A merge is just like any other commit, except it points to two parent commits instead of one.
I usually suggest to new git users that if they're really afraid of losing track of old branches, they can make them into tags after deleting. A tag is really just a read-only branch; it's an alias for a commit that you can't add new commits to. Once the user gets more comfortable with git, they usually don't feel the need to do this any more (since, as above, you can always just get the branch back by looking at the history - in practice, you almost never need to do this).
> What if you make a mistake and remove a branch that you didn't want to remove?
Threre are three cascading safeguards:
* you cannot remove the current branch, you gotta check out some other branch. Thus you get to know what you leave behind and what you get into.
* if you try to delete a branch not yet merged into another one (i.e., your changes would be lost), Git warns you and asks to use a different switch (-D rather than -d),
* and the last line of defense: in normal configuration, Git stores history of all operations on HEAD (whatever is the current branch) in .git/logs/HEAD. If you remove or lose any branch or changeset you were working on by a mistake, you can easily find it in the log and git-checkout it (or git-cherry-pick or git-merge).
At any rate, you can easily push your temporary branch to some other repo once in a while -- to have it thus backed up . You can easily remove it from the other repo once you are done.
> What if you remove the branch and nobody has any idea where change 'x' came from or why?
I describe that (the sense of changes) in the commit message.
In my experience, branch names are somewhat ephemeral. I really don't want to carry baggage of 4 years of small featur branch into the future. Even the `big, official' branchech change ovre time; whatever is `devel' today, will be `stable' next week and `legacy' the next quarter. Keeping all that baggage in a long-running project would be distracting.
While I agree with those safeguards, the big disadvantage (particularly when working with a large group of people) is that they are all defeatable by a stupid / uninformed team member, particularly if it isn't noticed for a couple of months.
Of course you can argue that we shouldn't work with stupid / uninformed people, but often it is unavoidable.
That's the beauty of a distributed VCS. If my team member deletes every commit inside his master and tries to force push that to GitHub, either by accident or by malice, I have the true history for my master sitting on my local machine. I can then revoke his push access to our upstream, re-push the true master history I have a copy of, and then write him up for being stupid and/or uninformed. It doesn't matter if he was able to do that in HG or Git.
If no one notices that such a thing happened for a couple of months, then we need to use better code review practices. Git won't prevent humans from making mistakes. Git will, however, complain if I try to push my master to the upstream's master and the histories don't match (if someone else deleted a previously merged commit), which can at least tip me off to something fishy. Then, I can either force push, and then tell all my other co-workers that the history has changed, or I can revert the destructive commit and carry on.
> I've never really understood the way that you can rewrite history in Git. It seems really odd to me - completely counter to the idea of having a repository in the first place.
Git is data-centric. It matters less how you used git to produce the final result (string of changesets), what matters is the changesets themselves. This comes out completely naturally since it's an evolution of Linux' patchstack development model.
That's an interesting viewpoint, which explains some of the problems I have been having with git.
I can see that git is a very natural way of storing, modifying and passing around change sets, to a range of people.
I previously used svn more for a way of 'recording all of history', and it annoys me that git really feels like it fights against this viewpoint. I can see why, as recording all of history makes passing around, changing and merging changesets more a pain.
Don't really have a solution to this. Might try hg, as it might fit better with my mental model.
The biggest difference is in defaults. Hg defaults are more applicable to smaller teams while Git's are towards larger ones. (You can always apply extra flags to get the desired behaviour in either case.)
For example with tags a `git push` does not send them to the other end. If 1,000 people are cooperating then you don't want to see everyone's tags. (`git push --tags` will send them.)
> What if you remove the branch and nobody has any idea where change 'x' came from or why?
If you want to make it clear that the change was from a branch, you can merge with --no-ff and you'll have a merge commit.
What you call 'rewriting history' is just moving pointers around a tree. Git maintains a non-linear history of development, entirely orthogonal to the concept of branches.
This means that for branches with significant amounts of work, you can get a good idea of why there it diverged from master just by inspecting the history.
A pointer pointing to the last commit before merge is nice to have, but you don't lose much when you delete it.
The history for every change in existing branches is recorded - if you remove a branch you either want to discard whatever changes where there or you merged it into another one and don't need it tagged as separate branch.
Commits in git are reference counted - the ones that have no references to them are removed, (and having a branch or other commit pointing to yours both count as references) and the ones that can be reached from tops of branches or tags are kept.
There seems to be a philosophical difference between the communities.
Hg is about replicating a single repository, while git is about a distributed system of repositories. Git embraces the fact, that every participant has full control about his data and assumes you want to publish cleaned up patch sets instead of all of your honest messy history.
You're removing the branch, not the whole commit. They can still see where the change came from by looking at the commit history and message. But they don't need to know how you locally organized your own self when you did it.
I could. And I have. And I do. But I know what I'm doing with them :)
My point was that patch queues are more for the kind of workflow that might be experimental in nature, that you might not want to land on the mainline for a while (if ever).
With git, you can have more than one experimental branch going at once, with more than one commit in each, and switch between them at will. I don't know how to do that with mercurial. It might be possible with a version controlled queue, but if it is I don't know the cli calls to make it happen.
At this point, I've got one experiment going with A and B, and another with C and D, both branching from the same master commit. My understanding of mq is that I could have either experiment_1 or experiment_2, but not both. Admittedly it's a situation which doesn't come up often, but it's just often enough that I'd be intrigued if hg could do the same.
I do that by pushing, popping and reordering patches.
For example right now in my queue I have a patch to disable http throttling on our main app which will never be checked in, a patch for a major refactoring of the data layer that might not fly, but the work is more significant than a couple days work, an upgrade to a new version of the sdk in another patch, a re-working of the docs patch etc.... I qpush whatever I want to work on between bug fixes which go directly to the development branch.
so something like....
hg qpush --move nothrottle.patch
hg qrfresh # repeat etc...
hg qpop # unapply patch from working directory
hg qpush --move membership_schema_refactor.patch
hg qrefresh # repeat etc....
hg qpop #unapply I haz bug to fix
hg commit -m "fix this bug"
hg push && hg pull && hg update development # do the sync dance
hg qpush --move membership_schema_refactor.patch
hg qfinish -a # change applied patches to changeset
Between the popping in pushing, I can switch to whichever branch revision I want assuming whichever patch applies cleanly.
Not saying it's the same as the hypothetical workflow in git, but it is how I handle multiple seemingly unrelated lines of development for better or worse. Hell of a lot more flexible than what I would have to do in perforce before they added shelving etc.... :)
It does seem that mercurial is encouraging a workflow which is basically "get everything into one patch before committing it" whereas git says "take as many commits as you want to get it right, you can squash it later if you want."
based on my example, yeah I can see where you would think that.
But that example wasn't meant to be the definitive be all end all hg workflow. just an example of how one person(me) keeps multiple lines of unrelated development straight in their head.
Changesets can be converted to patches, patches can be folded into patches, series of one or more patches can be converted to one or more changesets. IMO, if there's any edge case that all that doesn't provide, I probably don't need it.
Hard to believe, because it's not correct. Actually they can hidden via `hg ci --close-branch` (still available, but doesn't appear in listings), or removed completely by running `hg strip "branch(branch_name)"` on it.
I think for picking one that is fair, but I'd enjoy a good breakdown on how to think about each on their own so I don't try to use git like hg or vice versa.
I use both, so obviously I enjoy a good breakdown as well(often switching between the 2 several times a day).
And yeah, don't use hg like git and dont use git like hg unless you want pain.
But the author of this post comes off as an annoying zealot. So it is helpful to "keep it real" by recognizing that they are both tools, so in the end as the site says "who the fuck cares?", and master debators blog posts about the supposed "better" or "worse" is just stupid.
The information is good, this authors choice of delivery is annoying. The "vc tool war" if there ever was one was centralized vs distributed, and we the developers won, now let's all go build something awesome.
> When you are done with the ‘quick-fix’ branch, you can just remove it, and nobody has to know it existed
Well, I actually want to know about branches. If someone finishes work on some branch, he'll just close it after merge. Also, "hg branch" command will sort out non-merged branches from merged. This is so handy.
One thing that bugs me in git branches is that only gitk shows branches (in git's strange way, when topmost commit has tons of branches assigned to it), while in mercurial nearly all clients show branch info beside commit, since it's a core feature and it's stored in commit.
I am now working with git for >6 month (after >1 year work with hg) and every time I feel some mess with branches/merges it feels very uncomfortable.
So, for non-huge projects, hg branches are super-great, imo.
In "bookmarks are not enough", the author claims that that the commits just stay there after deleting the bookmark - the horror! Actually, the same thing happens in git. Commit unreachable via names will stay around until you run garbage collection. You can still see them in reflog.
"Anonymous heads" - yes, that exactly why `hg push` warns you. Exactly the same reason rebase warns you that it's destructive and pull rebase isn't the default. So you don't make a mess by accident. If you want to see what's available in the remote repository, like after `git fetch`, you can see the new commits via `hg incoming`.
I can't agree with "Nothing is lost" either - the tree he presents is trivial. Give it a go on a repository with 10+ topic branches which are re-synched to trunk a number of times and with more than 2 commits. Suddenly getting everything labelled with branch names is much more useful.
I'm not against one system or another, but having a fair explanation of both systems would be nicer than a blog saying pretty much "it's different than git, therefore it's bad". In the end it's down to personal preferences. Some people find "branch labels" more comfortable to work with, some prefer "branch bookmarks". That's it. Yet every once it a while we get a long "but this is so much worse than git!" explanation. Great, I'll keep using hg because I like it better, thank you very much.