How to rebase one Git repository onto another one?
Asked Answered
G

4

74

I had one Git repository (A) which contains the development of a project until a certain point. Then I lost the USB stick this repo A was on. Luckily I had a backup of the latest commit, so I could create a new repository (B) later where I imported the latest project's state and continue development. Now I recovered that lost USB stick, so I have two Git repositories.

I think I just have to rebase repo B onto repo A somehow, but I have no idea how to do that, maybe using fetch/pull and rebase?

Gregarine answered 11/3, 2010 at 19:47 Comment(0)
P
74

If A and B are not the same repo (you created B by using the latest working copy you had), you have to use a graft to pretend that they have common history.

Let’s assume you’ve added A as a remote for B as per VonC’s answer, and the repo looks like this1:

~/B$ git tnylog 
* 6506232 (HEAD, master) Latest work on B
* 799d6ae Imported backup from USB stick
~/B$ git tnylog A/master
* 33b5b16 (A/master) Head of A
* 6092517 Initial commit

Create a graft telling the root of B that its parent is the head of A:

echo '799d6aeb41095a8469d0a12167de8b45db02459c 33b5b16dde3af6f5592c2ca6a1a51d2e97357060' \
 >> .git/info/grafts

Now the two histories above will appear as one when you request the history for B. Making the graft permanent is a simple git filter-branch with no arguments. After the filter-branch, though, you aren’t on any branch, so you should git branch -D master; git checkout -b master.


1 git tnylog = git log --oneline --graph --decorate

Procession answered 11/3, 2010 at 20:15 Comment(10)
+1 for taking care of the "other scenario" were A and B differ completely in commit history.Heathenize
This was a brilliant help; did absolutely what I wanted with a minimum of fuss. Thank you!Strohl
I just used the subtree-merge approach to stitch several repositories into one. I successively failed at the attempt to rebase them one over the other. Using graft points, I've now been able to stitch them onto a single development timeline, thanks!Auberge
+1 By the way, I edited your answer to reflect what your git-tnylog does (I was puzzled and took me a while until I found it was an alias of yours: 'Pimped tnylog a bit')Placable
How would you push this to a remote? Say I host my B on bitbucket and want to push this. Simply (--force) pushing a new commit to bitbucket doesn't (appear) to add the history of A to the the remote bitbucket.Radiometeorograph
I already tried the command git filter-branch 33b5b16dde3af6f5592c2ca6a1a51d2e97357060..HEAD from link but then I can't push anymore since I get the error that the remote has unresolved deltas (remote: fatal: pack has 50 unresolved deltas).Radiometeorograph
Update: after an hour of googling and using the rebasing option from this answer, I managed to create something which could be pushed to a new bare repo.Radiometeorograph
@PimJager (or anyone else with the same query), see: How to push a “git replace --graft”.Major
Related: How do git grafts and replace differ? (Are grafts now deprecated?).Major
I'm getting an error "too many pending edits on SO" so I am going to suggest two changes here. 1. remove mention of author's personal alias "tnylog" and use the standard git command line from the footnote. 2. "Make the graft permanent by running git filter-branch with no arguments." As written, the sentence is both confusing and a bit judgmental of those who are unfamiliar with 'filter-branch'.Tally
H
46

If A and B are the same repo (the first SHA1 are common), you can:

  • declare A as a remote for B: git remote add A /path/to/A
  • git fetch A to update all remote A branches on the B repo
  • git checkout dev (on B, where you are developing)
  • git rebase A/devBranch to replay B (i.e. what you develop or re-develop from your backup) on top of A/devBranch (the development you lost). A bit like this SO question.

The last step allows you to sync your dev with the one you lost.
But actually, once you have fetch from A, you are done: B now contains the "all" history (the one you lost and your current work)

Heathenize answered 11/3, 2010 at 19:59 Comment(6)
Thanks, seems to work, just have to resolve some conflicts during the merge/rebase now :)Gregarine
@kroimon: conflicts were inevitable, I suppose, since you were re-developing in B some part of the code committed in A.Heathenize
I ended up in a detached HEAD, and had to reference this: https://mcmap.net/q/12735/-why-does-git-tell-me-quot-not-currently-on-any-branch-quot-after-i-run-quot-git-checkout-origin-lt-branch-gt-quotJuggle
@AlisonS I agree: the rebase command was incorrect. If you have checked out dev, all you need is git rebase A/devBranch in order to replay dev on top of A/devBranch. I have edited the answer.Heathenize
This worked well for me without A and B having a common first SHA1. I did an interactive rebase and dropped some initial commits.Finkelstein
This is the real answer to the question.Entice
P
6

2021 Update

git filter-branch has been deprecated since Git 2.24 (Q4 2019). Instead, for the scenario where A and B are not the same repo, you can use git replace --graft <commit> <parent>.

See:

Purlin answered 30/4, 2021 at 14:33 Comment(5)
But nevertheless git filter-branch still does not have a full replacement. The declared official replacement git filter-repo still can not fully replace it because of revealed issues: github.com/newren/git-filter-repo/issues/465 github.com/newren/git-filter-repo/issues/473Cog
because of revealed issues I suggest anybody reading that sentence to check for themselves the discussion on these "revealed issues" (at least the last messages in #473). Looks like git filter-repo works fine if one knows what it does (like everything else git I suppose)Hanforrd
@soxsupportsthemods corrupted repo and unexpected results definitely is not "works fine"Cog
@Cog from the issue discussions seems like the "corrupted repo and unexpected results" are just the way you see the normal output of that tool. That's why I added " if one knows what it does", which you left out of the quote. I won't argue those issues with you but again I suggest anybody interested in using it to read for themselves those discussions, because the author explains in great detail (and with lots of patience) why those "revealed issues" are actually the intended effects of using the tool.Hanforrd
@soxsupportsthemods This is not "intended effects", this is undocumented behaviour has found by tests. If you want to hit that by you prod repo, then go ahead, I won't argue either.Cog
B
2

First of all, start by making a working clone of repo A.

Then simply pull into it from B and merge. You might prefer to create a new branch, pull onto it, then merge the two branches. You might also need a forcing flag; I've done things like this in Mercurial (grafting two apparently-unrelated repositories together) and it needs "-f".

Biramous answered 11/3, 2010 at 20:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.