Skip to content

Instantly share code, notes, and snippets.

@mletterle
Last active November 26, 2021 08:56
Show Gist options
  • Save mletterle/8048086 to your computer and use it in GitHub Desktop.
Save mletterle/8048086 to your computer and use it in GitHub Desktop.
Replay Two Git Repositories History Into A New One, Chronologically

Combining Two (or more potentially) Git Repos

###Moving one repo into a subdirectory of the other

Preserves notes and sets commit details to author details

in newrepo

git filter-branch --index-filter \
    'git ls-files -s | \
        sed "s-\t-&'"[[new subdir]]"'/-" | \
        GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && \
        mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE
    ' --commit-filter 'export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export NEW_COMMIT=$(git commit-tree "$@"); git notes copy $GIT_COMMIT $NEW_COMMIT > /dev/null 2>&1; echo $NEW_COMMIT' repo2

###Preserving Notes

NOTE: there appears to be a patch to allow this to work with cherry-pick, but today (1.8.5) it appears only rebase will work.

This should be done after the initial merge creating the new root commit but before the rebase/cherry-pick script.

git notes add -m "deleteme"
git notes remove

git fetch ../repo1 refs/notes/commits:refs/notes/repo1
git fetch ../repo2 refs/notes/commits:refs/notes/repo2

git notes merge refs/notes/repo1
git notes merge refs/notes/repo2

git config notes.rewriteRef refs/notes/commits

###Using Cherry-Pick

cherry-pick will essentially replay all the diffs in order, creating a new set of commits in the order you want.

mkdir newrepo
cd newrepo
git init
git fetch ../repo1 master:repo1
git fetch ../repo2 master:repo2
git merge [[new root sha]]
(git log --format=format:"%ct %H" repo1 && echo -e "\n" && git log --format=format:"%ct %H" repo2 ) | sort -g | gawk '{print $2}' | tail -n +3 | git cherry-pick --stdin

###Using Rebase

(git log --format=format:"%ct %H" repo1 && echo -e "\n" && git log --format=format:"%ct %H" repo2 ) | sort -g | gawk '{print $2}' | tail -n +3 | while read line; do git checkout $line -b $line-branch; git rebase master; git checkout master;  git merge $line-branch; git branch -D $line-branch; done;

###Reset committer data

This will reset committer and commit date to the original author and author date, while preserving notes:

git filter-branch --commit-filter 'export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export NEW_COMMIT=$(git commit-tree "$@"); git notes copy $GIT_COMMIT $NEW_COMMIT > /dev/null 2>&1; echo $NEW_COMMIT'
@hfossli
Copy link

hfossli commented Mar 7, 2017

I have a problem using these scripts. It gets stuck on merge commits.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment