11 Things I’ve Learned about Git

Now that I’ve spent a few weeks with Git, I decided to record some of the things I learned while moving from Subversion (a centralized version control system) to Git (a distributed version control system).

1. Commit Early, Commit Often

Git, unlike any centralized version control system, allows each developer to have his own copy (“clone”) of the repository, which means you can commit as often as you want without affecting others — even if your changes are incomplete, break the API, or cause the build to fail.  This allows you to structure your commits as logical units of work, in contrary to the solo monolithic commit that represents the entire new feature.

Once you have your feature completed, you can go ahead and share your commits with others.

2. May the Fork be with You

Project hosting services like GitHub are a great way to share projects with others.  This nice web interface for Git is free for public open-source projects, and for a fee it can be used for private repositories. Git makes it easy to fork repositories, evolve them independently, and merge the changes back and forth. You can even “cherry-pick” particular commits and keep local changes. (I like letting my local repo be as clean or as crazy as I want. When it gets too unwieldy, there’s always git stash or git clean -f.) With Subversion, it’s all or nothing.

In the past, forks were often frowned upon because of the fragmentation they caused and the challenges related to merging.  A distributed version control system has improved this. Since Git stores the relationships between commits, merging forks back together is relatively easy.

Forks can be encouraged among team members without worrying about the merge step.

3. Check Your Head

If you end up in the wrong branch, or accidentally reset your head pointer, your source tree may not look as you expect it to. Understanding the way Git represents commits was the biggest challenge I faced. Good articles are here and here. Then, make sure you don’t lose your head.

4. RTFL: Read The Frickin (ref)Log

If you run into errors, lose commits, or find you’ve broken everything, go to reflog. It will show you all your recent repository activity. Also from here, you can reset your repository to any recent commit.

5. Pull or Fetch? 

In most centralized version control systems, updating the local source tree (to match the repository) cannot be taken lightly.  If you’ve made significant changes, updating from “HEAD” can cause a tremendous headache.  This is because you are essentially “fetching the new revision” and “merging it with your source tree.”

In Git, the concept of “fetching” and “merging” can actually be applied separately.  If you simply want to update your local copy of a remote branch, you do a fetch. You can defer the actual merging to later date. If you want to combine these two operations (fetch and merge), then you can issue a pull request.

6. Shut up and just Branch

Branching and merging in centralized version control systems often require some effort and time.  In fact, until the code is properly merged and committed to “HEAD”, I don’t consider it complete.  Git, on the other hand, stores the relationship between commits, which provides much better merge support.  Merging in Git is often an effortless operation.  You might try creating a branch whenever you start working on a new feature.

At any point, you can “merge” or “rebase” with the master branch, as well as share your branch with others for easy collaboration. (When I have some local commits on a branch but I need to pull down changes others pushed to that branch, I can simply rebase my local commits on top of those changes  with git pull --rebase.)  Separate branches makes it very easy to move effortlessly between tasks. I like the fact that Git doesn’t encourage the “branches/ tags/ trunk/ ” source code structure.

When problems arise, it’s easy to back out in Git (git checkout original_branch), or simply go through each file that has a conflict. I also think “merge made by octopus” would make an interesting caption on a painting.

Reverting in Git is pretty handy when you just need to undo a mistake. Just make a new commit with git revert commit_sha1 that undoes the specific commit’s changes.

7. More Rebasing

There’s nothing like making small local commits when working on a feature and then rebasing all 10 of them into 1 commit with a sensible commit message. It makes for a clean source tree, and makes me look like a better programmer.

8. Stash it, Don’t Trash it

If you need to switch tasks and you’re not able to commit your current work, check out the git stash.  Git allows you to stash your current changes for easy retrieval at a later date. This means you can stash your current work, reset the branch, and work on something else. Then, you can retrieve the stash and pick up work right where you left off.

9. Play the Blame Game

If you need to see when a particular line of code was introduced and who committed it, turn to git blame. Git blame shows the revision and author who last modified each line of a file. It also shows the commit comment for each line.

10. Those Who Don’t Learn from History are Bound to Repeat it

The CVS history view is an archaeological tool.  That is, it’s used to unearth historical artifacts about your source code.  It’s used to see who added a particular line of code, or to read the commit messages that accompanies a code change.

While using Git, the history view is a development time tool.  It’s used to see what else is happening in the repository, and to better understand the relationship between commits.  It’s handy to have this view open. Understanding history is an important part of working with Git.

11. git-svn: Best of Both Worlds

The integration between Git and Subversion (git-svn) is so well done that you can use Git as your interface to all of your Subversion repositories. Any Subversion repo I need to interact with can now be wrapped in a loving git embrace, and aside from a few git-svn specific commands (git svn dcommit), I can pretend I’m using Git. git-svn provides a bi-directional flow of changes between a Subversion and a Git repository.

  • – – – –