Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Git rename detection when class and filename changed in one commit

What is the best way to handle class renames (e.g. done with Resharper) with Git?

That is, if both the class name and containing file name are changed together and committed without further changes.

It seems the way Git handles renames via a percentage changed heuristic is a bit hit and miss. For large classes it will be recognised as a rename but for small classes the percentage threshold is reached such that it will be seen as a delete and add.

like image 431
Alan Christensen Avatar asked Jun 28 '11 21:06

Alan Christensen


People also ask

How do I get git to recognize a rename?

You have access to the command git uses to do this: `$git diff -M`. The command `$git diff` shows you the individual changes in a file, a branch, or the whole repo if you want, depending on what you pass in. The `-M` flag stands for “detect move” or, for our purposes, detect a rename. Detect renames.

How does git know when a file is renamed?

In Git's case, it will try to auto-detect renames or moves on git add or git commit; if a file is deleted and a new file is created, and those files have a majority of lines in common, Git will automatically detect that the file was moved and git mv isn't necessary.

Does git track file name changes?

Git tracks content, not files, so it doesn't matter how you get your index into the proper state - add+rm or mv - it produces the same result. Git then uses its rename/copy detection to let you know it was a rename.

How do I commit case-sensitive only filename changes in git?

Git has a configuration setting that tells it whether to expect a case-sensitive or insensitive file system: core. ignorecase . To tell Git to be case-senstive, simply set this setting to false . (Be careful if you have already pushed the files, then you should first move them given the other answers).


1 Answers

Keep in mind that in Git's history, file renames are not stored as "this was renamed from X to Y". Instead, the file X exists in one revision, and in the next revision Y exists (and X doesn't). For example:

Revision | Files
---------+----------------------------------
HEAD^    | a.cpp    x.cpp             z.cpp
HEAD     | a.cpp              y.cpp   z.cpp

In the above diagram, each revision is a row and each contains three files. Between the two revisions, x.cpp was renamed to y.cpp. The only information that the repository stores is the contents of each separate revision.

When Git (or another tool that reads Git repositories) looks at the above history, it notices that y.cpp is a new file in HEAD. Then it looks at the previous revision to see whether a similar file existed. In the case of a straight file rename, then yes, a file called x.cpp with the identical contents existed in the previous revision (and no longer exists in the current revision). So the new file is shown as a rename from x.cpp to y.cpp.

In the case of a rename-and-modify, Git will look at the previous revision's files to see if one file looks close to the new file (in terms of its contents). This is where the heuristic comes in. If most of the lines are the same, then Git will show it as a rename, but if there are enough changed lines compared to unchanged lines, then Git will simply say it looks like a new file.

To answer your question, the best way to handle resharper class renames is to simply do it and commit the new files. Git stores the old and the new files in its repository. Rename detection is handled later, at the time you actually ask about the history. This is why commands such as git log have options like --find-copies and --find-copies-harder.

like image 134
Greg Hewgill Avatar answered Oct 21 '22 22:10

Greg Hewgill