Git incorrectly infers "rename"

Viewed 1409

Git has inferred a "rename" when I had no desire for it to do so (this question is effectively the opposite of, say, How to make git mark a deleted and a new file as a file move?):

  1. I created a new file, and did git add. (I did not do a git commit, as I have no desire to do so at this stage.)
  2. Later on I did git rm on another file.
  3. Now git status reports renamed: old-file -> new-file. I have not committed yet.

The two files are in the same directory, have similar-ish names and a certain amount of common content. However, I deliberately did not do a git mv, as this is not a rename, I want the two files tracked separately. If I had wanted a rename I would have done a git mv rather than my deliberate git add/git rm.

What about the activity has caused git to decide it's a rename, and can it be told not to try to infer things I don't intend?

3 Answers

Git's logical underlying storage model only stores the repo contents before and after a change, not the change itself. So it has no way of distinguishing between, say, a move+modification and a delete+add.

Thus git mv is just convenience syntax for:

mv a b
git rm a
git add b

git status is merely inferring the most likely cause of the underlying change (given before and after), in an effort to make the human-readable output useful. There are certainly pathological edge cases - in your particular case it's inferred that the change was caused by a move and a small content change.

Update based on comments discussion: If you need to make it clear what's going on here, you could (as you suggested) perform the add and rm in separate commits. This has the downside of splitting a single "logical" commit into two, though that may be unimportant.

The files are similar enough so that git status thinks that it was a rename. Under the hood, it makes no difference at all, but if you want to ensure that it doesn't happen, make separate commits where you add and delete the files:

git add newfile
git commit
git rm oldfile
git commit

Git infers renames when it detects deletions and additions of similar content (by default 50% similar) whenever diffs are computed between any pair of commits in the history. That is, Git does not record a deletion and addition as a rename, so it is irrelevant if you do git rm ... and git add ..., or simply git mv ... which is basically an alias of the other two.

Git infers renames, except if you tell it to not do so, but beware: if you tell Git to not infer renames, then it will not infer any, even those deletions and additions that you want to be paired as renames.

For more details, I suggest reading the documentation of gitdiffcore.

Related