Git SVN Workflow 4

Posted by adelcambre
on Tuesday, March 04

I know, I know, this is blog post number one million about Git and git-svn. This is primarily for my co-workers who are wanting to switch to git for our SVN based projects like I have. (If I get them with git-svn, next we can start hosting with git directly, Muhahaha!)

First, a bit of background. Git was written by Linus Torvalds specifically for use by the linux kernel team. He had very specific ideas about how the version control system should work, and none of the offerings at that time satisfied his needs. Git is designed to be distributed (every clone is a full fledged git repository) and very very fast. It is also designed to be very easy not only to branch, but also to merge.

Disclaimer: This is not intended to be the “correct” way to use git + svn, only the way that I use it. That said, here we go.

First, we assume that you have a subversion repository at http://example.com/svn/my_proj. You will need to create the git repo setup to pull from this repo.


git svn init -s http://example.com/svn/my_proj

This will initialize the git repo for pulling from svn. The -s indicates that you have a “standard” setup for your subversion repository. I.e. trunk/ branches/ and tags/. This command will not yet import anything from subversion.


git svn fetch

This command will fetch all revisions from subversion that you have not yet received. The first time you run this could take quite a while. There are options to only fetch some of the revisions, but I prefer to fetch the whole history the first time. This way blame and log show the whole thing.

Now you will have both the whole revision history for trunk, but also all of the branches on the svn server. You can see all of the branches with

git branch -a

This will show all branches, including the remote branches.

Now that you have the entire subversion history stored locally, it can be useful to repack the repository. Right now, each revision has it’s own file, this command will pack those into bigger “pack” files. This will make the repository smaller, but also with many many fewer files


git repack -d

Next you will want to do some actual work on the repository. It is not recommended to make changes in your master branch, so lets make a branch for the new feature.


git checkout -b new_feature

This will make a new branch (the -b) and switch to it (checkout). Once you have your new branch you make a bunch of changes and add a new file or two. You need to add any new files so git knows to track them. You can do this with:


git add path/to/new_file

Now, once you are happy with this new feature and have it tested. You can commit it to git. This is one place where git diverges from subversion. Files you have changed aren’t automatically staged for committing. So right now if you try to commit, you will commit the file that you added in the above command, but nothing you changed (adding a file stages it). So you have two options at this point, you can manually stage each file you want to commit, you will want to do it this way if you don’t want to commit all the changes you have made. That will look something like


git add path/to/edited_file
git add another/edited/file
git commit -m "commit message" 

If you want to commit all of the changes you have made, and you have added all of the new files, you can automatically commit all changes, no need to stage. This is how I normally do things. That looks like:


git commit -a -m "commit message" 

The -a tells git to commit all staged and unstaged changes.

So now you have these commits in git but they have not been pushed to subversion yet. You will need to get this commit into master first. You need to checkout the master repo, then merge back with the new feature.


git checkout master
git merge new_feature

It is likely this merge will work without any conflicts, but if not you will need to fix the conflicts then commit.

Once you have the commit back in the master repo, you will need to resync the master branch to the svn repo to make sure you don’t commit conflicts.


git svn rebase

This will rebase the master branch to the subversion trunk. Next, you need to push the commit to subversion.


git svn dcommit

That’s it! That is the basic circle between an initial checkout back to a commit. I will be going through the different git commands in the next few weeks to go over how they work in more detail. Here are the things I am planning on covering:

  • git reset
  • git rebase
  • git log
  • git stash
  • git merge
  • git mergetool

Did I miss anything major? Let me know how you use git with svn in the comments.

Comments

Leave a response

  1. Chris McGrathMarch 04, 2008 @ 01:20 PM

    That workflow is very similar to how I use git-svn. Something I don’t do that I’ve seen in nearly every article like this is the merge back to master. I just git-svn rebase my topic branch to svn HEAD, fix any conflicts there and git-svn dcommit that, possibly doing a rebase -i beforehand to clean up the commit(s). Then I switch to master, rebase again to get the committed changes and delete the topic branch. My reasons for this aren’t really biggies imho, more that this is the way I’ve always done it. They are:

    1. Master will always contain only changes that are in svn, so easy to make a quick bugfix branch without having to work out which rev to branch from. 2. The git-svn man page doesn’t recommend doing git merges, but using dcommit or format patch to move changes between git branches. 3. It’s one less place to get conflicts, as they will only happen on the topic branch rebase.

    Looking forward to your other articles.

    Cheers,

    Chris

  2. Andy DelcambreMarch 04, 2008 @ 04:31 PM

    That is very interesting, I was never completely sure how dcommit’ing directly from a branch would work. The way that git-svn chooses what branch to commit to is something of a mystery to me, for example there is no way to get git-svn to push to a specific branch that I have found.

    I like your method though, might start using that instead.

    Thanks, Andy

  3. JeffGMarch 04, 2008 @ 08:52 PM

    Thanks for the repack command- I had not learned about that one yet so I’ll definitely look into it.

    When executing git branch -a You’ll notice there is a trunk remote. I create my topic branches based on trunk git co -b topic/123 trunk I find that I don’t really care about master since the svn repo is actually keeping track of everything. Why bother switching to master and updating it?

    My series of commands looks like the following.. // Clone the svn repo (init and fetch in one) git svn clone https://central/svn/repo/project -t tags -b branches -T trunk

    // I set up co as an alias to checkout // Create a branch based on trunk git co -b topic/1 trunk

    // Make file changes and stage them. git add .

    // I set up ci as an alias to commit, enter commit message git ci

    // Make sure your files are up to date with any changes since you created the branch // Since I based the branch on trunk, it knows where to look git svn rebase

    // Send the changes to svn git svn dcommit

    // Create a new topic branch git co -b topic/2 trunk // And repeat…

  4. ChrisMarch 05, 2008 @ 07:20 AM

    The way git-svn decides where to commit to is by finding the svn url from the most recent git-svn-id

    If you do a git log you will see the git-svn-id in the output. The first one you see will be where dcommit is headed.

    The way I am setup is with release branches so git branch would show 1.0 1.1 1.2

    Now when I start a feature on 1.2 I git checkout -b new-feature 1.2

    Do my work ….. I might also be working on multiple features at once so let’s say i have new-feature and new-feature2 both starting from 1.2

    When I want to commit to svn I decide what order, so I want new-feature first and new-feature2 second which means I’ll do git checkout new-feature2 git rebase new-feature

    now when I merge new-feature and new-feature2 into 1.2 I will have a fastforward merge. svn seems to not like any other type of merge. I then dcommit from 1.2

    Also if i need to move a feature back to 1.1 I would use git-format-patch and git-apply

Comment