Using Git for SVN Repositories Workflow

For the last months I have been using Git to work with my Subversion repositories. Besides from reducing disk usage, Git also makes my work slightly faster and independent of network access — I can make commits to repository which is sync’ed later when I get online.

One challenge though: Using branches

I ran into some pitfalls when using branches in Subversion repository. Here is a quick guide to how I setup and work with svn branches in git-svn.

Useful git-svn command cheatsheet

For the lazy reader, let’s just start with the compressed cheatsheet :)

[code lang=”bash”]git svn clone -s [url_to_svn_repository] [project_name]

git branch -a #show all branches local(git) and remote(svn)

git status

git svn info #show svn details (also URL)

#create local Git branch that mirrors remote svn branch

git checkout -b local/RELEASE-0.12 RELEASE-0.12 #will connect with a remote svn branch named ‘RELEASE-0.12’

git svn dcommit –dry-run # -n will show which svn branch you will commit into:

#=> Committing to http://svn.edgewall.org/repos/trac/branches/0.11-stable …

git svn rebase # pull changes from svn repository

git svn dcommit # push your local git commits to svn repository

[/code]

Step 1: Clone your repository with trunk, branches and tags

[code lang=”bash”]git svn clone -s [url_to_svn_repository] [project_name][/code]

The -s flag is important: It tells git to clone a standard svn layout with /trunk, /tags, /branches. NOTE that you have to use this -s flag to work with branches! Very important :)

Example: The Trac code repository has this layout by default. To make a git clone we do:

[code lang=”bash”]git clone -s http://svn.edgewall.org/repos/trac trac.git

Initialized empty Git repository in /Users/jesper/othercode/trac.git/.git/
A svntrac/__init__.py

[…]

M trac/admin/console.py
r7937 = 2aab87158be06edb1a1931240a453cac762c4615 (trunk)
Auto packing your repository for optimum performance. You may also
run “git gc” manually. See “git help gc” for more information.
Counting objects: 13630, done.
Compressing objects: 100% (13146/13146), done.
Writing objects: 100% (13630/13630), done.
Total 13630 (delta 10584), reused 0 (delta 0)
Removing duplicate objects: 100% (256/256), done.
Checked out HEAD:
http://svn.edgewall.org/repos/trac/trunk r7937
[/code]

SVN info shows the remote svn url:

[code lang=”bash”]cd trac.git/
git svn info

Path: .
URL: http://svn.edgewall.org/repos/trac/trunk
Repository Root: http://svn.edgewall.org/repos/trac
Repository UUID: af82e41b-90c4-0310-8c96-b1721e28e2e2
Revision: 7937
Node Kind: directory
Schedule: normal
Last Changed Author: rblank
Last Changed Rev: 7937
Last Changed Date: 2009-03-07 03:52:37 +0100 (Sat, 07 Mar 2009)
[/code]

Step 2: Create local branch to mirror svn branch

git branch -a shows all branches and tags (both local and remote ones)

Use git branch -a to see all remote branches:

[code lang=”bash”]
git branch -a

* master
0.10-stable
0.11-stable
cboos-dev
cmlenz-dev
debian
debian-dev
jonas-dev
tags/debian
tags/debian-dev
tags/trac-0.10
tags/trac-0.10.1
tags/trac-0.10.2
tags/trac-0.10.3
[…]
trunk
utopiste-dev
[/code]

Create local branch with same name as remote branch:

[code lang=”bash”]git checkout -b local/0.11-stable 0.11-stable

Switched to a new branch “local/0.11-stable”
[/code]

Step 3: verify you are on new branch

[code lang=”bash”]git svn dcommit –dry-run

Committing to http://svn.edgewall.org/repos/trac/branches/0.11-stable …
[/code]

(or git svn info)

[code lang=”bash”]git svn info

git svn info
Path: .
URL: http://svn.edgewall.org/repos/trac/branches/0.11-stable
Repository Root: http://svn.edgewall.org/repos/trac
Repository UUID: af82e41b-90c4-0310-8c96-b1721e28e2e2
Revision: 7930
Node Kind: directory
Schedule: normal
Last Changed Author: rblank
Last Changed Rev: 7930
Last Changed Date: 2009-03-03 00:41:48 +0100 (Tue, 03 Mar 2009)
[/code]

Step 4: work with code

now you are free to work on the local branch: ‘local/0.11-stable’. Hack on and commit to git with commands like:

[code lang=”bash”]git status

git add filename

git commit -m ‘my commit comment’

[/code]

Repeat as much as you like. There are two strategies: Work on the master branch and then commit to the svn branch later.

Step 5: commit to svn

[code lang=”bash”]git svn rebase #to pull changes from svn before commit

git svn dcommit[/code]

If you get an error like this:

[code lang=”bash”]git svn dcommit
Cannot dcommit with a dirty index. Commit your changes first, or stash them with `git stash’.
at /usr/local/libexec/git-core//git-svn line 431[/code]

Just do what it says:

[code lang=”bash”]git stash

git svn dcommit

git stash apply[/code]

The last line gets your dirty files back in your working directory.
Applying change to multiple branches

Assume your change now is applied to the branch ‘0.11-stable’.

[code lang=”bash”]git checkout master
git log –1
#=> shows log of your latest commit
#To apply these changes to Trunk, now you should change branch and merge:

git checkout local/0.11-stable
git merge master #will merge changes to the current branch (local/0.11-stable) from trunk/master.

git svn dcommit #to commit the merged changes into the svn repository[/code]

Related posts:

* Using Local File-based Git — Server Laziness (Feb 21st)
* Git Side Benefit: Reducing Disk Usage (Jan 17th)
* Dr Nic’s Gitify command: Going offline without your favourite Subversion repository? (Nov 2007)

8 Responses to “Using Git for SVN Repositories Workflow”

  1. Simon B. Says:

    Excellent article! I’m bookmarking it :)
    Some corrections needed though: –dry-run is showing with only one leading dash, blamed be your blog editor?

    >The last line gets your dirty files back in your repository.
    Isn’t that rather “back in your working directory”?

    And in the last step I’m confused. It seems you’re taking news from the svn repo’s 0.11 branch and putting them into the svn repo’s Trunk branch, together with any local changes you’ve made? But following the flow of your example before that gives an impression your changes in the git repo are not related to the from-0.11-to-trunk merge, so they really should go as separate commits? Or dcommits… :)

  2. Jesper Rønn-Jensen Says:

    @Simon B. thanks for your comments

    –dry-run is with double dashes but WordPress has decided to change it. Thomas and I might apply a patch but it’s a very annoying default for WordPress

    I changed the line to end with “…back in your working directory” — nice catch!

    The last step. I changed the wording so that the workflow implied I worked on Trunk and merged from trunk to branch. I think that makes it a little easier to understand.

    Besides, what I might wanted to do was creating a separate Git branch for a change — say “git checkout -b bug_35”

    And then merge that branch into both trunk and the release branch.

  3. Jesper Rønn-Jensen Says:

    This blog post also explains git/svn branch workflow pretty well:
    Quick Start: using git (for Windows) with Google Code Project Hosting by Ariya Hidayat

  4. Jacob Andresen Says:

    Cool article! I did not know that git could do this. Thanks :)

  5. Matthew Says:

    git’s decentralized features are ideal for someone leading up the kernel of Linux. With commit-ers to linux kernal’s 22,000+ files from around the world, this makes the decentralized model of git the best fit. i.e. for Linus Torvalds Kernel group.

    git implementation experience for the repository I administer has shown a full week needed for repository migration. The subversion repository(s) and all the right branches need to migrate into git. Then build scripts need factoring. The second week of migration is in progress but it looks like its pretty will complete.

    In the vast majority of source code management configurations (including what I recently used successfully with svn), there needs to be a central server anyway. So what is gained by pushing the decentralized git to behave as a centralized server.

    True the merging algorithms are better with git, but that’s a hard sell against the considerable costs. My git colleagues say I’m still in the anger stage of the svn grieving process: denial, anger, bargaining, acceptance.

  6. stephen Says:

    Im messing around with git and svn at the minute i have an empty trunk on my svn and then i branched it to make it

    trunk
    branches/sprint_1

    then i cloned it in git svn with branches / tags / trunk

    and worked in my branches/sprint_1 then did dcommit now everything is in branches/sprint_1 on my svn as i expected but now i want to put that on the trunk, however svn head is point to branches/sprint_1

    when i do
    git checkout master and then git merge sprint_1 the git svn info show the url has changed form /trunk to /branches/sprint_1

    any idea on how i get get /branches/sprint_1 onto the trunk when trunk is behind in the revision

    cheers

  7. benson margulies Says:

    There seems to be a giant disagreement about the safety of the following workflow:

    1) git svn init with the full set of branch and tag args.
    2) git branch to create a local branch
    3) git checkout the local branch
    4) do work
    5) git checkout master
    6) git merge local branch
    7) git dcommit

    What’s your belief?

  8. Erik Peterson Says:

    @benson
    I use the same workflow you present and its worked well for me. Our svn repos don’t have much branching. So I simply keep the svn trunk in sync with git master.