I have 2 branches. In first file named index.html is edited and commited. In second this file edited and moved to other folder. Can I make GIT understand (while merging) that index.html from 1st branch is the same file "folder/index.html" form second branch. In my opinion the problem is that when I moved file into a new folder GIT first deleted it and then created it in new folder. So after merging I will have 1 file with both changes?
-
Are the two versions of the file identical, except being in different folders? – mkrieger1 Dec 12 '17 at 16:30
-
What exactly are you doing (which commands are you running?), and what exactly is the problem you are encountering? – mkrieger1 Dec 12 '17 at 16:32
-
And what is the result you are trying to achieve? – mkrieger1 Dec 12 '17 at 16:35
-
Now, after merge I have 2 files index.html. One in old folder and the other in new folder. And I need to have the only one with changes from both branches. – Evgenii Dec 12 '17 at 16:39
-
It looks like you can try to tweak the `find-renames` option (`-X find-renames=25%` or whatever), but if that doesn’t work you’ll need to merge the files manually. Once you do there’s nothing for Git to remember; each commit is stored as its tree, not a list of changes, so once you have the merged file that’s all you need. – Daniel H Dec 12 '17 at 16:53
2 Answers
Git should already know they are the same file. See the answer to this question here. Is it possible to move/rename files in git and maintain their history?. If you type git log on moved file you see the commit after the move. The following command should have the full history.
git log --follow folder/index.html

- 106
- 6
Edit: I now believe I misread the question this morning, and there is only one modified file in your branch, derived from a single source. In which case, the description below is accurate, but tells us that Git should find the renamed file on its own, as long as it's sufficiently similar. If not, you can add and adjust the -X find-renames=number
argument to git merge
. You may want to run some manual git diff
s first, with --find-renames=number
(or the shorter spelling, -Mnumber
) to find out what numbers make Git see the merge-base to --ours
change as a rename.
If you have two modified files in your own branch, both derived from a single source file, Git's git merge
won't help you. You will have to merge this file on your own, perhaps using git merge-file
. See the final section below for instructions.
There are three inputs to git merge
The fundamental problem here is that git merge
looks at exactly three commits.1 We need names for these commits and Git is not terribly consistent about these names: sometimes they're --ours
and --theirs
for the two branch tip commits, and sometimes they're local and remote. I'll use L, for left or local or --ours
, and R for right or remote or --theirs
. The third commit is in some ways the most important, but Git finds this third commit on its own: we have no control over which commit it chooses. We choose only the L and R commits. The third commit is the merge base, and in short,2 it's where the two branches first come back together:
o--...--o--o <-- L
/
...--o--*
\
o--o--...--o <-- R
or:
...--o--o--o--*--o <-- L
\ \
o--o--o--o <-- R
In both of these graphs, the commit marked *
is the merge base.
1For simplicity, we'll assume a normal merge here, of two heads, with a single merge base commit. Criss-cross merge cases, with two or more merge bases, and octopus merges that have more than two heads, complicate this a lot, but are not the case we care about anyway.
2This is a little too short, but suffices for most cases.
Git makes two diffs
Now that Git has found the merge base—the L commit is just our current branch tip, and the R commit is the one you specify when you run git merge
—and has its three inputs, Git runs git diff
twice:
git diff --find-renames base L # what we did
git diff --find-renames base R # what they did
The first git diff
compares all the files stored in the merge-base commit—remember, each commit is a snapshot of all files—to all the files stored in L, to see what we changed in --ours
. The --find-renames
option makes this particular git diff
check for files that were renamed, but does not make it check for files that were copied. The rename detector runs with the default "at least 50% similar" (you can find detailed descriptions of how this rename detector works in some of my other answers: diff rename, copy, and break detection and similarity index), but you can tweak the percentage with -X find-renames=n
.
The second git diff
does the same thing but with their R commit.
Then, git merge
goes on to combine the two sets of differences.
If one or both of the two diffs detects that index.html
in the base was renamed to some other path(s) in one or both of the L and R commits, Git will take one of the renames (and declare a high-level rename/rename conflict if both of these commits renamed the base file). Again, though, there's no option for turning on copy detection, and Git will just see any copy as a new file in one or both of L and R. This could result in a high-level add/add conflict, if it's added in both; or if it is just added in one commit, Git assumes it should be added in the new commit.
If there are no high-level conflicts and no low-level conflicts, git merge
will normally make a new commit immediately. You can suppress this with --no-commit
and do additional work to the work-tree. (The result is what some call an evil merge. You should be aware of this, and at least mark the commit somehow, e.g., in the commit message. Alternatively, make a normal merge, followed by a fixup commit. There are no perfect answers to this particular problem.)
Manual merge
To manually merge a copied file, extract the merge base version—you can find the merge base commit hash ID with git merge-base
—and both tip versions into three ordinary files, then use git merge-file
on it. Note that by default, git merge-file
writes its output over one of the three inputs—this is likely what you will want, since the HEAD or --ours
version is already in your work-tree, under the name you are using for it, so all you really have to do is extract the base and --theirs
or R versions:
git show base-hash:base-path > newname.base
git show theirs:theirs-path > newname.theirs
git merge-file newname newname.base newname.theirs
then remove the .base
and .theirs
versions when you are done merging. (It's wise to inspect the resulting merge, as always, since Git just obeys simple line-at-a-time rules for combining diffs.)

- 448,244
- 59
- 642
- 775