3

For historical reasons I currently have the following project structure:

  • part0:

    • master
    • branch0
    • branch1
  • part1:

    • master
    • branch0
    • branch1

The binaries of the project are built by checking out commits of the same date from the two repositories, so I need e.g. part0 - master(01.04.2013) and part1 - master(01.04.2013). Checking out the parts from different dates won't work.

I now want to combine these two repositories into one, with the structure

  • comb:
    • master
    • branch0
    • branch1

where the commits in master are ordered by commit date from part0 - master and part1 - master.

If I use the described merge schemes I found, I will get a history of

part0 - master (everything) + part1 - master (everything) afterwards

So a checkout of any commit will not work, because either I only get part0 (before having merged part1) or I get the newest part0 - master with an outdated part1 - master.

4

1 回答 1

2

I finally puzzled the solution out of the links I posted above. First the problem in pictures:

repo0: A -- B -- C -- D -- E
             \
              F -- G -- H

repo1: A' -- B' -- C' -- D' -- E'
              \
               F' -- G' -- H'

Where the commits A and A' correspond and to get the project in a running form, you need to checkout A and A' simultaneously into their respective directories, say proj0 and proj1.

In the repositories the files and directories are put in /.

The target repository should look like this:

A -- A' -- B -- B' -- C -- C' -- D -- D' -- E -- E'
                 \
                  F -- F' -- G -- G' -- H -- H'

So I first did a move of all the contents of the repositories commits into the final directories with this (of course in a copy):

git clone --mirror path/repo0 repo0
git filter-branch --tree-filter "(mkdir -p proj0 ; find * -maxdepth 0 ! -iname proj0 -exec mv {} proj0/ \;)" -- --all

git clone --mirror path/repo1 repo1
git filter-branch --tree-filter "(mkdir -p proj1 ; find * -maxdepth 0 ! -iname proj1 -exec mv {} proj1/ \;)" -- --all

Now I could merge the two, first I create a new repository with the old ones as remotes:

git init new
git remote add proj0 path/repo0
git remote add proj1 path/repo1

git fetch --all

Then I merge branch by branch, I just name them BranchE and BranchH. As they exist from the beginning of the project, I had to get the very first commit, this is done with the dummy-branch:

git checkout -b dummy remotes/proj0/BranchE
git checkout -b start `git log --topo-order --reverse | head -n 1 | sed s/"commit \(.*\)"/"\1"/`
git checkout -b merge start

git merge -m "merge" remotes/proj1/BranchE
git rebase --onto start start merge

git branch -D start
git branch -D dummy
git branch -m merge BranchE

I did the same for BranchH. The drawback until now is, that the histroy looks now like this:

A -- A' -- B -- B' -- C -- C' -- D -- D' -- E -- E'
 \
  \
   \
    A' -- B -- B' -- F -- F' -- G -- G' -- H -- H'

The last thing to do is to bring together the similar parts of the history, this is done with this command:

git checkout BranchE
git rebase `git log --oneline | grep "commit comment of last common commit B'" | sed -r s/"(^[a-f0-9]+) .*"/"\1"/` BranchH

Et voilá, you get the desired result.

于 2013-05-30T18:27:11.317 回答