Warm tip: This article is reproduced from stackoverflow.com, please click
git

Merge a branch into the folder of another branch

发布于 2020-03-27 10:29:26

I have a branch A with a folder named foo. Another branch named B has only the content of the folder foo. Is it possible to merge the branch B into the folder foo of branch A?

The branch A contains for example

foo/
    hello_world.c
goo/
    AliceAndTom.c

The branch B contains only the content of the folder foo/

./
    hello_world.c
    hello_world.h

The result I want in the branch A after merging branch B:

foo/
    hello_world.c
    hello_world.h
goo/
    AliceAndTom.c

Edit: branch B was created using git command git filter-branch --subdirectory-filter dir/to/filter -- subdir_branch

Questioner
Alan
Viewed
81
Mark Adelsberger 2019-07-04 02:59

You can't directly merge it in.

There is some confusion in the comments about whether git's rename detection would help here. Based on how you say the branch was created (with filter-branch), it most likely won't. That's because the new branch probably doesn't have a common ancestor with the old branch.[1]

So first you have to solve the problem of getting git to recognize corresponding files from the branches; and then you have to solve the problem that it still won't know the most recent common ancestor for the two current states of that file, so the merge won't go smoothly. (Each file that exists on both branches will conflict on, most likely, the entire file, not really merging anything.)

This is one of the problems that can arise from having "different content on different branches"; it's not how git is meant to work, and it can make integration of changes across branches quite tedious.

How tedious this would be depends on how much the files have changed on each branch since the filter-branch was run. The most general procedure I can think of is:

(1) write a script that moves the files back to their subdirectory

(2) run filter-branch (with the above script as the tree filter) on the newer branch, creating yet another branch with structure closer to the original

(3) identify the last commit in the new branch that corresponds to a commit in the original branch

(4) use git replace to substitute the above-identified commit in place of the corresponding commit from the original branch.

(5) Now you should be able to merge the new branch to the original branch. Then you can undo the git replace and possibly dispose of the new branch.

There are details to understand about each of these steps; it is not a simple procedure, so refer to the documentation for the involved commands if you want to pursue it.


[1] - More specifically, for rename detection to apply, git has to be looking at a patch that both removes a file, and creates another file (at a different path) with sufficiently similar content.

If you had branched from master and then, on the branch, made a commit in which you moved the files, you would now be looking at something like

x -- x -- O -- A <--(master)
           \
            x -- x -- B <--(bramch)

When merging branch to master, git will look at the patch between O and B, which meets the criteria for rename detection (as long as the moved file didn't change too much).

But filter-branch likely created a totally separate history

x -- x -- x -- A <--(master)

x -- x -- B <--(branch)

By default a merge will fail (no common history), and if you make it not fail (by saying to allow unrelated histories) then it will act as though there were a merge base with an empty TREE (i.e. no files). The patch from that to A creates foo/file, and the patch from that to B creates file, but neither patch both deletes the file at one path and creates it a the other, so rename detection isn't going to happen. The merge will create a second copy of the file.