Ahmad Lashgar, PhD

Home | Resume | Software | Twitter | Blog


Git manual for Mercurial users

November 15, 2020 - Git users are on the rise. Bitbucket removed support for Mercurial in July 2020 and made repositories inaccessible/read-only in August. If you are a Mercurial user, sooner or later you probably want to switch to Git.

I worked with Mercurial for more than two years when I was at Avigilon. Earlier this year I joined MTT Innovation and started learning Git. I documented every difference I noticed between Mercurial and Git. And prepared a cheat sheet document listing what most popular Mercurial commands map to in Git. Let's cut the chase and present my findings to you.

Major Differences

  • What is completely new is the concept of remote and local. In Mercurial, when pulling the repo all remote branches will be linked to local branches. In fact, local and remote are the same. In Mercurial, as long as no new branch is created locally, there is no need to link remote and local branches. In Git, however, once repo is pushed all branches are remote at the global repo. When users clone this branch, they should create local branches and link them to remote branches -- called tracking. Some of the commands we see below are for linking/synching local to remote which are only applicable to Git -- it is automatically handled in Mercurial.
  • To be honest, I do not know what Mercurial branch corresponds to in Git (maybe there is none). However, Git branch is pretty much a moving Mercurial bookmark. Git branch corresponds to one commit. Git branch can be deleted without commits being affected -- leaving the commits so called unnamed head. Branches are created more often in Git. Code review workflow in Git is to create a new branch when making a PR. But in Mercurial, branch keeps a track of long-term product, e.g. a line of release.
  • Git has a concept of "staging". Stage is a state for modified files. Staged files are modified but not committed yet. Stage state is explained as a change snapshot. Further changes to the same file can be done and new changes can be compared against snapshot (staged version). Only staged changes will be committed ultimately. To the best of my knowledge, Mercurial does not have a direct correspondence to stage (there are ways to emulate it though ...)

Mapping between Git & Mercurial

Here comes 20 most common versioning operations in Git and Mercurial. I start with easiests that surprisingly are same across the board and then it escalates. (I assume you have a backup of your repo before playing with these commits):

  1. Let's start with creating a new empty repository in current directory:
    # Mercurial: hg init # Git: git init
  2. Add files and commit:
    # Mercurial: hg add path/to/file1 hg add path/to/file2 hg commit -m "message" # Git: git add path/to/file1 git add path/to/file2 git commit -m "message"
  3. Show history of commits as a graph:
    # Mercurial: hg log --graph # Git: git log --graph
  4. Check what is going out before push (calm fire):
    # Mercurial: hg out # Git: git log --branches --not --remotes
  5. Push current local changes:
    # Mercurial: hg push # Git: git push
  6. Push specific local commits:
    # Mercurial: hg push -B <bookmark> # Git: git push origin <branch-name>
    Important note: when pushing changes in Git after squashing remote commits, use -f to force changes to remote. Use with CAUTION.
  7. Switch repo to another version:
    # Mercurial: hg update <hash> # Git: git checkout <hash>
  8. Find the hash that the repo is currently at:
    # Mercurial: hg id # Git: git rev-parse --short HEAD
  9. OK, moving to some serious stuff. I am going to take responsibility and tell you how to move commit(s) on top of another commit (aka backporting or cherry-picking):
    # Mercurial: hg graft -r <hash-to-move> <hash-of-new-tip> # Git: git rebase <target-branch> <source-branch>
    Here I should add that hg graft clone/replicates the commits on the new tip while git rebase moves the commits.
  10. Track/link local branch to remote:
    # Mercurial: # Sorry, no such concept in Mercurial # Git: git checkout --track origin/<branch-name>
  11. Squash commits from start-hash to tip (last N commits) in local repo:
    # Mercurial: hg histedit -r <start-hash> # Git: git rebase -i <branch-name>~N
    Note that histedit is an extension for Mercurial and must be added to ~/.hgrc before being able to use it.
  12. What if you want to clean up your repository of code no longer needed, then you need strip. Remove a branch starting at <hash>:
    # Mercurial: hg strip <hash> # Git: git branch -d <localBranchName>
    Note that strip is an extension in Mercurial and must be added to ~/.hgrc
  13. Happens often we need to undo the very last commit, so here it goes:
    # Mercurial: hg rollback # Git: git reset --soft HEAD~1
  14. Remove remote branch:
    # Mercurial: # Sorry, no such concept in Mercurial # Git: git push origin --delete remoteBranchName
  15. Unstage changes:
    # Mercurial: # Sorry, no staging concept her # Git: git reset HEAD <file>
  16. Happens often you want to start over as the implementation has pitfalls. Discard local changes and restore back to current commit:
    # Mercurial: hg revert -C <file> # Git: git checkout -- <file>
  17. While not that often in Mercurial, you probably always need to work on a separate branch when you want to make a pull request in Git. Create new branch with new modified files:
    # Mercurial: hg branch <branch-name> hg add <file2> <file2> hg commit -m "message" # Git: git checkout -b <branch-name> git add <file2> <file2> git commit -m "message"
  18. Shleve is a common concept between Git and Mercurial. Keep changes in the Shelve if you want to switch to some other ideas without commiting current changes. Here is how to Shelve local changes:
    # Mercurial: hg shelve --name <name> # Git: git stash save "message"
  19. Restore specific entry from shelve:
    # Mercurial: hg shelve list hg unshelve <name> # Git: git stash list git stash pop <entry-id>
  20. Rename a branch to some proper name because you picked a not-good-enough name at first:
    # Mercurial: # Sorry, branches are permanent # Git: git branch -m <oldname< <newname>

Conclusion

Alright, that's all I had. I hope you find commands above useful when migrating from Mercurial to Git. Reach out if you want to connect or suggest an edit.


Let's connect on