Lately I'm playing with Mercurial (hg) and Git but I couldn't tell the difference between the two. Actually it is quite thin but noticable. Here's a comparison of hg vs git and my conclusion
Sorry for the format but these are notes I took while playing with hg and git. I 'm too lazy to format them in html ;)
# this comparison assume that you have
# a ~/.gitconfig and a ~/.hgrc configured
# with your username (otherwise you need to
# add your username at every commit)
mkdir /tmp/scm-tests
cd /tmp/scm-tests
# create a central repo
mkdir [scm]-central
git:
git init --bare git-central
# bare: you need to look at the man page to
# find this - needed otherwise you not be
# able to do push (you will need to go to central and pull)
hg:
hg init hg-central
# on the contrary hg assume by default
# that you can push (might be seen as
# a security issue - that doesn't bother me)
# user1 clone central
mkdir [scm]-user1
git:
git clone /tmp/scm-tests/git-central/ git-user1/
# you will get a 'warning: You appear to
# have cloned an empty repository.' I don't
# understand why they putted that ?! what's the problem here ?
hg:
hg clone /tmp/scm-tests/hg-central/ hg-user1/
# add and commit 2 initial files
$ cd [scm]-user1
$ ls
OTHER.TXT README.TXT
$ cat OTHER.TXT
other file
$ cat README.TXT
readme file
git:
git add README.TXT OTHER.TXT
git commit -a -m "user1 modif1"
# using the 'a' option or not
# could be a post on itself
hg:
hg add README.TXT OTHER.TXT
hg commit -m "user1 modif1"
# pushing modification to central repo
git:
git push origin master
# here you get some obscure
# message in the output like
# 'Delta compression using up to 2 threads'
hg:
hg push default
# creating user2 and cloning central
$ cd /tmp/scm-tests
mkdir [scm]-user2
git:
git clone /tmp/scm-tests/git-central/ git-user2/
hg:
hg clone /tmp/scm-tests/hg-central/ hg-user2/
# - - - - - - - - - - - - - - - - - - - -
# concurrent modification of different files
# - - - - - - - - - - - - - - - - - - - -
modify [scm]-user1/README.TXT
modify [scm]-user2/OTHER.TXT
git:
cd git-user1
git commit -a -m "user1 modif"
cd ../git-user2
git commit -a -m "user2 modif"
hg:
cd git-user1
hg commit -m "user1 modif"
cd ../git-user2
hg commit -m "user2 modif"
# push user2 modif to central
git:
git push origin
hg:
hg push default
# pull from user1
cd ../[scm]-user1
git:
git pull origin master
# I always have a doubt if it's
# origin master or the opposite
hg:
hg pull default
# notice that you don't need to tell
# hg where you want to send the changes
# it assume it's the current dir
...
(run 'hg heads' to see heads, 'hg merge' to merge)
# this message is important because it means you
# have to issue 2 extra command to merge otherwise you can't push
# the merge will be easy but in this case git just does it !
hg merge # we don't specify any revision,
# hg is smart enough to find the right one
hg commit -m "Merge" # by default in tortoisehg the
# commit message is already configured - don't know if it's
# a tortoisehg feature...
# push user1 modif to central
git:
git push origin
hg:
hg push default
# pull user1 modif in user2 folder
cd ../[scm]-user2
git:
git pull origin master
hg:
hg pull default
...
(run 'hg update' to get a working copy)
# again this message is important, hg fetched the central
# repo info, but didn't moved you automatically on the latest revision
# we could have done this by using the -update option,
# but I wanted to show you this situation
hg update # another extra command compare to git
# diplay graph of what we made
git:
git log --graph --color # notice the commit 'Merge branch...' was done
# automatically, we did nothing (except push and pull)
* commit 817f6e11a14530c6b09502edd069895ab19d8aea
|\ Merge: b0647d4 c0a4f25
| |
| | Merge branch 'master' of /tmp/scm-tests/git-central
| |
| * commit c0a4f259c33f8d8876088b08703776bb2739bb4a
| |
| | user2 modif
| |
* | commit b0647d415b30a407f0367ff96e5a417956d83d93
|/
|
| user1 modif
|
* commit 6ffe857f8ece5ea7df051975b36c4384bdbe1383
hg: # didn't found any text based graphical tool
# at first but apparently there is an
# extension off by default:
# http://mercurial.selenic.com/wiki/GraphlogExtension
hg log
hangeset: 3:2e322ec563c5
tag: tip
parent: 2:995f574cea3d
parent: 1:4d258b644a75
summary: Merge
changeset: 2:995f574cea3d
parent: 0:a3ebb2490ce5
summary: user1 modif
changeset: 1:4d258b644a75
summary: user2 modif
changeset: 0:a3ebb2490ce5
summary: user1 modif1
# git and hg: same number of revisions
# - - - - - - - - - - - - - - - - - - - -
# concurrent modification of the same file (with conflict)
# - - - - - - - - - - - - - - - - - - - -
modify [scm]-user1/README.TXT
modify [scm]-user2/README.TXT
the same line
commit code omitted
# push user1 to central
git:
git push origin
hg:
hg push default
# pull central to user2
cd ../[scm]-user2
git:
git pull origin master
...
Auto-merging README.TXT
CONFLICT (content): Merge conflict in README.TXT
Automatic merge failed; fix conflicts and then commit the result.
hg:
hg pull default
...
(run 'hg heads' to see heads, 'hg merge' to merge)
hg merge # extra command
merging README.TXT
warning: conflicts during merge.
merging README.TXT failed!
...
use 'hg resolve' to retry unresolved file merges or 'hg update -C' to abandon
# fix conflict by editing file
vi README.TXT
# mark file as merged
git:
git add README.TXT
git commit -a -m "fixing conflict"
hg:
hg resolve -m README.TXT # this m option was hard to find
hg commit -m "fixing conflict"
rm README.TXT.orig # I don't know if it's me not
# using hg properly or hg behavior but we need to
# cleanup manualy some backup file :(
# push user2 conflict fix to central
git:
git push origin
hg:
hg push default
# update user1
cd ../[scm]-user1
git:
git pull origin master
hg:
hg pull default
...
run 'hg update' to get a working copy)
hg update # extra command
# diplay graph of what we made
git:
git log --graph --color
# notice that this time there is no automatic merge message but ours
* commit 58e0ec598f272ac5d4dff02b7a9d0e3842a08c81
|\ Merge: 7c4b4dc e176f07
| |
| | fixing conflict
| |
| * commit e176f07da2db925528c7a068af5f558484af6f56
| |
| | user1 modif conflict
| |
* | commit 7c4b4dc8cbbb49138312df41fe19f2b2dee90d2b
|/
|
| user2 modif conflict
|
* commit 817f6e11a14530c6b09502edd069895ab19d8aea
...
hg:
hg log
changeset: 6:6f8dceccba2d
tag: tip
parent: 5:32085fee1b07
parent: 4:7417be83b483
summary: fixing conflict
changeset: 5:32085fee1b07
parent: 3:2e322ec563c5
summary: user2 modif2
changeset: 4:7417be83b483
summary: user1 modif2
changeset: 3:2e322ec563c5
...
# git and hg: same number of revision: 3
# - - - - - - - - - - - - - - - - - - - -
# conclusion
# - - - - - - - - - - - - - - - - - - - -
learning curve (if you already know svn):
git: not easy, need regular practice and at least
one week to become familiar with command line args
hg: 1 day, just feels like svn
commands:
git: 21 commands with (obscure) command line arguments
hg: 50 (obvious) commands
branches:
git: merging from branch is done with one command line
hg: you need to type extra (useless) command to merge in obvious (non conflicting changes) case.
Apparently the extra (useless) commands behavior can be change by tuning some conf parameter:
http://hgbook.red-bean.com/read/a-tour-of-mercurial-merging-work.html#sec:tour-merge:fetch
vocabulary:
git: origin
hg: default
windows support:
git: lame
hg: good (tortoisehg)
linux support:
git: good (git help are man pages)
hg: good enough
merge conflict:
git: easy
hg: less obvious / elegant
me:
I like both
hg: because it's so easy to use
git: for it's elegant way of handling merge and branches