Sreekanth Pothanis
Sreekanth Pothanis

Reputation: 735

Git merge of moved files

I forked a repo and changed the file A. Now file A in the upstream repo has moved to a sub directory. I am trying to merge the upstream repo to my fork.

Git thinks that file A was deleted in its original location and a new one was created in the new sub directory (This could also be because, of the way they moved the file upstream).

I want to get my modifications into this file in the new location with least possible repercussions, w.r.t history of commits and stuff. Can anyone please help me out with ways to achieve this?

Upvotes: 5

Views: 4912

Answers (2)

torek
torek

Reputation: 487735

Git always detects renames "after the fact", by comparing the two trees in question (or one tree and the index, for cases that don't include git merge itself). (Linus Torvalds considers this a feature; see this SO question for instance.)

In any case, git merge will run git's internal diff with merge detection enabled and the default similarity-index of 50%, unless you configure it otherwise.1 Similarly, git diff has some defaults which are also configurable. If you run git diff --find-renames -M50% manually between your merge-base and the upstream, you're probably fine (but see footnote 1 about configuring).

If git does not detect the rename, you may need to adjust the rename detection thresholds and/or increase the number of files that git should consider. The first of these is the rename-threshold value in the -X options (rename-threshold first appeared in git 1.7.4). See the documentation for details.


1You can set merge.renameLimit to the number of files to consider in terms of rename detection. If you do not set it, the current default is 1000 files (but the default has changed over time). Also, if you do not set it, merge uses diff.renameLimit, so you can set just the second of these, and have both diff and merge use both values.

The way file rename detection works is a bit complex, but simple enough to describe by example. Suppose that git is comparing commit 12345 with commit 67890, and in 12345 there are files with pathnames A, B/C, and D; but in 67890 there are pathnames B/gronk, B/C, and D. This means path A is gone but new path B/gronk has appeared. Git will then remember such paths (up to the rename limit value), and will compare the contents of 12345:A with the contents of 67890:B/gronk. If the files are "sufficiently similar", git will declare that 12345:A was renamed to 67890:B/gronk.

I'm not sure precisely how git decides that a file is 50%, or 75%, or whatever, similar/different. I have seen that similarity index is based on "chunks" rather than lines (even though the usual diff output is line-oriented), though.

Upvotes: 13

CodeWizard
CodeWizard

Reputation: 141946

You need to use git mv and not just mv to move the file.

Since git takes a snapshot of the content it does not care about the filename (which is stored in an idx file as metadata).

If you simply move the file, git will not "understand" that you want to move it and will treat it as a new file.

You need to revert your changes and then use:

git mv <old path> <new path>

Now in git status you will see the move of the file and not the deletion of one and the creation of a new one.

Upvotes: -2

Related Questions