Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using git to merge two directories with common parent (but different SHA1)

In our project somebody took a copy of the code. They started a new git repository in their directory and continued their work there. Meanwhile the work on our main project repository also continued. We are now left with two git repositories with no common SHA1.

A --- B --- C --- D
      |
      | Copy of code to new directory
      |
      E --- F --- G

B and E are the exact same files, folders, etc.

Our goal is to use Git to merge changes F and G into the main branch (currently at D)

like image 544
HNygard Avatar asked Jan 14 '23 13:01

HNygard


1 Answers

First, I'm going to redraw your diagram.

A --- B --- C --- D  master

         E --- F --- G  copy

Let's first put both of these in the same repo. There are no special steps to this, you just git fetch both into the same repository.

Then you create a graft. Edit .git/info/grafts...

<E-sha1> <B-sha1>

Then run git filter-branch to make it permanent.

git filter-branch --tag-name-filter cat -- --all

This will create a new history, the "correct" history:

A --- B --- C --- D  master
        \
         E --- F --- G  copy

After that, you can just merge.

Demo in action

I created two repositories which matched the diagram, copying from commit B to make commit E.

  • E has SHA-1 3aa6b69127d20ac42746c39be3c273a9d80fb706
  • B has SHA-1 95b0fc836dbea7eaa0d043390df841d184af7cd5
$ git init
$ git remote add master ../gittest
$ git remote add copy ../gittest2
$ git fetch master
remote: Counting objects: 12, done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 12 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (12/12), done.
From ../gittest
 * [new branch]      master     -> master/master
$ git checkout -b master master/master
Branch master set up to track remote branch master from master.
Already on 'master'
$ git fetch copy
warning: no common commits
remote: Counting objects: 9, done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 9 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (9/9), done.
From ../gittest2
 * [new branch]      master     -> copy/master
$ git log --format=oneline copy/master | tail -n 1
3aa6b69127d20ac42746c39be3c273a9d80fb706 E
$ git log --format=oneline master/master
1532332fe705931babe9db04c8d84051867b52c9 D
90903c0ac8dc26649b875ee00ea39bfd7571b1fb C
95b0fc836dbea7eaa0d043390df841d184af7cd5 B
e9de90c82a32041cff3a19f8e40d4358bc4ec2ca Commit A
$ git log --format=oneline master/master
bec0e7af9374e4c57cb87f3fd5b2fb2b6a3f431b G
a61e01d096e81c36bcc450afd98ca94cff909622 F
3aa6b69127d20ac42746c39be3c273a9d80fb706 E

What happens if you merge now?

I got merge conflicts that I'd rather auto-resolve.

$ git merge copy/master
Auto-merging test.txt
CONFLICT (add/add): Merge conflict in test.txt
Automatic merge failed; fix conflicts and then commit the result.

Grafts in action

$ echo 3aa6b69127d20ac42746c39be3c273a9d80fb706 \
    95b0fc836dbea7eaa0d043390df841d184af7cd5 > .git/info/grafts
$ git filter-branch --tag-name-filter cat -- --all
Rewrite bec0e7af9374e4c57cb87f3fd5b2fb2b6a3f431b (7/7)
WARNING: Ref 'refs/heads/master' is unchanged
Ref 'refs/remotes/copy/master' was rewritten
WARNING: Ref 'refs/remotes/master/master' is unchanged
$ git merge copy/master
Auto-merging test.txt
Merge made by the 'recursive' strategy.
 test.txt |    5 +++++
 1 file changed, 5 insertions(+)

The final result

$ git log --graph --oneline | cat
*   4dadb0a Merge remote-tracking branch 'copy/master'
|\  
| * bb2fc85 G
| * be88bc5 F
| * 0816baf E
* | 1532332 D
* | 90903c0 C
|/  
* 95b0fc8 B
* e9de90c Commit A
like image 85
Dietrich Epp Avatar answered Jan 29 '23 02:01

Dietrich Epp