Jimmy’s post is a nice reminder of the advantages of a distributed version control system. But having ramped up on Git primarily through git-svn, I thought it would be worth enumerating some of the advantages I’ve found just in my local workflow:
1. Local Branching
More than anything, cheap local branches have changed how I work. I don’t know any developers that have the luxury of only working on one thing at a time. Not only do local branches provide the perfect mechanism to compartmentalize work on different features (or just ideas you’d like to try), but Git’s branching mechanism also works great for trying different approaches to the same problem. It’s trivial to reset a branch back a few commits if an idea doesn’t work out, or create a new branch from a few commits ago to see if there’s a better way.
The other advantage of local branches is that they’re not public unless you want them to be. Not only are centralized branches slower to work with and commit to, the ceremony required to set them (if you have permission at all) attaches a feeling of permanence—I’m less likely to commit something that’s not necessarily finished, but may still be useful. I don’t have these reservations with Git—my main repository at work has maybe 20 branches of refactorings, spikes and other works in progress that I’m free to revisit as time allows without feeling self-conscious about their current lack of polish.
2. Smarter Merging
Because of Git’s focus on file content rather than file location, it’s much better at resolving merge conflicts for you, especially across renames. That’s not to say you’ll never get conflicts, but they’re typically real conflicts—files deleted in one branch and modified in another, overlapping Visual Studio project file changes, etc. And for those you can hook up a 3-way merge tool like Beyond Compare or P4Merge to make quick work of them.
3. Pause and Resume
So your boss comes in and says something to the effect of “stop everything you’re doing and make this change for the CEO.” What do you do? In SVN you might create a patch file of your work in progress and revert. In TFS you might shelve your changes up to the server. In Git you have a two options that are built right in:
git stash, which pushes the changes in your index and working directory onto a local stack of stashes from which you can pop at any time. Your typical workflow would look something like this:
$ git stash save "I love interruptions!" $ git checkout master -b pti # ... save the day ... $ git checkout my-feature $ git stash pop
The disadvantage of stashes is that they’re not explicitly tied to the branch where the work happened. The longer it takes to get back to what you were working on, the higher the chance you’ll forget you had some work stashed.
- Stage all your changes and make a temporary commit that you’ll later revert:
$ git add -A && git commit -m "I love interruptions!" $ git checkout master -b pti # ... save the day ... $ git checkout my-feature $ git reset HEAD~
The kicker is the final command, which moves the
my-featurebranch pointer back one (to
HEAD‘s parent) but leaves the working directory unchanged—keeping your old work in progress. The advantage of this approach is that the temporary commit lives with the branch where the changes belong.
It doesn’t take long for both of these operations to become second-nature, greatly decreasing the impact of these interruptions.
4. Rewriting History
Pretending a temporary commit never happened is just one example of rewriting history, which becomes a fundamental operation for an intermediate/advanced Git user. But first, our cardinal rule:
Once a commit is pushed, it’s permanent.
History that has been shared with others is off-limits for rewriting unless you have explicitly communicated that history may be rewritten. For example, I routinely overwrite branches I push based on pull request feedback, but once something hits posh-git‘s master branch it’s there for good.
So with that out of the way, I present my corollary to the cardinal rule:
Until you push, pretend you’re perfect.
In the simplest case, this might simply mean using
git commit --amend to fix a typo in the last commit’s message, or include changes to a file that you forgot to save. For more complex needs, like pretending you wrote your tests before the implementation, check out the amazing interactive rebase.
5. The Staging Area
Some people view Git’s staging area as an annoyance—an extra step between code and commit. I simply see it as your standard commit dialog with all the boxes unchecked by default. Checking all the boxes is as simple as
git add -A, which I alias as
git aa (
git config --global alias.aa "add -A"). Or you can use
git commit -a to stage all changes before commiting (though this won’t pick up additions or deletions).
The real power, though, comes from the ability to stage (
git add --patch) and unstage (
git reset --patch) parts of your changes before commiting. You can even use
git checkout --patch to revert some changes in your working directory without affecting others, or even to pull in partial changes from another commit altogether.
I use this feature on an almost daily basis to separate cosmetic changes from refactoring from new feature work, without having to undo some changes so that I can commit the others. The result is more smaller, atomic commits that separate inert changes from those that actually change behavior.
Tip of the Iceberg
Ultimately these are just a few of the things that have convinced me Git is worth the learning curve, worth the cost to migrate, worth the cost to retrain your staff; and this doesn’t even touch on the advantages and flexibility of Git’s distributed nature. I just hope to pique your interest enough that you give it a try for yourself.
And if you’re already using Git, how does this compare with your list of must-have features?