Search code examples
gitgit-merge

How to establish a branching relationship between an older commit of one branch with another unrelated branch


I downloaded the source code of a project when it was in state A (see the picture below). It was just normal download resulting in a local source code tree denoted by C in the image. I did not use git clone because at the time I was not familiar with git. Then I did some coding in my local project source codes, and after I got familiar with git, I decided to make this source code tree a git repository, this is D in the image. Did some changes and commits and I arrived at F. Let's call the branch where this local development occurs "branch #2". Then I forked the main development branch and fetched the branch from the fork to a local branch called "branch #1". At this time, the main development branch is in state B. Since branch #1 and branch #2 do not have a common ancestor, if I merge branch #2 into branch #1, I will get this error fatal: refusing to merge unrelated histories. One can get around this restriction by using --allow-unrelated-histories as an option to git merge but this seems to be frowned upon according to this answer "refusing to merge unrelated histories" failure while pulling to recovered repository. According this answer, a better way would be to establish a branching relationship between the two branches. In my case, it should be between D and A.

  1. How to do this?
  2. The thing is, I only know a range of commits around A when I downloaded the code. I don't know the exact one because it was a long time ago. Will it be a problem if I slightly miss A?

My problem in a schematic diagram

EDIT:

The suggested duplicate Change the root commit parent to point to another commit (connecting two independent git repositories) does not exactly deal with the same situation. In that question, the OP needs to splice the root commit of a branch (new branch) to the last commit of another branch (old branch). In my case, I want to connect the root commit of a branch to a commit in another branch that is not the last commit. The accepted answer also assumes that the last few commits of the old branch are identical to the first few commits of the new branch. The end result of the suggested answer is a linear history. While in what I want to achieve, the history of both independent branches exist as if they share a common ancestor.


Solution

  • Assuming there is a single local repo with all of that history available and assuming that A=C:

    git checkout -b my-stuff A
    git cherry-pick C..F
    

    Now your stuff is connected to A.

    Update

    If D is the first commit in that separate history then it has to be done like this:

    git checkout -b my-stuff A
    # let's get the state of the project, contents-wise, exactly like D looks like
    git restore --worktree --staged --source D -- .
    git commit -C D # use the same information for the new commit as D had (authorship and stuff)
    # if at this point you run git show you should see only the changes you introduced in the project to create D, check
    # let's wrap it up
    git cherry-pick D..F # apply all commits past D up to F
    

    That should be enough