I want to extend on another question I had: Merge two Git repositories and keep the master history
I have succeeded in merging 2 different repo's into one repo. I needed a rebase to do this successfully. The master is correct, but I also want to keep the merge history. Is this possible?
I have 2 repositories:
This is the result after rebasing. The times of the top repo are the rebase-time. The original date is lost!
This is how I did it:
# Assume the current directory is where we want the new repository to be created
# Create the new repository
git init
# Before we do a merge, we have to have an initial commit, so we'll make a dummy commit
dir > Read.md
git add .
git commit -m "initial commit"
# Add a remote for and fetch the old RepoA
git remote add -f RepoA https://github.com/DimitriDewaele/RepoA
# Do the same thing for RepoB
git remote add -f RepoB https://github.com/DimitriDewaele/RepoB
# Rebase the working branch (master) on top of repoB
git rebase RepoB/master
# Rebase the working branch (master with RepoB) on top op repoA
git rebase RepoA/master
Is it possible to have something like this? (painted solution!!!)
I would like to keep the original time + the merge history.
UPDATE - ANSWER
The answer that worked best for me, was working with graft points. But other answers are also very usable in other use cases. I have added my results on github, so everybody can evaluate.
Answer 1: Best working in my case The 'graft' did reveal the correct working answer for me.
GitHub: RepoGraft
Answer 2 the "replace" option from "LeGEC" also gives good results for some use cases. One anomaly stayed for me:
GitHub: RepoHistory
Answer 3: Worth adding The answer from 'VonC'. I could not get the option '--preserve-merges working' in my case. This might work in other scenario's, but I did not test this furtner.
As you've discovered, rebase
isn't the command you want to use to stitch histories together (because it actually rewrites history). Early Git had a feature (hack) designed specifically for what you're trying to do: graft points. Even better, since 1.6.5 you can use git replace --graft
instead:
git checkout master
git replace --graft $(git log RepoB/master --format=%H | tail -1) HEAD
git replace --graft $(git log RepoA/master --format=%H | tail -1) RepoB/master
git reset --hard RepoA/master
(git log RepoA/master --format=%H | tail -1
returns the initial commit from RepoA
)
Technically you could skip the first replace
if you don't actually have anything of value yet in master
, yielding just history with RepoB + RepoA.
These commands create entries in refs/replace/*
that can be pushed and pulled to share your revised history with others. Or, if you don't care about preserving the SHAs of RepoA/RepoB, you can make the replacements permanent by running git filter-branch --all
to produce a "real" set of commits of the desired lineage.
There are two options in git rebase that should be of interest in your case:
p
--preserve-merges
Recreate merge commits instead of flattening the history by replaying commits a merge commit introduces.
--committer-date-is-author-date
(from git am
)
By default the command records the date from the e-mail message as the commit author date, and uses the time of commit creation as the committer date. This allows the user to lie about the committer date by using the same value as the author date.
Test if the second rebase doesn't yield a better result with:
git rebase -p --committer-date-is-author-date RepoA/master
This answer suggests a different way to use RepoB
as the active repo, and still have access to RepoA
history :
use git replace
# start with a regular clone of the active repo :
$ git clone RepoB
# add repoA as a remote :
$ git remote add -f history https://github.com/DimitriDewaele/RepoA
# get hash of *initial* commit on repoB :
$ git log --oneline origin/master | tail -1
abcdef Initial commit
# get hash of last commit on repoA :
$ git log --oneline history/master | head -1
12345 Merge branch 'develop'
# use 'git replace' to tell git to stitch histories in the log :
$ git replace abcdef 12345
Note : this operation is done on your machine, not on the remote repositories, so should be repeated on all new clones.
Variant :
You may push RepoA:master
to RepoB
under a new name (e.g : RepoB:history/master
), then you can use git replace abcdef history/master
, on commits which are all stored in RepoB
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With