Easiest way to replay commits on new git repository
Asked Answered
M

2

26

I've been using git-svn, and recently, I've been getting errors when trying to commit (I think this is due to a bug in libneon, but this is beyond the scope of this question). The solution has been to re-clone my git repository using git svn clone. However, I have changes on the master branch in my old git repository that I was not able to commit to svn using git svn dcommit. I'd like to replay these changes on the new repository cloned with git svn. I think I could probably export a patch-set using git format-patch, and then replay these changes on the new repository but I'm not entirely sure how to do this, and I wonder if there's an even easier or more elegant way to accomplish this.

Metaprotein answered 17/3, 2011 at 14:56 Comment(0)
H
56

From your new repository, add a remote reference to your old repository:

git remote add temp file:///path/to/old/repo/on/your/machine

Fetch from the old repo:

git fetch temp

Check out your master branch from the old repo:

git checkout temp/master -b wip

(wip stands for work-in-progress)

Rebase the changes on top of the stuff in your present repository:

git rebase master

Update master to point at your new HEAD:

git checkout master
git merge wip

Delete the remote reference to your old repo and the wip branch that you used:

git branch -d wip
git remote rm temp

What you are actually doing:

Firstly, by adding the remote reference and fetching, you are pulling the commits from your previous repository which you do not yet have in your current repository. Git knows how to do this because, no matter where or how it was made, the same commit looks the same everywhere. It is the SHA1 hash of a few well-known pieces of information, including the directory tree, the committer, timestamp, ...

So when you created a new Git repository based on the same SVN repository, all the commits had the same SHA1 sums. As a result, the new commits that you fetched into your current repository continued to point at the right stuff. This is very cool, and important to remember.

You then switched to the tip of temp's master branch and told it to rebase onto your current master. The rebase may have been unnecessary, as master from SVN may not have moved away from the master in your old repo, but it was best to be safe.

A rebase finds the nearest point of commonality between two commits by working backwards through their histories until they both point at the same parent commit. It then switches to the branch name you gave it (in this case master) and cherry-picks each of the commits that were missing from its history from your original branch. Once complete, it points the branch you were on when you started at the last commit it applied.

Finally, the merge of master with wip was just to fast-forward master to the end. As it was a straight line, it really was just a fast-forward. You could have just as easily done a rebase or a reset --hard; any of these would have altered the master branch to point at the correct location. Merge was just the safest of these, because if something weird had happened, it would have let you know that it was not a simple fast-forward.

Haroun answered 17/3, 2011 at 15:48 Comment(1)
Worked flawlessly, thank you. I'd like to better understand the process involved here, so could you explain at a high level how this works? In particular, I think that git rebase master plays back the changes in temp/master onto master, but I don't understand how it knows to do that. Also, what is the point of merging wip after rebasing? Thanks!Metaprotein
K
9

You could:

  • add your old repo as a remote of your new repo (git remote)
  • fetch the old branch
  • rebase the part of the old branch you are interested in on top of your current master.

See How to cherry pick a range of commits and merge into another branch:

# go to your current but incomplete new master branch
git checkout master

# mark your current master HEAD as branch 'tmp'
git checkout -b tmp

# reset your master to the old one
git branch -f master oldrepo/master

# replay the right commits on top of 'tmp' (which was your master HEAD)
git rebase --onto tmp first_SHA-1_of_old_master_to_replay~1 master

# remove tmp branch, 
# your master HEAD is now on top of tmp, with the right commits replayed
git branch -d tmp

That solution will work even if the SHA1 differs from your old master history and your new master from your second git-svn clone.
That is why I recommend a rebase --onto: the history of both masters might have nothing in common if, for any reason, the second git-svn clone don't generate exactly the same SHA1.

Kokoruda answered 17/3, 2011 at 16:17 Comment(2)
My SHA1 sums were not exactly the same, and rebase --onto saved me from endless conflicts. Thanks!Peloria
@DanielHershcovich: you are welcome, and thanks for the edits (I approved them)Kokoruda

© 2022 - 2024 — McMap. All rights reserved.