Wednesday, March 9, 2011

Mercurial and Git cheatsheet

We are using Git and Mercurial for many different projects. Apart of suffering often of confusion in command usage, I am still trying to figure out tricks and understand advanced features. here I will try to keep a cheatsheet uptodate. Corrections are welcome, as well as comments about how to fill in the gaps. Whatever comes in and makes sense, I will update the cheatsheet.

In this I am not trying to show how that git and hg are the same. I only want to show how common tasks could be done in the two versioning systems. Please be aware that (as some commented) this might create confusion, since there is no 100% same way to do things and branches, tags and whatever do not always have the same meaning in both worlds. So please be careful. I find the cheatsheet useful, so might others

Discussion ongoing in the comments section.


--- update 1: Wed Mar 9 11:22:54 CET 2011

7 comments:

Anonymous said...

I hate to say this, but the cheatsheet in its current version is really wrong on many levels. It will confuse the hell out of people.

It seems to me like you are mostly a git-user with less hg knowledge, judging by the mix-up of concepts you depicted there.

It would take a very long post to correct everthing (almost resulting in a new cheatsheet), so let me just give you some quick pointers:

1. "tip" in hg is just the chronologically last commit in the repository. It is nowhere near a branch in git, especially not "master". If you search for a somewhat equivalent hg concept for "master", it would be "default".
2. Branches in git are completely different from branches in hg. If you compare branch operations in both systems, you'd have to use the bookmark-concept in hg. hg's branches have no counter-part in git, btw...
3. Revision ranges in hg "r1:r2" do not match to git's "r1..r2". hg's equivalent is called "revset" and uses the same syntax for topological ranges. It would be "hg log -r 'r1..r2'", then, but only with newer version supporting revsets.
4. Tags in hg are stored in history and cause commits (except local tags created with -l option). Git tags are not stored in history by default, they exist outside of the DAG.
5. Git's index functionality is a subset of hg's queues ("mq"). The work-modes are really different. Be careful if you try to map a index operation (diffing, adding, etc.) to a hg command, most of the time it will come out wrong.
6. Remotes are not paths and vice versa. The remote concept is so much more than hg's paths.
7. Stashing is called shelving in hg, there is a extension for that.

moovida said...

thanks for your comment anon, I really appreciate it. I feel sad that the thing looks that wrong to you and I am willing to accept the critique. As you might have seen, I put your comment as a warning in the post.

Funny thing is that I use git in only one single project and mercurial on many. That should make me worry even more. The truth is, that it started from a git perspective, to help me learn it.

I am not trying to find equivalent commands anyways, I am trying to similar things in both worlds.

Thanks for the pointers, appreciate them.

Comments on the pointers:

1) Ok, so we should have
master and HEAD vs. default and tip

I can't find where I write in the sheet that tip is master though. I think I used the above notation?

2) My try was to work without extentions in hg. You feel it is really that wrong to describe operational similar operations?

3) Why do you say that. Excuse my ignorance, but to me the r1..r2 has the same result as r1:r2

4) yes, I agree about that. But then how would you write that down if you compare git and mercurial? Suggestions welcome. Ok, I should add the need to push up git tags and remove them remotely?

5) Yes, which is why I left out the third example of diffing. But the others should be right? Again, I didn't want to call extensions in the loop.

6) Ok, but you can pul and push on both the same way.

7) Thanks, that is one extension I will want to use.


I am a bit puzzled about how to go on in this. I want to create a page on which to compare siilar commands in git and mercurial. But last thing I want to do is confuse peopel and myself...

























Thanks for the pointers, I

Anonymous said...

Part 1:

I apologize if I came through as arrogant in the previous post regarding the cheatsheet being "all wrong". It was not meant that way, the thing is just that I often have to deal with DVCS users that use similar cheatsheets in the hope to have a nice recipe for use-cases, only to run into walls after some time, blaming whatever tool the know the least (be it git or hg). This is what I mean with confusing.

Your intention with this is noble, but IMHO it is really important to define the use-cases behind the actions describing a git-hg command-match.

E.g. take the first entry "name of the default head"... what do you mean with that? "The name the appropriate system uses to reference the latest commit on a given branch, specifically the branch the system uses as default." is my guess here. In this case, the hg part is wrong, because "tip" always references the last commit in the repository.
You can have more than one branch in an hg repository, so there will be situations where "tip" is not the latest commit of the branch that hg uses as default (coincidentally it is called "default" in hg - would be "master" in git). If my guess about your definition was right, the right description in the hg column would be "default (last commit == default)".

> Funny thing is that I use git in only one single project and
> mercurial on many. That should make me worry even more. The truth
> is, that it started from a git perspective, to help me learn it.

I see. Then that's why the action description is mostly using git terminology. But there is no reason to worry, it just shows that hg is a bit more forgiving regarding "knowledge down to the bit".

Anonymous said...

Part 2:

On your answers:
> 1) Ok, so we should have master and HEAD vs. default and tip
>
> I can't find where I write in the sheet that tip is master
> though. I think I used the above notation?

Already answered that in the example above. I agree, I can't remember how you wrote it exactly, so forgive me if I was jumping the gun here. I just wanted to point out that "tip" in hg is not related to one single branch only.

> 2) My try was to work without extentions in hg. You feel it is
> really that wrong to describe operational similar operations?

In the way you did it, it gives a false feeling of safety IMHO. In the category of "Ah, hg branch is just git branch, so lets make a feature branch called 'mytest' here." If the git-user does that, he will later complain that hg is wrong to not have a simple way to remove a "branch". If the hg-user does that, he will later complain that git is suddenly not showing from which branch the commits came.

> 3) Why do you say that. Excuse my ignorance, but to me the r1..r2
> has the same result as r1:r2

It is certainly not ignorance on your part, it is a conceptual shortcoming of hg (already improved by the revsets). r1:r2 will not show the topological range, but the pure chronological range, meaning that if you have 2 branches, chances are that you will see revisions from both branches, even if r1 and r2 are revisions from only one branch. In git, r1..r2 is a shorthand for "all commits reachable from r2, but not from r1". So you see, git's elipsis is more than a simple range, it is a DAG set operation, like hg's revsets are. Come to think, hg's equivalent of git's r1..r2 would be more like "ancestors(r2) - ancestors(r1). Only in the most often used sense of "commits between this and that on my branch", it is equivalent to "r1..r2" in hg's revsets.

> 4) yes, I agree about that. But then how would you write that down
> if you compare git and mercurial? Suggestions welcome.
> Ok, I should add the need to push up git tags and remove them
> remotely?

IMO, hg users would be confused about git's tag push/pull-need and lack of history ("who renamed that tag when why?"), whereas git users would be confused why a "hg tag" is creating a new commit, and more important, why the heck there is a conflict in a .hgtags file on merge.

My suggestion would be to note the above and give "hg tag -l" as match to "git tag", with the note that you can not push/pull local hg tags. The concepts are just different.

Anonymous said...

Part 3:

5) Yes, which is why I left out the third example of diffing. But
> the others should be right? Again, I didn't want to call
> extensions in the loop.

Well, "hg diff" always diffs the working copy against the repository. Git does it against the staging area. It is a difference and the reason why git-fans count the index as plus against hg. And this is wrong.

In hg, you can use MQ to create a patch, and "qrefresh" things into this patch. Every diff will show you changes of working-copy w.r.t. the patch. Every time you "qrefresh", you change that patch again, just as you can in git with the staging area. If you feel your patch being ready (or in git terminology your index being ready), you qfinish it (or commit it in git).
This would be a rough equivalence of git and hg commit workflows. Thus, the "hg diff" is not the same as "git diff", more like "git diff HEAD".

> 6) Ok, but you can pul and push on both the same way.

No. Not even that. hg always transmits all missing revisions (on all branches) on "hg push" and receives all missing revisions (on all branches) on "hg pull". You have to use the "-r" option to specifiy a specific revision up to which you want to pull or push.
git just pushes the current branch to the branches origin and fetches(!) all remote trackings. This is where remotes are different to paths. Paths are just aliases for URLs, remotes can contain branch trackings and some more information like what to push, how to push etc.


Please don't get me wrong, I'm certainly not in the position to teach git and hg thoroughly. I'm just the casual nitpicker pointing out what is wrong in his opinion. ;)

Anonymous said...

Part 3:

5) Yes, which is why I left out the third example of diffing. But
> the others should be right? Again, I didn't want to call
> extensions in the loop.

Well, "hg diff" always diffs the working copy against the repository. Git does it against the staging area. It is a difference and the reason why git-fans count the index as plus against hg. And this is wrong.

In hg, you can use MQ to create a patch, and "qrefresh" things into this patch. Every diff will show you changes of working-copy w.r.t. the patch. Every time you "qrefresh", you change that patch again, just as you can in git with the staging area. If you feel your patch being ready (or in git terminology your index being ready), you qfinish it (or commit it in git).
This would be a rough equivalence of git and hg commit workflows. Thus, the "hg diff" is not the same as "git diff", more like "git diff HEAD".

> 6) Ok, but you can pul and push on both the same way.

No. Not even that. hg always transmits all missing revisions (on all branches) on "hg push" and receives all missing revisions (on all branches) on "hg pull". You have to use the "-r" option to specifiy a specific revision up to which you want to pull or push.
git just pushes the current branch to the branches origin and fetches(!) all remote trackings. This is where remotes are different to paths. Paths are just aliases for URLs, remotes can contain branch trackings and some more information like what to push, how to push etc.


Please don't get me wrong, I'm certainly not in the position to teach git and hg thoroughly. I'm just the casual nitpicker pointing out what is wrong in his opinion. ;)

moovida said...

Hi Anon. Your comment never sounded harsh to me. I'm a big fan of people that criticise in polite way and even take the time to give good explanations. So your first post (and much more your following explanations :)) are very welcome. they make me note how superficially I know certain parts of the game.
I want to take time to first get through your comments properly and then try to integrate a bit more of your defined concepts into the cheatsheet. I have still to understand how to have them graphically fast in the view but also not always in the middle.

I appreciate your help and would also appreciate if you could give the thing a look again when it is updated. Sure I would also accept layout ssuggestions.

Thanks.