My previous post about Git and SVN hopefully intrigued you enough to want more. Yes, you can use Git locally even though your stuck with using Subversion for the central repository. In this post I’ll show you how to get started, while in the next post we’ll see how the Git Eclipse plug-in.
1. Install Git
You 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/manexport PATH=${PATH}:/usr/local/git/bin:/usr/local/git/libexec/git-core/
(Okay, I actually used some variables in my real .bashrc 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 repository
The 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. Remember, Git works locally, so this commit is recorded locally. This may sound strange at first, but really it allows you to commit (locally) much more often, which can improve your development process since backing out things that don’t work is really easy.
To see the history of commits, use the “log” command
$ git log
This displays each time, message, and SHA-1 hash (the unique identifier) for the last n commits. You can use these hashes (or the first 4 characters or so) in the diff command.
$ git diff <hash>
displays the difference between a previous commit and changes, while:
$ git diff <hash1> <hash2>
displays the difference between two previous commits.
There are a lot of things Git can do – way too many to cover here. But look at “git bisect”, “git grep”, “git revert” jut to name a few. But let’s get back to our workflow.
4. Updating from Subversion
If 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.
5. Committing back to Subversion
Once 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.
Conclusion
This 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. And once again I’m going to suggest reading the Git Community Book or watching Bart Trojanowski’s presentation.
Note: Portions of this post were taken from a previous post on my personal blog.
Filed under: techniques, tools