15

I'm trying to use the GCC git mirror, documented here.

Some time ago, I cloned the git repository:

git clone git://gcc.gnu.org/git/gcc.git

Added the git-svn stuff:

git svn init -Ttrunk --prefix=origin/ svn+ssh://gcc.gnu.org/svn/gcc

And then git svn rebase and git svn dcommit etc. all worked nicely.

Some months later, I've done various development work on local git branches, and I've come to commit more changes to upstream SVN:

  1. Update from the git miror:

    $ git rebase
    
  2. Ensure I have the absolute latest from SVN, but it doesn't work:

    $ git svn rebase -v
    Unable to determine upstream SVN information from working tree history
    

Somehow I've broken the meta data! As well as the above, I think I did git svn fetch at some point, by mistake, but that shouldn't be harmful, should it?

So, I tried creating a fresh branch from the remote git mirror:

$ git branch svntrunk remotes/origin/trunk
$ git checkout svntrunk
$ git svn rebase
Unable to determine upstream SVN information from working tree history

A web search suggests that the branch history has somehow diverged from SVN, but I've checked git log and every commit has a corresponding git-svn-id, which seems to disprove that, no?

So, I tried a fresh clone from git://gcc.gnu.org/git/gcc.git, and in that repository git svn rebase works fine. How can the two repos, in which both have had git rebase from the same source, have different history? Presumably they can't, and the difference is in the local meta-data?

Now, I don't want to trash the repo that I've been working in (although I could), and exporting the patches to another repo to commit defeats the point of having git-svn in the first place. So, how can I repair it?

ams
  • 24,923
  • 4
  • 54
  • 75

3 Answers3

14

You should be able to do this with a graft. Grafting is designed to be used for bringing in legacy repositories is my understanding. It lets you manually tell git that some ref has a common parent.

As mentioned in the comments, there may be a simpler solution. This would work if all else failed though.

You'd pull down the current git repo (either with git or git-svn) and add your old git-svn repo as a remote. They won't be related, so gitk will look like this:

unrelated ancestry

I had an incident at work where we had some do a file system copy instead of an svn copy, so some of my branch names allude to that. Hopefully it all makes sense anyway. Pretty lucky that I just happen to have all these images and story. What are the chances!? Probably pretty good, git-svn is easily confused.

naming branches

The copied_from branch is where they copied from. The graft_ref is where this met up. They had added a bunch of files, changes properties, and then changed some of the files. It was a bit of a mess.

The general idea is to get the index of the original source, but the file contents to match the modifications. So we start in the ref branch, then reset mixed to get the index.

$ git checkout graft_ref
$ git checkout -b graft_parent
$ git reset --mixed copied_from

reset mixed

If you didn't make changes between the two branches when it was broken, then you shouldn't have to worry about this step.

$ git commit -a -m "svn_branch changes made on svn_trunk filesystem copy"

Now we need to do the actual graft. I trimmed the hashes here for readability.

$ git log -1 --pretty=format:%H graft_ref
631d3c84e35de98236c3d36c08d14ec92f9c62ae
$ git log -1 --pretty=format:%H graft_parent
96a4e87b554e0030035d35ca3aac23e9d71962af
$ echo "631d3... 96a4e8..." > .git/info/grafts

grafted

Which looks about how you would expect. Now we just need to rebase the changes up the tree. My notes are missing for this, but I think this is what I would have done, hehe.

$ git checkout svn_branch/master
$ git checkout -b consolidate_changes

consolidated

$ git rebase master

rebase

You should be able to push these changes where ever. Grafts are not tracked by git though, so the grafted branch (svn_branch/master and friends) will break again. It is effectively a dead tree unless you do the graft again. For git-svn this isn't much of an issue because you just commit changes and then pull the source down again as a clean copy.

Tom Kerr
  • 10,444
  • 2
  • 30
  • 46
  • +1, very nice answer (from one of your blog posts, maybe...?). I'm not sure if it's directly applicable to the problem being addressed (for that, a simpler solution may be most appropriate), but as an applicable and usable procedure for grafting changes across repositories in an inconsistent state, I completely approve. :) – MrGomez Mar 23 '12 at 23:10
  • 1
    @MrGomez I hope there is a simpler solution. :)) All else fails, I am fairly certain it would work though. – Tom Kerr Mar 23 '12 at 23:16
  • So, essentially, I start from a fresh repo, insert my topic branches into the not-broken git-svn history at the right point via a graft, and use rebase to make the graft obsolete. Did I get the right end of the stick? – ams Mar 26 '12 at 10:51
  • 1
    I've now transferred the interesting parts of my work across to a new repo. Thanks. – ams Mar 27 '12 at 12:23
3

The simplest thing that might work is to run git svn fetch. If you re-attach an existing git repo to svn, you'll get this error for no good reason when you git svn rebase. Underlying problem is that git-svn makes assumptions about the state of your repo that don't always hold if you didn't start from a clean svn clone.

The next option is to backup master git branch master-bkp. And then run git reset --hard <way-back>. Then run git svn rebase.

Only if you're working on a git repo that you can in no way attach back to svn in a normal way, you can try grafting. Grafting is clever and complex, but possible. It's also hard to do right, easy to screw up, and very time consuming. Not recommended as a first option.

iwein
  • 25,788
  • 10
  • 70
  • 111
3

This is a fairly common scenario. I, too, have run into this when attempting to synchronize my newer Git repositories against my older, SVN-backed data store.

The secret is to keep SVN consistent with the working master branch in Git. The common commands to do so are, as you mentioned, git svn rebase (to unwind your changes and synchronize back with SVN) and git reset --hard (to perform a hard rollback to a previous revision when your internal index is in an inconsistent state). It took a great deal of searching to root this procedure out for me. (Take a backup copy before you perform either of these.)

The reason this appears to happen is Git becomes deeply confused when you manage two or more change histories simultaneously against the same working branch, which should be HEAD by default. If something changes in the upstream SVN repository and downstream change histories that Git cannot reconcile, it loses its place and throws the error you've listed above.

This problem is additionally addressed here. As mentioned in the top answer, you'll want to rule out the possibility that your version of git needs to be upgraded and attempt the fixes they've recommended, as well.

Community
  • 1
  • 1
MrGomez
  • 23,788
  • 45
  • 72
  • Thanks, I saw that question, but as I have git version 1.7.0.4 it did not solve my problem. So, you think that if I roll a branch back some way it might magically start working again? – ams Mar 23 '12 at 22:41
  • @ams It did for me, at least! It took me about three days of fruitless Google queries before I found this solution for my own repository. In my case, I'd been committing changes to Git tri-directionally: within its working files, from a downstream repository, and in SVN. It didn't like that one bit. – MrGomez Mar 23 '12 at 22:50
  • @ams See also, these suggestions: http://stackoverflow.com/questions/3095611/git-svn-dcommit-error-unable-to-determine-upstream-svn-information – MrGomez Mar 23 '12 at 22:56
  • Hmmm, I created a new branch from SVN revision 3, and `git svn info` still fails. Am I supposed to do something special with new branches? – ams Mar 26 '12 at 10:45
  • @ams New branches in the same, SVN-tethered Git repository appear to connect to through the same interface, which [has the same synchronization issue](http://stackoverflow.com/questions/4966085/git-svn-woes-why-oh-why-can-i-never-dcommit). What I'd do in this scenario is take a backup of my current working Git repository, create a patch of my changes from it using Git's patch facilities, then take a fresh copy from SVN and apply the patch. For complex branching, Tom Kerr's [answer](http://stackoverflow.com/a/9847545/517815) is more robust and correct. (Aren't convergent answers great? :) ) – MrGomez Mar 26 '12 at 20:45
  • Thanks @MrGomez. I'm sure your answer would have been right another day, but somehow my repo was too far gone. :( – ams Mar 27 '12 at 12:21
  • @ams No problem. Sometimes, the longer answer is, in fact, the better one. :) – MrGomez Mar 27 '12 at 17:58