I've recently been exploring
Git, and I'm really impressed. In fact, I'm having a lot of fun. Yes, tooling is not quite there, but it is coming along. Among other tools, there's an
Eclipse plugin (still pre-1.0), a
Hudson plugin, a
JIRA plugin, and plans for a
Fisheye plugin. I've tried the Eclipse plugin, and was moderately impressed. Some things just don't work yet, but then there still are lots of problems with the
Subversive SVN plugin for Eclipse. I routinely use the SVN command line interface to do (almost) everything, so using Git from the command line isn't a stretch at all.
What really convinced me to try Git, though, was how well it does branching. Actually,
branching and merging. Quite a few source control management systems can branch, but the problem is they don't merge - or if they do, they don't do it well. Git, on the other hand, makes merging easy. In fact, it's really easy to create branches, switch between branches, and merge branches.
So why am I so hung up on branches? Well, being able to branch and merge means that I can use branches for everything - bug fixes, new feature work, prototypes, or maintenance. I can create a branch where I do all my work, and I can commit as many times as I want. I can even use tags to help me remember important commits (e.g., "all tests pass"). And when my work is complete, I can merge my changes onto as many branches as I need to. All of this is done locally, without polluting the main repository's trunk or branches. Of course, if I'm working with other people on a feature or bug (e.g., "please review these changes and provide feedback"), I could expose my own repository or we can leverage a shared repository. And when the work is all complete, merging onto the official branch(es) is cake, too.
I don't want this post to be another tutorial about Git. There are already quite a few resources out there, and Google is your friend. If you're new to Git, I'd recommend starting with the videos and documentation at
git-scm.com, which is the new home for Git. If you're wondering "Why would I use Git?", then I'd suggest
Linus Torvald's talk at Google. If you're asking "How would I use Git?" or "How does Git work?", then a great starting point is
Bart Trojanowski's excellent tutorial. The
Viget folks also have a number of good
blog posts.
What I do want to cover in this post, however, is how to get started with Git and Subversion. See, Git comes with a bridge to Subversion so that we can use Git locally but can routinely do the equivalent of "svn update" and "svn commit" - on any and all branches. In fact, using Git locally means that we can probably work with SVN branches just as easily as SVN users, but
we can merge branches. And since branching and merging are so much easier for us, we can create our own branches locally without having to expose them all to the central SVN repository.
So, here's my simple introduction to using Git with Subversion.
1. Install GitYou can
install it several different ways, depending on your platform and your preference. I have a Mac, so I could download and compile it using
MacPorts, or I could grab the
latest installer. I like simple, so since I don't yet have MacPorts I went for the installer. Piece of cake. I also chose to manually set up my path, so in my .bashrc file I added these statements:
export MANPATH=${MANPATH}:/usr/local/git/man
export PATH=${PATH}:/usr/local/git/bin:/usr/local/git/libexec/git-core/
(Okay, I actually used some variables to keep things DRY. I thought the above might cut through all that noise.) Note that we added "
git/libexex/git-core/" to the PATH. This directory contains all the commands that start with "git-" (e.g., "git-svn ..." is really the same as running "git svn ...").
2. Clone a remote SVN repositoryThe next step for me was creating a local git repository that mirrored the remote SVN repository.
$ git svn clone -s http://example.com/my_subversion_repo local_dir
The "-s" option tells git that the Subversion repository use the "standard" naming convention of using "trunk", "branches", and "tags" directories. When this command runs, it creates a subdirectory called "local_dir", initializes the git repository in that "local_dir", connects to the SVN repository at the URL (which does not include "trunk" or any other branch/tag information), proceeds to download all of the history (including branches and tags) in the SVN repository, does some cleanup, and checks out the equivalent of the SVN "trunk" to our working area. And, because of Git's bridge with SVN, we'll be able to regularly pull changes from SVN into our git repository, and we can even upload changes we make locally back into SVN.
We now have our new Git repository, so cd into it:
$ cd local_dir
If you look closely, you'll see a couple of things. First, Git doesn't proliferate your working area with ".svn" folders. Instead, there's just one ".git" directory at the top. Git also uses a different technique than SVN for remembering which files and directories should be ignored. But we can bootstrap Git's ignore information automatically from SVN's. We'll use the ".gitignore" file in our working area, since we want this information to be versioned and we want everyone using the repository to be able to use it. If your SVN repository already has a ".gitignore" file, then someone's done the work for you. Otherwise, you'll have to run the command to generate the file:
$ git svn show-ignore > .gitignore
This will take a minute or two, depending upon the size of your working area. I would then commit this new file, placing it in the master branch.
3. Committing (locally)To commit our new file, we first have to tell Git that we want to start tracking this file. Or, in Git parlance, we want to stage the file by adding it to the index:
$ git add .gitignore
We can use the
status command to see a summary of what's already staged, what's being tracked but hasn't been staged, and what's not being tracked at all:
$ git status .
We can also get a more detail report showing all the individual changes that have been staged:
$ git diff
We can then commit our staged changes:
$ git commit -m "Description of commit" .
or, if we want to automatically stage any modified or deleted files, we can add use the "--all" (or "-a") option:
$ git commit -a -m "Description of commit" .
With the "commit command, Git records the entire set of staged changes as a single commit to our current branch, and it moves the branch's HEAD pointer to this last commit.
4. Branching and MergingSince your Git repository is local, the process for branching and merging is the same, regardless of whether your Git repository is brand new or whether its based off of a Subversion repository or a Git repository. Steps for creating a branch, making changes, committing those changes, switching branches, and merging branches is all covered well in other places, so I won't cover it here.
But if your repository is based off of a SVN repository, you'll still want to pull changes made to Subversion into your repository. And you'll probably want to be able to push your changes (e.g., commits) back into SVN.
5. Updating from SubversionIf you're familiar with Subversion, you're hopefully used to doing "svn update" before committing your changes. This pulls any revisions that others have made since you last updated, and its good form to do this and to make sure everything compiles and runs locally before committing.
In Git, you do this with the "git svn rebase" command. This command fetches revisions from the SVN used by the current HEAD (of the current branch), and "rebases" any current commits on the branch to apply to these latest SVN changes. This is analogous to creating a patch file for each local commit (relative to that commit's parent) since the last rebase, updating the branch with the new SVN revisions, then sequentially applying the patches. In other words, it takes all your local changes (in the form of commits) and reapplies them to the latest SVN revisions.
Here's the actually command to rebase the current branch:
$ git svn rebase
If you want to fetch all of the revisions on all branches in the SVN repository and rebase any local commits on those branches, you can add the "--fetch-all" option.
6. Committing back to SubversionOnce you have rebased your local commits on a branch, you will still have changes to that branch that aren't yet in Subversion. We want to do the equivalent of an SVN commit, which in Git is to commit each diff on the branch back to SVN:
$ git svn dcommit
This creates a new revision in SVN for each local commit on the branch. Of course, if you want them to look like a single revision, you'd need to
squash the commits before running the
dcommit command.
ConclusionThis post focused mostly on how to get started with Git using a remote Subversion repository, so I didn't talk much about how to go about branching and merging. I think you'll agree, though, that Git's Subversion bridge seems very intuitive, and in fact many of the same concepts used by the Subversion bridge are the same concepts used by the rest of Git.
But if you're new to Git, no doubt you're feeling unsure about what Git is and how you'd use it. Again I'd have to point you to the recording of
Bart Trojanowski's presentation. It is 2 hours long, but it really is a fabulous tutorial if you're interested in learning the fundamentals how Git works and how to use Git.