Mercurial

Git Churn

I’m still really on the fence regarding the flame war between Git and Mercurial; they both have their strengths and weaknesses. (In case you’re wondering, the reason I haven’t been forced to make a decision yet is because $WORK uses Perforce .) When I learn about one trick in one of these tools, I try to see if that trick is possible in the other. So far this search has been pretty limited, and both applications seem to be equally capable.

My buddy OJ pointed me out to a Mercurial extension called churn, which is a pretty nifty tool. It allows you to quickly see who made how many changes to either a particular file or an entire repo (emails removed to avoid these people being spammed).

guido 1970354 *******************************************************************
jack.jansen 1665771 *********************************************************
solipsis 1588311 ******************************************************
georg 1331332 *********************************************
martin 1005541 **********************************
benjamin 697038 ************************
thomas 460885 ****************
christian 445461 ***************
fdrake 437229 ***************
tools 418957 **************

After finding out about this cool little feature I decided to see if Git had something similar. After a little digging around I came across this thread on StackOverflow(2) which talked about the Git shortlog command. Running this command with the “-sne” argument (as given from the StackOverflow page) on my Project Euler git repo returned these results:

129 Bryce Verdier
42 Bryce Verdier
4 Bryce
1 bryce

While there are mentions of the gitstats project, I didn’t want to bring that into the equation. I just want to compare the core applications. Using a third party application, like gitstats, wouldn’t be fair to Mercurial.

Seeing how both of these tools seemed to have similar capabilities, I decided to investigate a little more. I started off by trying to see if there might have been a flag to remove the stars from the Mercurial churn output; while the stars are cool, I think the Git output looks cleaner without them. Sadly, there isn’t a flag in churn to remove the stars.

One thing I did like about churn over Git shortlog was the ability to see either the number of lines changed or the number of change sets. I think that having more options for how you want information presented is a good thing. I also liked the output of churn over that of shortlog. I want to see the user information before I see the number of changes they made; I think it’s easier to process information that way than the other way around. Sadly, even though there is a quite a lot of documentation regarding formatting within the git log man page, there doesn’t seem to be a print variable for summarizing one’s commits. Or for changing the formatting if one uses the “-s” flag in the shortlog command.

As hard as I tried to break the tie between Git and Mercurial, these two particular extensions each have their own advantages and disadvantages. While it might be ideal to attempt to combine churn and shortlog into the ULTIMATE CHANGE TRACKING EXTENSION, that is a task for another day (and probably another programmer.) I guess the flame war between these two tools is just going to have to continue for a little while longer.

Git Rollback


Lately I've been spending some of my free time giving back to the Python community, sending in patches and doing patch reviews for the core Python project. The Python project uses Mercurial for its source code management. So I've been able to get a little more familiar with Mercurial as of late. This led me to learn about the rollback command, which I've found to be helpful.

A while ago I was lucky enough to watch the live stream of the “Fringes of Git” video by O'Rielly (which you can view here). In the video I learned about Git's reflog and how it keeps commits relative to the HEAD (or TIP for you hg users). So I had the bright idea of trying to implement Mercuial's rollback command by using an alias in Git.

For starters I put this alias into my .gitconfig file (avoid adding the alias block if you already have one):

  1. [alias]
  2. rollback = reset –hard HEAD@{1}

There... now you have the “git rollback” command.

Here was the process I used to test how similar the git version of rollback is to the mercurial one:
created two directories: test_hg and test_git
ran hg init in test_hg and git init in test_git:q
then just created a file called test.txt, added “test 1” into both directories, then committed appropriately.
Repeated the process for “line 2” and “line 3” and committing the additions.
This created a hg log like this:

  1. changeset: 2:fddfdadb02cc
  2. tag: tip
  3. user: Bryce Verdier <bryce@scrollingtext.org>
  4. date: Tue May 10 11:06:30 2011 -0700
  5. summary: test 3
  6.  
  7. changeset: 1:02ab8efc62ce
  8. user: Bryce Verdier <bryce@scrollingtext.org>
  9. date: Tue May 10 11:06:22 2011 -0700
  10. summary: test 2
  11.  
  12. changeset: 0:ca1341dfc464
  13. user: Bryce Verdier <bryce@scrollingtext.org>
  14. date: Tue May 10 11:06:14 2011 -0700
  15. summary: test 1

and a git log like this:

  1. commit 0357aa15e585692abda6e0c36213fce9f80523a4
  2. Author: Bryce Verdier <bryce@scrollingtext.org>
  3. Date: Tue May 10 11:07:31 2011 -0700
  4.  
  5. test 3
  6.  
  7. commit 7e2d21450b0e085ca57ac8e15b7f5b46314c1264
  8. Author: Bryce Verdier <bryce@scrollingtext.org>
  9. Date: Tue May 10 11:07:24 2011 -0700
  10.  
  11. test 2
  12.  
  13. commit d165a9825e96d40608b1da4319321c272424e019
  14. Author: Bryce Verdier <bryce@scrollingtext.org>
  15. Date: Tue May 10 11:07:15 2011 -0700
  16.  
  17. test 1

Next I ran “git rollback” and got a log like so:
  1. commit 7e2d21450b0e085ca57ac8e15b7f5b46314c1264
  2. Author: Bryce Verdier <bryce@scrollingtext.org>
  3. Date: Tue May 10 11:07:24 2011 -0700
  4.  
  5. test 2
  6.  
  7. commit d165a9825e96d40608b1da4319321c272424e019
  8. Author: Bryce Verdier <bryce@scrollingtext.org>
  9. Date: Tue May 10 11:07:15 2011 -0700
  10.  
  11. test 1
  12.  
  13. And when I did the same for mercurial, I got a similar result:
  14. changeset: 1:02ab8efc62ce
  15. tag: tip
  16. user: Bryce Verdier <bryce@scrollingtext.org>
  17. date: Tue May 10 11:06:22 2011 -0700
  18. summary: test 2
  19.  
  20. changeset: 0:ca1341dfc464
  21. user: Bryce Verdier <bryce@scrollingtext.org>
  22. date: Tue May 10 11:06:14 2011 -0700
  23. summary: test 1

A difference showed up though when I ran the command a second time. When I ran “git rollback” this was my log result:
  1. commit 0357aa15e585692abda6e0c36213fce9f80523a4
  2. Author: Bryce Verdier <bryce@scrollingtext.org>
  3. Date: Tue May 10 11:07:31 2011 -0700
  4.  
  5. test 3
  6.  
  7. commit 7e2d21450b0e085ca57ac8e15b7f5b46314c1264
  8. Author: Bryce Verdier <bryce@scrollingtext.org>
  9. Date: Tue May 10 11:07:24 2011 -0700
  10.  
  11. test 2
  12.  
  13. commit d165a9825e96d40608b1da4319321c272424e019
  14. Author: Bryce Verdier <bryce@scrollingtext.org>
  15. Date: Tue May 10 11:07:15 2011 -0700
  16.  
  17. test 1

And when I ran “hg rollback” a second time I received an error:
“no rollback information available”

At this moment I'm not very knowledgeable in how Mercurial deals with its logs. But at this cursory glance it appears as if the rollback command completely removes all traces of the last commit. (Someone please correct me if I'm wrong.) Also, the rollback does not go back any further than one commit. I honestly think that this might be a good thing because it could keep you from blindly shooting yourself in the foot, if you need to go further back than one commit there is always the revert command.

While I don't know much about Git's internals either. However I can at least make sense as to why the rollback alias functions this way. By using the reflog to revert to the last commit, I end up undoing my undue. Which can be seen when we review the reflog after running the rollback command twice:

  1. 0357aa1 HEAD@{0}: HEAD@{1}: updating HEAD
  2. 7e2d214 HEAD@{1}: HEAD@{1}: updating HEAD
  3. 0357aa1 HEAD@{2}: commit: test 3
  4. 7e2d214 HEAD@{3}: commit: test 2
  5. d165a98 HEAD@{4}: commit (initial): test 1

This makes sense to me if I focus on two things in particular in the log above. 1) Git's SHA1 hashes in the first column. And 2) looking at the log in reverse. Starting with the last line, we see the first commit. And going up to the second to last line we have the second commit. If you notice the hash values for “commit: test 2” and “commit: test 3”, those are the same hashes used for both of the git rollback commands, the second and first lines of the log. So instead of removing an entry from the log, git is just moving the head to the last commit.

While my alias isn't a complete copy of Mercurial's rollback command. I do feel that the abstract idea behind it is still correct. And hopefully I've done something to help make Git a little easier to use for someone out there besides myself.

If you made it this far down into the article, hopefully you liked it enough to share it with your friends. Thanks if you do, I appreciate it.

Bookmark and Share

Syndicate content