git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: git rename/moved status unreliable in ruby

From
Chris Torek <chris.torek@gmail.com>
Date
May 2, 2026, 08:06 UTC
Message-ID
<CAPx1Gvd_VEWHrBWtUjNeWZ+wfmsAOTamKmL6fhBSQi=MbmXRcw@mail.gmail.com>
In-Reply-To
<OsOzcjEwvHCQSghLE8LD_wHb_jDlil9I88OUuhpiRONnVd1o9p3gStbK1mx4q7OwY3ePtbZO-BBgTNOCeJ2DMyvBsdlMhRmDrTP894KP5xo=@proton.me>

On Thu, Apr 30, 2026 at 10:06 PM sebastien.stettler <sebastien.stettler@proton.me> wrote:

> ... understanding the reasoning would be great
In my opinion, the key to understanding here is this:
Git Stores Snapshots.

What this means is that every commit is a full snapshot of all of the files for that commit. There are no "changes" at all, there is only a full snapshot, every time.

Now, internally, the storage format is more complicated (and compressive, ultimately using the concept of changes as well, though not exactly the way one might expect). But from the "what things look like" point of view, and how you should think about what Git sees, each commit is simply a full and complete snapshot of every file. So if you have one commit where `foo.rb` exists, and `bar.br` does not, that snapshot has a `foo.rb` but no `bar.rb`. If you make a second snapshot, in which `foo.rb` no longer exists but `bar.rb` does now, that second snapshot, well, has those files.

The tricky part is that you normally ask Git to *compare* two snapshots (at least for "what changed" purposes). When you do that, Git extracts both snapshots and, well, compares them. If `foo.rb` has been removed and `bar.rb` has been added, Git then goes on to compare the *contents* of those two files.

If the contents match exactly, and you've asked Git to "find renames", Git will always say that the file that vanished from the first commit, only to be created identically under a new name in the second, was "renamed", rather than the one file being deleted and the second added.

If the contents match "fuzzily" (for some value and algorithm of fuzz-factor), Git may also say "renamed". You can control this with `--find-renames=<value>`. The key idea here is that Git is *finding* renames: either exact-same-contents, or "sufficiently similar" contents, based on remove-and-add pairs.

Since Git only *stores* snapshots, you can get two different results from comparing the same two commits. All you have to do to get this is to adjust whether Git checks for renames at all, and if so, to what extent.

These rules apply to `git show`, `git diff`, `git merge`, and even the diffstat that `git commit` optionally shows after a commit. For this reason, all the "compare some commits" commands -- including `git merge` -- take this `--find-renames=<value>` option. Detection of renames can be countermanded entirely with `--no-renames`.

This is why -- and when -- making two separate commits, one with "exact same content for deleted-file-D vs added-file-A", followed by later changes to new file A, helps: if you compare the commit that has file D to the middle commit, the two files match exactly, and any rename detection you have turned on finds that rename. If you then compare the middle commit to the final commit, file A exists in both, so Git shows changes to file A. But as soon as you compare the original file-D-containing commit to the final file-A-updated commit, you run into the original issue again: to detect this as a rename, you may need to allow rather generous rename detection.

If, in the future, Git gets fancier rename detection, comparing the original commit directly against the final one could find the rename automatically. So:

> If this is considered something that can be improved ...

It *could* be improved. Doing so in a way that works for more than just some special cases -- e.g., in a way that works for ordinary text, or graphical images, for instance, rather than just for Ruby sources (or just C sources, or C++, or Swift, or Python, or whatever) -- seems particularly tricky. Some degree of ignoring white-space changes would probably help multiple cases, though.

Chris
Previous: Junio C HamanoNext: sebastien.stettler
Message 5 of 9 in “git rename/moved status unreliable in ruby”
  1. sebastien.stettlerMay 1, 2026
  2. Phillip WoodMay 1, 2026
  3. Johannes SixtMay 2, 2026
  4. Junio C HamanoMay 3, 2026
  5. Chris TorekMay 2, 2026
  6. sebastien.stettlerMay 2, 2026
  7. Jeff KingMay 4, 2026
  8. Junio C HamanoMay 5, 2026
  9. Chris TorekMay 5, 2026

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.