Git

Git Prepush

I'm not sure how everyone else uses git, but at work we use it in a more centralized manner, just like the subversion I migrated us from some time ago. That means that for our changes we create a branch off of the master branch, known as a "feature branch", make our changes, test said changes, do a quick peer review, and finally merge back into master; rinse; and repeat.

Since there are changes constantly going into the master branch, that means we have to do what I call the git prepush shuffle, which is:
1) checkout master
2) update (pull) master branch
3) checkout feature branch
4) update (rebase) feature branch
5) fix conflicts (if any)

Now the feature branch is ready to push for the review process then be merged into master.

Why am I telling you all of this? Well, I hate having to type all of that stuff out each and every time. One day I wondered if there was a way I could improve this. I knew I could do aliases, so now it became a question of if I could do more than one command within an alias. A little internet searching lead me to this StackOverflow answer, that showed that it was possible.

This brought me to the next hurdle; I knew I needed to switch to the master branch. But then how would I get back to the feature branch? A little more rabbit hole falling got me to this StackOverflow answer, which states that you can go back to the previous branch with the "@{-1}" reference.

The one note I would also like to add before I show you what I did is that I assume that:

  1. [branch]
  2. autosetuprebase = always

is set. WHich I've found really helpful when I'm working on a more centralized manner which multiple committers.

My ultimate "creation" is this:

  1. [alias]
  2. prepush = !git checkout master && git pull && git checkout @{-1} && git rebase master

So if anyone out there does the same dance that I do, feel free to use this alias and save yourself some time and typing.

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 Pre-Commit Python Syntax

All of us programmers have made this mistake before: we’ve submitted code that we thought was correct only to have it fail during compilation because of a simple syntax error. Just between you and me (and the rest of the interwebs), I did this last week. Then I thought to myself that it would be good to come up with a tool to help ensure it didn’t happen again. I’m proud to say that on the Git front, I have fixed my problem.

Enter Git Hooks, a way to add scripts to various parts of the Git workflow. Using what is called the pre-commit hook, I was able to write up a quick bash script that goes through all the changed files in Git staging area, check if they are Python files (based on the .py file extension), and then run those Python files through Pylint to look for various errors, including syntax and type errors.

So here is my git hook:

  1. #!/bin/bash
  2.  
  3. files_modified=`git diff-index --name-only HEAD`
  4.  
  5. for f in $files_modified; do
  6. if [[ $f == *.py ]]; then
  7. pylint -E $f
  8. if [ $? != 0 ]; then
  9. echo "Code fails pylint check."
  10. exit 1
  11. fi
  12. fi
  13. done
  14. exit

Feel free to copy this and modify it for your own needs. My gift to you; no charge. But an occasional “Thank you Bryce” email is always nice. I also accept lavish gifts of cash or ThinkGeek merchandise.

One quick thing before I let you get back to your busy lives. If you have an older version of Pylint than 0.24, the “-E” flag might need to be changed to “-e”.

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