Friday, January 23, 2009

Git allows my workflow to be "stateless" (or more advanced features of git)

I've been using git since May, and I feel like I use it pretty comfortably now. Oddly enough, with SVN, I just learned the basic check in and out, and left it at that. With git, I find that I want to do something--after poking around docs and tutorials, I find some of the advanced features of git something that I'd want to use.

Git has a bit of a learning curve, since their commands aren't exactly intuitive (deleting a remote repository anyone?, how about reverting is different from resetting?) you have to discard your notions of Svn, and learn how Git models a repository. But once you grasp that, it's pretty easy, not to mention pretty powerful. For me, Git had certain features that gave me a "wow", as I learned about and got use to using them. Here's in order as I discovered them.

Local branching

Branching is fast, and merging is relatively painless. When branching is fast, you end up treating branches are like tabs on your browser, but for source control. This lets you experiment with different code without affecting the main branch.

git checkout -b new_branch

Builtin Grep

I hated that grep would also search through the .svn files. Sure, I wrote an alias and script so that it wouldn't, but it would leak sometimes. Having git have a built-in grep was very nice.

git grep "some_regex"

Interactive staging of commits

Git has this idea of staging files, meaning marking them for a commit. Because you can commit different files, you can fix little things that might not have to do with your current ticket, but add them as separate commits.

But what if you edited one file but the edits had to do with two different tickets? With git, you can selectively stage parts of a file for committing.

git add -p

Rebasing branches

Rebase was a little confusing to me at first. The name didn't tell me anything. Rebase isa tool with multiple functions, with the same underlying principle, which is why it has just one name.

At the very basic, you can use rebase to move the root of the local branch if you want the changes made on the parent branch since then. This is important because it puts the burden of merging on the patch writer, and not on the merger. In addition, it makes for a cleaner commit history.

git checkout child_branch
git rebase parent_branch

Even better, you can reorder the commits as long as you haven't pushed it to a remote repository yet. The same task allows you to squash your commits together for a cleaner history.

git rebase -i HEAD~n

Stashing changes

Because you can stage files for commits and you can readily switch between branches, sometimes, you might be working on something, but they're not ready to be committed. The production code has a bug that needs to be fixed immediately. Git won't let you switch branches, but you don't want to commit unfinished code. What do you do?

Git lets you stash your changes to save your uncommitted changes, but not commit them anywhere. This is useful to switch to another branch to fix an emergency bug or perform other operations that require no uncommitted changes in the working directory (like rebase).

git stash
...fix bug on other branch...
git stash list
git stash apply

Bisection of commits

Sometimes, you're humming along with your commits, and you find that there was a part of the application that is broken, but your tests didn't catch it. You need to revert back to that version and make a branch. You can use git bisect to look for a particular commit. You give it a commit, and git will checkout that version, and ask you whether it works or not. Then it bisects the commits and half and then checks out the other half and asks you if it's correct. If not, it'll keep narrowing down the space of commits until you find the right one.

git bisect commit_hash other_commit_hash

Basically, a lot of what Git enables me to do is switch between tasks easily. I don't know if it's the right word, but it lets me work statelessly. At no time does git not let me do something because of a particular part of my workflow that I'm in. Outside of a few commands like rebase and merge (which can be fixed with stash), the commands I can run in git doesn't depend on the previous commands I've run. Really, git is a tabbed browser for me.

My biggest complaint is probably that the names of the commands aren't intuitive because it goes against the usual mental model of what a repo does. The names are actually make a lot more sense only after you understand how git models a repository. So take a look at the git book, and learn what you can.



  1. Nice. Thanks. I might try it out.

  2. Anonymous4:30 PM

    Thanks - Interesting Post