A Gaggle Of Git Tips
Mark Cornick, Former Viget
Article Category:
Posted on
Almost a year ago, our first post about Git hit this blog. Since then, our posts about Git have been popular with you, our readers. Clinton’s overview of git-svn, for example, remains one of our most popular posts, months after he first wrote it.
Like much of the Ruby and Rails world, we’re using Git every day, and finding little ways to exploit its power for fun and profit. This post brings together several of our little Git discoveries. Some are profound; some are subtle. Some are well-documented in the literature; some are hidden in corners of the Git source. All of them, however, have made our lives a little easier—and we hope they’ll do the same for you.
Tony Pitale:
My favorite Git tool that I’ve never heard of before a few weeks ago is git-bisect
. When Pat and I were pairing, we would switch back to old revisions to see what worked, and move forward in time to find when something broke. With git-bisect
you can do:
$ git bisect start $ git bisect bad $ git bisect good 128dafd75384f42c2544acd28d432c8abef79878
Once that’s done, it’ll go through the tree and help to pinpoint changed commits that may cause the breakage.
If you have a lot of commits between good and bad, you can checkout, run tests and add to the list of good or bad commits. Once you do, it will continue analyzing.
(Brian Landau adds: I like the idea of git bisect but haven’t used it yet myself. I also like git whatchanged -p
.)
Justin Marney:
I like git rebase -i HEAD~n
, otherwise known as an interactive Git rebase. I usually work in a branch called work
where I can commit broken codes. I switch back and forth between work
and master
when I am syncing with svn. On occasion I’ll accidentally commit on the master
branch. Similarly, a few of my Git projects don’t even have a work
branch. I use git rebase -i
to make sure every commit is clean. For example, when I have two commits with broken code followed by a third clean commit, I will fire up the following command:
$ git rebase -i HEAD~3
Which will bring up a TextMate window that looks something like the following (with way better commit messages):
pick 87a23d7 added fb_connect abilities pick 81bb0ca refactorings pick 439db70 more refactoring
Here, I can squash previous commits into the newest one and Git will rewrite the branch history accordingly:
pick 87a23d7 added fb_connect abilities s 81bb0ca refactorings s 439db70 more refactoring
After saving this file, Git will reorganize itself and allow you to set a commit message. The three commits are replaced by a new commit that contains all of the previous changes and your new commit message.
Patrick Reagan:
When I find myself getting interrupted, git stash
is a feature that I can’t live without. In its simplest form you can quickly tuck away your changes:
$ git stash
And then retrieve them when you’re ready:
$ git stash apply
While that gets the job done, I don’t find it adequate for how I work. Sometimes I’ll forget what I was working on, or if I’ve already applied the latest change that I stashed away (“apply” leaves the change in your stash). This works better for me:
$ git stash save "Changes to user authentication"
At any time, I can see the incomplete changes that are out there:
$ git stash list stash@{0}: On master: Changes to user authentication
When I’m ready to get back to working on that change, I’ll pull it off the stack:
$ git stash pop
Now I have what I need and I won’t accidentally re-apply it.
Clinton R. Nixon:
My tips are simple:
I’ve started using git add -i
more than git add --patch
. It lets me interactively select the changes I’ve made that I want to stage, patching, updating, reverting, or adding untracked files easily. (See http://book.git-scm.com/4_interactive_adding.html)
Another whole set of tips could be around setting up your .gitconfig
file:
[user] email = clinton.nixon@viget.com name = Clinton R. Nixon [alias] up = pull origin st = status di = diff co = checkout ci = commit br = branch sta = stash [core] pager = most +s +'/---'
The alias stuff is probably common knowledge to most people. The pager line is really nice: it makes Git use most
as my pager, and the +'/---'
part makes it automatically jump to the first diff, and let me jump through to later diffs by typing n. (This is a less and most feature, not Git, but it’s nice to be able to specify it through Git.)
Mark Cornick:
Git’s lightweight local branches are awesome. I use them often to isolate my different fixes and ideas from each other. However, I found myself getting confused about which branch I was currently editing. One way I help myself keep track of things is to put the current branch in my bash prompt.
For this to work, you need Git’s bash completion support. If you install Git from MacPorts, you can pull this in by specifying the bash_completion
variant. If not, you can find it in the contrib
directory of the Git source, and source it in your .profile
. (Of course, this also gets you tab-completion of various Git commands, branches, paths, etc. It’s worth installing even if you don’t change your prompt.)
Once you’ve done this, you can insert the current prompt into your prompt by setting something like this in .profile
:
PS1='\h:\W$(__git_ps1 "(%s)") \u\$ '
The magic part is (__git_ps1 "(%s)")
—this calls a bash function to find the current branch, and outputs it using the printf-style format string specified. So my prompt now looks like:
Laika:awesome(master) mcornick$
when I’m in the awesome
repository on the master
branch. (When I’m not in a Git repository, no branch is shown, so this doesn’t pollute my prompt elsewhere.)
David Eisinger:
I’ve been playing with the various hooks, trying to find ways to boost productivity. Here’s a post-commit I’m using to track commits across multiple projects (very helpful for timesheets.) It loads the most recent commit, and prepends the date, project name, and commit message to a central log file.
Matt Swasey:
Pushing a branch or tag to your remote repository is helpful in sharing experimental code or release points with other developers.
(~/rails_blog):$ git checkout -b experimental ... hacking some crazy features ... (~/rails_blog):$ git add . (~/rails_blog):$ git commit -a -m "added midi player to ALL layouts" (~/rails_blog):$ git push origin experimental
This will create a remote experimental branch, which will show up under the branches
menu if you are hosting your repository on GitHub.
Other developers wanting to hack on your experimental branch should do the following:
(~/hacking/migs_rails_blog):$ git clone git://your-repo-url.git (~/hacking/migs_rails_blog):$ git checkout -b experimental origin/experimental
The same goes for tags: after you’ve created your tag, just use push
to place it on your remote repository.
(~/rails_blog):$ git tag REL_1.1 (~/rails_blog):$ git push origin REL_1.1
Again, if you are hosting your repository on GitHub, your new tag will show up under the tags menu.
Bonus round: If you ever want to archive your repositories, perhaps when creating downloads for releases, you can easily accomplish this with git-archive
:
(~/rails_blog):$ git archive --format=tar --prefix=rails_blog-1.1/ REL_1.1 | gzip >rails_blog-1.1.tar.gz
And that’s our selection of useful Git tips. Which ones do you find useful? Do you have your own tips to share? Let us know in the comments.