18

I don't know git very well. :-/

Background

I have two unrelated git-based document repositories that I would like to combine into a single repository. I would like to preserve the original timestamps (dating back to 2005) and individual file histories. The two repos contain no branches, no folders, and there is no overlap in terms of file naming.

In ASCII-land, it looks like this:

REPO A    |-------------------------|
REPO B                    |===============|

Where the overlap denotes time.

Goal

My goal is to "zipper up" the overlapping timestamps so that the two repos look like a single, unbroken history:

REPO A+B  |-------------------==--=---============|

What I've Tried

Again, I don't know git very well, so I could have screwed something up.

First I tried to add the newer, smaller repo as a remote for the larger, older repo, fetch the changes, and commit the result. I ended with all the new repo changes lumped together in a branch after the older repo:

MERGE  |-------------------------                 -|
                                 \===============/

Next I tried rebasing (with --committer-date-is-author-date), which I thought would work, but instead I end up with one long commit history that just stacks the two repos on top of each other.

REBASE |-------------------------===============|

I haven't been able to find a way to "replay" the combined history. I was really hoping rebase would be the answer.

Answers I've Looked At

Inigo
  • 12,186
  • 5
  • 41
  • 70
Ryan
  • 605
  • 7
  • 14
  • If using the _merge_ approach, `git log --date-order` will show the commits ordered by their commit time as you're asking for, and `git log --date-order --graph` will show the branch tree as well. – David Pärsson Jun 09 '16 at 11:43

2 Answers2

12

While @codeWizard's reply was helpful, that approach didn't retain the timestamps the way I wanted. It did lead me down a rabbit hole that helped me find a solution though...

  1. Create a new, blank repository

    git init
    
  2. Add and fetch the old repositories as remotes

    git remote add -f oldRepoA ../oldRepoA
    git remote add -f oldRepoB ../oldRepoB
    
  3. Export the combined commit history by timestamp and hash, pipe the output to sort, discard the timestamps via cut, and then pipe the list of chronologically sorted hashes to xargs, which runs a shell script to export a patch for each individual hash and then immediately apply the patch to the new repo.

    git log --all --oneline --format="%at %H" | sort | cut -c12- | 
        xargs -I {} sh -c 
            'git format-patch -1 {} --stdout | 
             git am --committer-date-is-author-date'
    

The --committer-date-is-author-date is key to keeping the original timestamps. There might be a better way of doing this, but this works well enough for my use case!

Ryan
  • 605
  • 7
  • 14
  • What happened with @CodeWizard's suggestion? Each cherry-pick set the date of the commit to the current datetime? I'm [trying to do something similar to you](http://stackoverflow.com/questions/38508600/how-do-i-combine-two-git-repositories-one-a-snapshot-of-the-other-with-current), but the repositories are similar so I don't know if it will work. – NobleUplift Aug 08 '16 at 21:54
  • 1
    Yeah, this eventually results in `X: already exists in working directory Patch failed at 0001`. Would this work for a repository with multiple branches such as `master` and `develop`? – NobleUplift Aug 09 '16 at 14:59
5

You will have to write a script that will do it.

How to do it

  1. get a list of all your commits timestamps per branch

    # print out the commits time stamp & sha-1 of each commit
    # do it for all your branches
    git log --oneline --format="%at %H"
    

    enter image description here

  2. Combine the 2 lists together and sort them by the time stamp using any sort tool (sublime, unix sort etc)

  3. Checkout new branch starting the first commit you have in your files

    git checkout <first commit id>
    

    enter image description here

  4. Create new branch starting from this commit

    git checkout -b <new_branch_name>
    
  5. Loop over all the other commits and use cherry-pick to bring them into your branch (script)

    git cherry-pick <next commit id>
    
CodeWizard
  • 128,036
  • 21
  • 144
  • 167
  • Would this work for two repositories that have the same file structure, but one is a forked snapshot of the other at a latter date? I'm [trying to address this problem here](http://stackoverflow.com/questions/38508600/how-do-i-combine-two-git-repositories-one-a-snapshot-of-the-other-with-current). – NobleUplift Aug 09 '16 at 20:44