views:

4061

answers:

4

My motivation for trying out git-svn is the effortless merging and branching. Then I noticed that man git-svn(1) says:

"Running git-merge or git-pull is NOT recommended on a branch you plan to dcommit from. Subversion does not represent merges in any reasonable or useful fashion; so users using Subversion cannot see any merges you've made. Furthermore, if you merge or pull from a git branch that is a mirror of an SVN branch, dcommit may commit to the wrong branch."

Does this mean I cannot create a local branch from svn/trunk (or a branch), hack away, merge back into svn/trunk, then dcommit? I understand that svn users will see the same mess that merges in svn pre 1.5.x have always been, but are there any other drawbacks? That last sentence worries me, too. Do people routinely do these kinds of things?

+26  A: 

Creating local branches is definitely possible with git-svn. As long as you're just using local branches for yourself, and not trying to use git to merge between upstream svn branches, you should be fine.

I have a "master" branch that I use to track the svn server. This is the only branch that I dcommit from. If I'm doing some work, I create a topic branch and work away on it. When I want to commit it, I do the following:

  1. Commit everything to the topic branch
  2. git svn rebase (resolve any conflicts between your work and svn)
  3. git checkout master
  4. git svn rebase (this makes the next step a fast-forward merge, see Aaron's comments below)
  5. git merge topic_branch
  6. resolve any merge conflicts (there shouldn't be any at this point)
  7. git svn dcommit

I also have another situation where I need to maintain some local changes (for debugging) that should never be pushed up to svn. For that, I have the above master branch but also a branch called "work" where I normally do work. Topic branches are branched off work. When I want to commit work there, I checkout master and use cherry-pick to pick the commits from the work branch that I want to commit to svn. This is because I want to avoid committing the three local change commits. Then, I dcommit from the master branch and rebase everything.

It is worthwhile running git svn dcommit -n first to make sure that you are about to commit exactly what you intend to commit. Unlike git, rewriting history in svn is hard!

I feel that there must be a better way to merge the change on a topic branch while skipping those local change commits than using cherry-pick, so if anybody has any ideas they would be welcome.

Greg Hewgill
If I understand this correctly, in git, merging git with svn is OK, but not svn with svn? So I can't merge my svn/branch with svn/trunk in git?
Knut Eldhuset
Rewriting history in SVN is hard if you want to do something trivial. In non-trivial cases it’s practically impossible.
Aristotle Pagaltzis
Jegern: That's correct. For merging svn-to-svn, I would recommend using svn itself to do that.
Greg Hewgill
Greg Hewgill's answer has a lot of votes, but I believe it's wrong. Merging from topic_branch into master is only safe if it's just a fast-forward merge. If it's a real merge requiring a merge commit, then the merge commit will be lost when you dcommit. The next time you try to merge topic_branch into master, git thinks the first merge never happened, and all hell breaks loose. See the question [Why does git svn dcommit lose the history of merge commits for local branches?](http://stackoverflow.com/questions/425766/why-does-git-svn-dcommit-lose-the-history-of-merge-commits-for-local-branches)
Aaron
@Aaron: Good point thanks, I've updated my answer.
Greg Hewgill
@Greg Hewgill, the edit isn't clear. You wrote that the rebase "makes the next commit a fast-forward merge." I don't know what that means, because a fast-forward merge doesn't involve a commit, but perhaps you meant "makes the next merge a fast-forward merge". But if that's what you meant, it's not correct. Whether a merge from topic_branch into master is a fast-forward merge or not depends on whether any commits have been made on master since the branch point. When you rebase master, that creates new commits on master, so a subsequent merge is necessarily *not* a fast-forward merge.
Aaron
@Aaron: Yeah I don't know what I was thinking there. I've fixed it again, hopefully that makes sense. One of the troubles is that there's so many ways to do things in Git, that it takes quite some explanation to describe a *specific* sequence of actions.
Greg Hewgill
+4  A: 

A safe way to merge svn branches in git is to use git merge --squash. This will create a single commit and stop for you to add a message.

Let's say you have a topic svn branch, called svn-branch.

git svn fetch
git checkout remotes/trunk -b big-merge
git merge --squash svn-branch

at this point you have all the changes from the svn-branch squashed into one commit waiting in the index

git commit
luntain
As others have pointed out, though, this does lose the granularity of the commits.
Tchalvak
+3  A: 

Rebase the local git branch onto the master git branch then dcommit and that way it looks like you did all those commits in sequence so svn people can see it linearly as they are accustomed to. So assuming you have a local branch called topic you could do

git rebase master topic

which will then play your commits over the master branch ready for you to dcommit

Joel Jauregui
+1  A: 

I have nearly the same workflow as proposed by Greg Hewgill except that in general I rebase interactively my local commits between each dcommit so as to glue ("squash" in git terminology) then into a single commit onto the svn server. It makes the commits on SVN more efficient as otherwise, an svn revision will be added for each of your local git commit and I found it not desirable in most case.

So to summarize, here is my workflow:

  • I have a "master" branch that is the only branch that I dcommit from and that clone the SVN repository (-s assume you have a standard SVN layout in the repository trunk/, branches/, and tags/):

git svn clone [-s] <svn-url>

  • I work on a local branch "work" (-b do the checkout into "work" after branch creation)

git branch -b work

  • commit locally into the "work" branch (-s to sign-off your commit message). In the sequel, I assume you made 3 local commits

...

(work)$> git commit -s -m "msg 1"

...

(work)$> git commit -s -m "msg 2"

...

(work)$> git commit -s -m "msg 3"

Now you want to commit onto the SVN server

  • [Eventually] stash the modifications you don't want to see committed on the SVN server (often you commented some code in the main file just because you want to accelerate the compilation and focus on a given feature)

(work)$> git stash

  • rebase and resolve conflicts between "work" and "remotes/git-svn"

(work)$> git svn rebase

At this level, you will have conflicts between you local commits in the "work" branch and the last unique glued dcommit. Simply run the following command to skip the commits in "work" that were previously glued :

(work)$> git rebase --skip

Continue until the rebase is successful (resolve the eventual other conflicts). Use the following command to ensure everything is fine:

(work)$> git log --graph --oneline --decorate

  • go back to the master branch and svn rebase there (to update from the SVN server)

(work)$> git checkout master

(master)$> git svn rebase

  • Now it's time to merge with the "work" branch

(master)$> git merge work

Resolve any merge conflicts (there shouldn't be any at this point)

  • Now you want your 3 commits to look into a single one on the SVN server. All you need to do is to type:

git rebase -i HEAD~3

This launch your $EDITOR and allow you to perform what's referred to as an "interactive rebase". The idea is to "pick" your first commit and "squash" the others. You should end with something like that:

pick   08464ae msg 1
squash 8750643 msg 2
squash af6f7ae msg 3

# Rebase 05e760e..21e20fa onto 05e760e
#
# Commands:
#  pick = use commit
#  edit = use commit, but stop for amending
#  squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
# 

Save and quit the editor. You will then have your $EDITOR that is executed to edit your commits (good to maintain a single sign-off signature)

  • finally commit on the SVN server

(master)$> git svn dcommit

  • Go back to work and eventually recover your stashed files:

(master)$> git checkout work

(work)$> git stash pop

Sebastien Varrette