4

In my current project I am facing an interesting problem with git:

Since the beginning of the project changes were stored in a SVN repository on a remote server at my client. In the progress of development I started to have a local git repository in parallel to make it easier for me to test new features without breaking the current version. Sadly git-svn failed to work which would have make my life easier.

Now my client switch to another server and in this process they moved the SVN repo to git (using git2svn).

Although I'm happy about this in general, I have two problems now:

  1. Since I worked on a bigger feature I didn't commit to SVN for 7 days. I worked in a local feature branch and made backups of that to my local server but the SVN copy which is now the remote git repo is outdated compared to my local version.

  2. Since the remote repo has been created by git2svn it is totally different from my own repo (message: Warning: Repo has no common commits.) - which makes standard merging impossible.

Now my desired goals:

  1. Merge both repos to have my current version be the checked in one and push to origin works again.

  2. Keep the history of both repos (old ones from SVN as well as the ones from the last 7 days from my git)

What I tried so far:

I tried to clone the remote git (from svn) and merged my local repo into it. I got 153 conflicts with "changed in both versions". Accepting "theirs" (i.e. my latest developments) loses the history of the file (it just starts with the initialisation with my parallel git repo).

My idea is that I can create a patch for each commit of the last 7 days and commit this into the new repo with the corresponding commit message (i.e. "manual merge"). Before I write a script to do this, I wanted to ask if there is a built-in way to do that.

Thanks in advance!

UPDATE: I tried many solutions but every one of them failed me. The basic problem is that I have two branches now with not a single common commit but the same tree structure. This results in merge conflicts like "added in both branches" because git doesn't know that myFile.txt" andmyFile.txt" are the same and can be merged. Instead I have to do a manual merge of the 150 files which I changed in that week - which I can't and won't do.

My best approach so far was to create a patch for the changes and apply that to the "new" repo. But I didn't get around to create a patch that won't fail yet due to a tree mismatch. I have yet to find the right commit to start the patch info from.

Resolution

TL;DR: There is none. You can just choose between the lesser evil.

  1. You can use the merge method desribed here but it will replace the individual file history from the SVN with the one from your side-git (i.e. you can't lookup why this file has been changed 3 month ago if your other repo is just 2 months old). But on the positive side you still have your changes merged together in the global history via git log.

  2. The other option I ended up with was to copy and replace all the files in the "new" (aka SVN) repo with the current status of my git repo (via cp -vru but omitting the .git-folder and all generated files). This let me loose the history worth of one week but still let me look back into the past until the beginning of the project which I prefer. To ease the pain of this loss a bit I crafted a detailed summary of my doings in the commit message using git log --date=short --pretty="format:%cd - %s" --name-status which at least gives me the chance to go back to this entry and see a message with a description. But of course this won't work for file deletions and you can't know which change actually belongs to which part of the biiiig commit message.

Community
  • 1
  • 1
Stefan Hoth
  • 2,675
  • 3
  • 22
  • 33
  • I cannot see why you could not follow poke's suggestion to (1) checkout the branch `mine-master` of the `mine` repository and (2) run `git rebase master mine-master` to replay the work of `mine` on top of the work kept in the `master`. – JJD Nov 10 '12 at 11:32

2 Answers2

3

I just tested this with a repository of mine. I created made a new clone which represents your client’s new repository. Then I copied the contents of an older version to a new folder, and initialized a repository in it. I added the contents, and made a few commits.

That way I had two repositories with no common commit.

Now, in the new repository, I added my copy (in the following: mine) as a remote and fetched its contents. Then I checked out a new branch to mine’s master:

git checkout -b new mine/master

So I had all the history of my separate repository accessible from this branch. Next I merged the updated master into my branch, using a recursive merge strategy but favoring our (new) changes for conflicts:

git merge master -s recursive -Xours

This will automatically merge everything and in case of conflicts it will automatically resolve those by just using our versions, effectively throwing away the changes in master.

So, you should end up with a merged branch and all the history from both repositories is still there.

poke
  • 369,085
  • 72
  • 557
  • 602
  • Thank you for your detailed description and your efforts. I indeed end up with a merged repository but: For most of the files the SVN-history is lost and replaced by the history of the newer git one. – Stefan Hoth Nov 08 '12 at 13:13
  • No, the history is not lost (you can still access it in the log etc.). What you mean is that changes in the files you edited as well is lost. That is because you just use your version for any conflicting file. If you don’t want that, you will have to fix the merge conflicts manually yourself. – poke Nov 08 '12 at 13:18
  • Oh ok, I understand. But how would I see the "other", seemingly lost, history? What command would I use for that? – Stefan Hoth Nov 08 '12 at 13:25
  • You can use `git log` as the simplest example; or use `gitk` to see it in the graphical browser. – poke Nov 08 '12 at 13:26
  • While I was hoping to safe the individual file history as well I am happy enough to have merged my changes into the new repo without losing the info where I did what in the time in between. Thanks for your help. – Stefan Hoth Nov 08 '12 at 14:59
  • 1
    You have individual file history, but you need to understand that Git’s history is not necessarily linear. Usually it will diverge and merge all the time. You can see an individual file’s history (that is the parts of the global history, that affected the file) using `git log -- ` btw. Also note that Git does not track individual files but always the content of the full repository in a commit, so technically, there is no individual file for Git. – poke Nov 08 '12 at 17:51
1

If I understand correctly, you have a situation like this:

remote repo:
A - B

your repo
C - D - E - F

Where commit A is actually identical to C, and commit B to D (in regard to files contained). I belive that what might work is simply rebasing your work on the latest remote commits, by doing something like

git rebase --onto B D

This should take your commits from D to your HEAD (e.g. F), and apply them as patches to B, resulting in this:

A - B - E' - F'

This then should be identical to your local changes, and could be confortably pushed to the remote repository.

che
  • 12,097
  • 7
  • 42
  • 71
  • Hmmm, thanks for your quick answer but this didn't work. I did `git rebase --onto newgitrepo/master myfeaturebranch` but now the history of the featurebranch is not visible in this branch anymore. Did I do it from the wrong direction? Wouldn't it be better anyway to try to get the latest feature-changes into the newgitrepo because this would be compatible to push? – Stefan Hoth Nov 06 '12 at 14:39
  • The argument of `rebase` should be the first commit that you do not want to include. I think that in the diagrams above, `myfeaturebranch` would point to commit `F`, but what you want is one of its ancestors -- `D` (that corresponds to `newgitrepo/master`, or `B` in the diagram). – che Nov 06 '12 at 16:50
  • It doesn't seem to work. Either direction I try for rebasing I still end with either other history missing. – Stefan Hoth Nov 06 '12 at 22:08
  • So you did find the correct upstream commit (which is something different from both newgitrepo/master and myfeaturebranch)? – che Nov 06 '12 at 22:12
  • Problem is that they are never 100% in sync due to the async commits. Because while the SVN existed I committed issue-based (feature commits) and for git I just did "bulk commits" ( `git -am "Catch up with SVN" `). I now feel bad but at the time git was only useful in between. :( – Stefan Hoth Nov 07 '12 at 01:21
  • @StefanHoth Please check out the `rebase --onto` workflow once again [here](http://stackoverflow.com/a/12646996/356895). If the post does not help you please come back to me. – JJD Nov 08 '12 at 00:31
  • @JJD I tried to apply it for my case but it failed. I added some info in the original post to clarify my problem. Maybe that helps to help me. PS: With which program did you do the image in your post? – Stefan Hoth Nov 08 '12 at 11:40
  • @StefanHoth I used the tools from www.cacoo.com. I will reread your problem tonight or could pass you by if you are c-base tonight. – JJD Nov 09 '12 at 15:13
  • @JJD Thanks for the offer but I'm not in town right now. PS: o_O – Stefan Hoth Nov 09 '12 at 17:58