I have to change our code base to a monorepo alternative. That's why I have to "merge" many project inside a new project. But we want to keep git history.
My problem is this :
I have got a repository A and a fork of this repository : the B.
There are many changes in both of the repositories, and the code of the A is not compatible with the B (and B not compatible with A).
The Repository C will be the merged repository with a directory A and a directory B. The code inside the repository A will be in directory A and B in B.
In A, I create a directory "A", move all files inside this directory and commit this change in a branch "monorepo-merging". I do the same with B but in a directory "B"
I create a new project C (git init
), I add as a remote the repository A and I merge the "monorepo-merging" (git merge A/monorepo-merging --allow-unrelated-histories
and git merge B/monorepo-merging --allow-unrelated-histories
)
And it's not working as I was thinking. For each files existing in both projects there is a conflit inside directory A and directory B. It will take too much time to resolve all this one by one.
Is there a way to merge this two repository without lost git history of the two repositories and make it works easily ?
If I understand your question correctly, you have a repo A and a repo B, now you want to create a repo C and its structure will be like this
C
├── A
└── B
Let's go back to the state where you have not created the directory A in A and the directory B in B. Suppose the branch in A is branchA
and the branch in B is branchB
and the branch in C is just master
.
#!/bin/bash
git init C
cd C
git fetch /path/to/A branchA:branch_from_A
git fetch /path/to/B branchB:branch_from_B
tree_a=$(git rev-parse branch_from_A^{tree})
tree_b=$(git rev-parse branch_from_B^{tree})
tree_c=$(echo -e "040000 tree ${tree_a}\tA\n040000 tree ${tree_b}\tB" | git mktree)
git reset --hard $(git commit-tree -p branch_from_A -p branch_from_B ${tree_c} -m "merge A and B as sub directories")
/path/to/A
and /path/to/B
could be a local path or a URL to the repository.
The echo -e
part creates a tree file that describes the structure of C, where there are 2 folders A and B. The content of A is described by the tree object ${tree_a}
, and the content of B by the tree object ${tree_b}
. git mktree
creates a tree object ${tree_c}
for C from this tree file.
The git commit-tree
part creates a merge commit from ${tree_c}
, whose 1st parent is the head of branch_from_A
and 2nd parent is the head of branch_from_B
. You can swap them if you want branch_from_B
to be the 1st parent. Also the commit message is specified at the same time. You can modify it later with git commit --amend
.
After the commit is created, the git reset
command makes master
point at this commit and updates the directories and files in C.
The histories of A
and B
are merged together by this merge commit. If you check out a commit before this merge commit, you can only see the content of A or B.