Using Git with SVN
I've been using Git with Subversion successfully for months now. I thought I would write up a quick guide on how to use git and svn at the same time without loosing your mind.
A note about differences between git and svn
The main difference between Git and svn is that within a branch, svn has a linear history. I can ask questions like "On this date, what did the files in the repository look like?" All changes occur one right after the other. New changes always are at the newest revision.
In git, all the assumptions above are broken. I routinely have histories that look like this:
revision 1 --> revision 2a ----------------------> revision 3
\ /
\--> revision 2b --> revision 2c -/
I know that revision 3 comes before revision 1, but does revision 2c come before 2a? That's not a well-posed question in git. This is what it means for code history to be non-linear.
- Disadvantage: It makes going back in time tricky, and it complicates display of code history. Git needs
gitk in order to view histories.
- Advantage: If the code isn't ready to compile as of revision 2b, you never see that behavior on the main code line.
Anyway, svn can't handle trees like the above. Git won't let you check in something with a non-linear merge history into an svn repository.
Rebasing versus merging
Assume that Rob is working on revision 2a, and that Gernot is working on 2b and 2c. These changes have to be merged. Git's default behavior is to create a new revision with the contents of both branches, and mark it as a merge:
revision 1 --> revision 2a ----------------------> revision 3
\ /
\--> revision 2b --> revision 2c -/
This works, but it produces non-linear histories.
The other way to check in is to remake the tree like so:
revision 1 --> revision 2a --> revision 2b' --> revision 2c' --> revision 3
\
\--> revision 2b --> revision 2c
... where 2b' and 2c' are just copies of 2b and 2c, but adjusted so they have the right parent. It's called a
Rebase.
- Advantages: History becomes linear.
- Disadvantages: 2b' didn't really come after 2a. If there are merge conflicts, 2b' might be way different from 2b. 2b' also isn't tested. 2b might have compiled, but there is no guarantee that 2b' will compile.
You can think of rebasing as what svn does when you run
svn up. If you have local changes in your directory, svn will try and merge those changes so they are based off the most recent revision of the repository. Svn only allows you to rebase one uncommitted revision, whereas git lets you rebase multiple local revisions.
Quick start guide to use git like svn
- Create a repository from SVN:
git svn clone --stdlayout svn://server/directory
- This copies the whole repository to the main code.
- Check in locally:
git commit -a
- Create a new local branch:
git checkout -b new_branch trunk
- Update:
git svn rebase
- Check in remotely:
git svn dcommit
Git gotchas
- To revert a file to the last known good revision:
- To switch local branches
- To add a file to the repository
Read
http://marklodato.github.com/visual-git-guide/index-svg.html for an explanation of why Git works this way.
Neat git tricks
- To check in only part of a file:
- To fix a local commit that hasn't gone to svn yet
Sharing git repositories across a machine
Occasionally you may want to take git revisions from machine to machine, without checking into svn and checking back out again. I'm still working on a way to do this sanely. I suggest the following:
- On a server you have access to everywhere, run
git svn clone --bare --stdlayout svn://server/directory
- The
--bare is really important, or bad bad things will happen.
- To copy this repository, run
-
git clone ssh://server/dir/to/git/repo
- To update from this repostitory
-
git fetch ; git rebase origin/branch branch
- Don't use
git merge or git pull, as that introduces non-linear history, and you won't be able to commit results back to svn.
- To push to this repository
-
git push branch:branch
- If this gives you an error, then update first.