It’s been almost a week since I moved to Git for my version control instead of Subversion. It’s been an interesting experience with some amount of learning to do. There are a number of extra things to keep in mind when using Git, but as time goes by, I’m quickly becoming used to them and they don’t seem bothersome at all.
The first major difference is going from centralized to distributed. I use version control to keep my files in sync across multiple computers as well. With Subversion a commit would save my changes and also push them to my remote server. With Git, that is now two separate processes. I would have liked to be able to have them both as a single step, but honestly, it really isn’t much of a problem. I’ve become used to doing a git push just before I leave my computer.
Once I got past that, the next thing that I had to deal with was the fact that a commit even to the local repo is actually two separate operations. You first have to add any changes made to something called the Index. It’s easiest to think of the Index as a sort of staging ground for your changes. You can selectively add files to the Index, building up a coherent set of changes and then commit them together. The Index can take some getting used to, but it is easily one of Git’s killer features. Often enough, you end up editing a bunch of completely different files. They could be files in different projects, or files in the same project that do totally different. When it comes time to commit, you’d like to be able to commit the different changes separately so that you can track exactly what changes you made by project. The Index lets you do exactly that. In fact, git will actually let you select which set of changes to a particular file you want to add to Index in a given add operation if you do a
git add --patch filename . This gives you an unprecedented level of fine-grained command over how the change history of your files are saved. I haven’t had a chance to use this level of power yet, but as the semester progresses and I get involved in large project, i don’t think it’ll be long before Git saves the day. See this post for a more detailed discussion of how useful Git’s Index is.
Even if all the changes to the change history could be made only in the Index, that would be an incredible amount of power. But Git’s power stretches beyond the Index to the commit operation itself. The
git commit --amend operation pushes the changes in the Index into the previous commit which comes in handy when you forget a change, or realize that you need to change something else. And if that isn’t enough, the
git rebase --interactive command basically hands you a nuclear-powered swiss army chainsaw with which you can go to town on your repository. Needless to say, this kind of power isn’t too be used without some amount of care and consideration.
Git’s control over committing is impressive, but equally impressive is Git’s branching capabilities. In Subversion, a branch means copying the current files to another subdirectory in the same repository. This means that all branches are visible at all times and that anyone checking out the full repository will get all branches. This isn’t always what you want. Sometimes you don’t everyone to see your changes until they’re ready for prime time and you might want new developers to get the production branch and then later pull additional branches as needed. Since Git is in many ways a filesystem that supports tracking changes, the branch command works different. A branch doesn’t require you to create a subdirectory in the same repo. Instead it creates a new information tree in Git’s internal filesystem. So you can have separate branches with very different directory structure, but without cluttering your visible directory hierarchy.
git checkout branchname will move you to a new branch and change the visible filesystem in place to match the branch’s. So you can traverse your file structure as normal without even thinking about the fact that there are other branches. This comes in very handy for automated testing or building. You don’t have to repoint your testing infrastructure to a new directory to test multiple branches. If your branches have the same layout, you can just switch branches and run the same testing scripts.
Merging is also made simple. Suppose you have an experimental branch that you want to merge into the master branch. First checkout the master branch (git will tell you if you’re already there) and just pull in the experimental branch as so:
git checkout master
git pull . experimental
You don’t have to worry about specifying revision numbers, and if some changes in experimental had already found their way into master (via a previous pull maybe), git will simply ignore them (or let you manually choose which edits to commit if there are conflicts). Git merging by example is an excellent article that explores branching and merging with a series of simple examples.
All of Git’s features make non-linear development easy. You don’t have to think too hard about branching off the main trunk or making changes to another part of your current branch. The Index and merging features make it easy to organize and alter changes after you’ve made them. You spend less time and effort managing your commits and you can devote more energy to actually dealing with your code base, safe in the knowledges that your changes will be recorded just as you want them to be. Whether you’re a one-man powerhouse hacking on a number of different projects in your spare time or a member of a commited team working to a tight schedule, Git can be a very powerful tool in your arsenal of code-management utilities. Use it well.