Engineering 24 min read

Rename a Note and Rewrite It in One Commit, and Git Loses Its History

MMNMNOTE
gitversion-historyrenamemarkdownnotesengineering

Git never records that you renamed a note. Every time you run git log or git diff, it guesses: a deleted file and a new file count as a rename only if more than half the content is unchanged.1 Rename a note and rewrite most of it in the same commit, and its history stops at the rename.

That is not a bug report. It is the design, and it has been the design since April 2005. Pro Git, the book on the official git site, says it in two sentences: "Unlike many other VCSs, Git doesn't explicitly track file movement. If you rename a file in Git, no metadata is stored in Git that tells it you renamed the file."2

For a folder of Markdown notes, that design meets a habit. Titles are filenames, so notes get renamed all the time — and the rename usually arrives with a rewrite, because you renamed the note because it changed. In our test on git 2.43.0, a 20-line note renamed with 12 lines rewritten in one commit returned a git log --follow history of exactly one commit.

If you keep your notes under git — the setup in Undo Is Not a Time Machine — this is the follow-up about renames. It covers the 50% line that actually ends a note's history and what the famous 1,000-file limit really breaks, which is less than most answers claim. Then the one habit that prevents both, and how to dig out history you have already lost.


Git never records a rename — it guesses one each time you look

A commit in git stores a snapshot of every file's content and path, and nothing else about movement. When you ask for history, git compares two snapshots, sees a path disappear and another appear, and decides on the spot whether they are the same note. The guess is recomputed every time you look.

Linus Torvalds argued for this on the git mailing list on 15 April 2005, while git itself was new. "First off, let's just posit that "files" do not matter. The only thing that matters is how "content" moved in the tree."3 Recording renames at commit time, he wrote in the same mail, "only gets you to a not-very-interesting easy 1% of the solution (ie full file renames)."3

His case was that detection improves over time, and a recorded rename freezes yesterday's guess into the repository forever. Git kept that bargain. Since git 2.9.0 in 2016, git diff and git log run rename detection by default.4 The setting is diff.renames, and the manual says it "Defaults to true."5

The cost of the bargain lands on one specific person: the one who renames and rewrites.

Because nothing is stored, the question "did git keep my note's history?" has no fixed answer. It depends on two things measured at the moment you look — how similar the old and new content are, and how many files changed alongside it.

The 50% line: where a note's history actually ends

A renamed note keeps its history only if git judges the old and new versions similar enough. The default bar is 50%: more than half of the file must be unchanged. Rewrite more than half of a note in the same commit that renames it, and git sees one note deleted and an unrelated note created.

The manual states the rule under -M, the rename-detection flag: "-M90% means Git should consider a delete/add pair to be a rename if more than 90% of the file hasn't changed," and "The default similarity index is 50%."1 Similarity is measured as the "amount of addition/deletions compared to the file's size"1 — content, not line count, so the exact percentage depends on what you wrote.

Here is the measurement. A 20-line note, meeting.md, gets two ordinary commits. Then it is renamed to 2026-10-11-roadmap.md and lines 1 to 12 are rewritten — 12 of 20 — in a single commit:

$ git mv meeting.md 2026-10-11-roadmap.md
$ # rewrite lines 1–12 of the 20-line note
$ git add 2026-10-11-roadmap.md
$ git commit -m "c3 rename+rewrite 12/20"
$ git show --stat -M --format= HEAD
 2026-10-11-roadmap.md | 20 ++++++++++++++++++++
 meeting.md            | 20 --------------------
 2 files changed, 20 insertions(+), 20 deletions(-)
$ git log --follow --oneline -- 2026-10-11-roadmap.md
e72b677 c3 rename+rewrite 12/20

Twenty lines added, twenty deleted, no rename. git log --follow — the flag whose whole job is to "Continue listing the history of a file beyond renames"6 — returns one commit. The two earlier commits are still in the repository. They are no longer connected to the note's current name.

Git measured this pair at 40% similar, under the bar. Nothing warned. Nothing failed.

That silence is the problem. The commit succeeds, the note reads correctly, and the loss only shows up months later, when you run git log on the note and its past begins on the day you renamed it.

The naive fix — git mv — changes nothing

The folk answer to "how do I rename a file in git and keep its history?" is to use git mv instead of renaming in Finder or a script. It does not help. git mv is a shortcut for moving the file, removing the old path, and adding the new one. Nothing about the rename is stored either way.

Pro Git is direct about it: "Git figures out that it's a rename implicitly, so it doesn't matter if you rename a file that way or with the mv command."2 The receipt above used git mv. It made no difference, because the decision happens later, at read time, from content.

The question has drawn steady traffic for sixteen years. "Is it possible to move/rename files in Git and maintain their history?" — asked on Stack Overflow on 22 February 2010 — had a score of 866 and 523,482 views as of 11 October 2026.7 The answer was always the same mechanism: history follows content, so keep the content similar across the commit that moves it.

What does matter is the order of operations. That is the habit at the end of this post.

How git decides: three passes, cheapest first

Rename detection runs three passes, cheapest first. Exact moves (identical content) are paired first. Then, since git 2.31, files that changed directory but kept their filename are checked against each other. Only what is left goes to the expensive pass, which compares every remaining deleted file against every remaining added one.

In order:

  1. Exact renames. Identical content under a new path is matched immediately. A move with no edit is always found.
  2. Same filename, different folder. The manual describes "a preliminary step that first checks if files are moved across directories while keeping their filename the same."8 It uses "a bit higher threshold" than 50%, and it does one comparison per file.8
  3. The quadratic pass. Everything still unmatched is compared pairwise — "the later quadratic step (the one that pairwise compares all unmatched files to find the "best" matches)."8 This is the only pass with a size limit.

The three passes in sequence:

flowchart TD
    A[Two snapshots to compare] --> B[Pass 1 — exact content match]
    B --> C{Unmatched files?}
    C -- No --> G[Done]
    C -- Yes --> D[Pass 2 — same filename, different folder]
    D --> E{Unmatched files?}
    E -- No --> G
    E -- Yes --> F[Pass 3 — quadratic similarity, 50% default bar]
    F --> G

Three passes, cheapest first: exact content matches are always found in O(n); same-filename directory moves (git 2.31+) run next at a higher similarity bar, also O(n); pairwise similarity scoring is O(n²), capped at about 1,000 files per side by default.

The second pass is newer than most answers about renames. Elijah Newren, a longtime git contributor, added it in a February 2021 commit titled "diffcore-rename: guide inexact rename detection based on basenames."9 His benchmark on a "mega-renames" test case dropped from 1799.937 s to 187.248 s.9

For notes, the second pass matters more than its speed-up. Move inbox/ to archive/ with every filename intact and the edits light, and git pairs the notes by name before the limit is ever consulted.

What the 1,000-file limit really breaks

The 1,000-file limit does not cut a single note's history. It applies only to the quadratic pass and the commit-wide view: git show, git log --stat, git diff across a big reorganization. Past it, git skips the pairing and that view shows mass deletion plus mass creation. git log --follow on a lightly edited note still finds the rename.

The setting is diff.renameLimit: "The number of files to consider in the exhaustive portion of copy/rename detection," and "If not set, the default value is currently 1000."10 In the source, the exhaustive pass is skipped when num_destinations * num_sources > rename_limit * rename_limit11 — a product, not a count. For a balanced move with N files out and N in, it trips just past 1,000 renamed files on each side.

Here is a vault reorganization that trips it. 1,200 notes move from inbox/ to archive/, gain a date prefix, and get one appended line each, in one commit:

$ git diff --name-status HEAD~1 HEAD | cut -c1 | sort | uniq -c
warning: exhaustive rename detection was skipped due to too many files.
warning: you may want to set your diff.renameLimit variable to at least 1200 and retry the command.
   1200 A
   1200 D
$ git -c diff.renameLimit=2400 diff --name-status HEAD~1 HEAD | cut -c1 | sort | uniq -c
   1200 R
$ git log --stat -1 --format= | tail -1
 2400 files changed, 10800 insertions(+), 9600 deletions(-)

A reviewer reading that commit sees 2,400 files changed. That is the damage — a reorganization that reads as a mass deletion. Now follow one of those notes:

$ git log --follow --name-status --format=%s -- archive/2026-10-11-note-1.md
c2

R096	inbox/note-1.md	archive/2026-10-11-note-1.md
c1

A	inbox/note-1.md

R096 is a rename at 96% similarity, and both commits are there. A second sampled note, number 777, showed the same. Following one file has one destination, so the product never comes near the bound. The limit garbles the overview; it does not orphan the note.

Three control runs pin down the edges, all on git 2.43.0:

Where does 1,000 come from? A time budget, set as a file count. Newren raised the default from 400 to 1,000 in July 2021, keeping "the original time limit of 2s for diff.renameLimit."12 On an AWS c5.xlarge, his timing table read "1300 1.995s" — about 1,300 renamed files per side in two seconds. Then: "So let's round down to nice even numbers and bump the limits from 400->1000, and from 1000->7000."12

He also explained why the budget is a count and not a timer: with a timer, "we don't discover that we won't get all the renames until after we've put in a lot of work rather than just upfront telling the user there are too many files involved."12 Merges get the larger budget. merge.renameLimit, when unset, "currently defaults to 7000."13

The habit: move in one commit, edit in the next

Commit the rename by itself, with no edits, then make the edits in a second commit. An exact move is always paired, so the move commit carries the history across, and the edit commit is an ordinary change to a file git already knows. The same 20-line note, split this way, keeps all four commits.

$ git mv meeting.md 2026-10-11-roadmap.md
$ git commit -m "c3 move only"
$ # now rewrite lines 1–12
$ git commit -am "c4 rewrite 12/20"
$ git show --stat -M --format= HEAD~1
 meeting.md => 2026-10-11-roadmap.md | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
$ git log --follow --oneline -- 2026-10-11-roadmap.md
18c8fa2 c4 rewrite 12/20
55f49d6 c3 move only
c61dc4a c2 edit
c994017 c1 create

Same edits, same final file, four commits instead of one. The only difference is where the commit boundary sits.

Notice --follow in that last command. Without it, plain git log -- 2026-10-11-roadmap.md stops at the move in our run and lists two commits. If you always want the long view for a single path, one setting makes it the default: git config log.follow true makes git log "act as if the --follow option was used when a single is given."14

The same split applies in three places where notes get reorganized:

A rename can break two things: your links, which is an app's job to repair (see The Links Between Your Notes Aren't Plain Markdown), and your history, which is this habit's job. Case-only renames, like Notes.md to notes.md, have their own trap on macOS and Windows; the case-sensitivity post covers it.

Digging out history you already lost

The history is not deleted; it is disconnected. Lower the similarity bar for one command with -M30% and git will pair a heavily rewritten note with its old name again. For a large reorganization, raise the rename limit for that one command with -c diff.renameLimit=<N>. Both are one-off lenses — neither should become a standing setting.

On the receipt from earlier, the 40% pair reappears as soon as the bar drops below it:

$ git diff -M30% --summary HEAD~1 HEAD
 rename meeting.md => 2026-10-11-roadmap.md (40%)
$ git log --follow -M30% --oneline -- 2026-10-11-roadmap.md
e72b677 c3 rename+rewrite 12/20
bfead25 c2 edit
696908e c1 create

All three commits come back. Pick the threshold from the evidence: git diff -M<n>% --summary on the suspect commit prints the similarity of every pair it finds, so you can see the note's own score before you trust the result.

For the commit-wide view, the warning tells you the number. "At least 1200" means git -c diff.renameLimit=1200 or higher for that command; in our run, both 1,200 and 2,400 restored all 1,200 renames.

Neither flag changes the repository. They change how git reads it this once. That is the right size for them, for the reason in the next section.

Trade-offs: the guess also errs the other way

Rename detection is a similarity guess, and similarity can mislead in both directions. Notes built from the same template share most of their text, so git can pair two unrelated notes as a rename. In our test, two different daily notes from one template were paired at 71% — at the default bar. Lowering the bar widens that error.

The setup: a 13-line daily-note template with frontmatter, a task list, and empty Log and Reflection sections. Delete the 1 October note and add the 2 October note in one commit, each with its own one-line entries, and git show -M --summary reports rename daily/{2026-10-01.md => 2026-10-02.md} (71%). Then git log --follow on the 2 October note lists the 1 October commit as part of its history.

That pairing is false. The two notes share a skeleton, not a past.

The exact figure moves with the text — across other fills of the same 13-line template we saw 70% to 83%, every time above the default bar. Raising the bar to -M90% made the false pair disappear. This is why -M30% belongs in a one-off dig, never in your config: in a vault full of templated notes, a permanent low bar would stitch unrelated days together.

The other limits, plainly:

Researchers have measured the same failure in software repositories — not in note vaults. In 2014, Steidl, Hummel and Juergens reported that "up to 38.9% of the files in open source systems have an incomplete history."15 Their study covered Subversion projects; in their words, "we evaluated it only on SVN-based systems."15

In 2018, Hora and colleagues found that "between 10 and 21% of changes at the method level in 15 large Java systems are untracked."16 Neither number is a rate for notes. Both say the same thing: when tools infer moves, histories split.

The receipts in one table

Every number in this post comes from a scratch repository on git 2.43.0, reproduced on 11 October 2026. The table puts the eight runs side by side: what changed in the commit, what the commit-wide view showed, and whether git log --follow on one note still reached the note's first commit.

RunOne commit containsCommit-wide view--follow on one note
R11 note renamed, 12 of 20 lines rewritten1 add + 1 delete1 of 3 commits
R1, splitmove commit, then edit commitrename, | 04 of 4 commits
R1 + -M30%(reading R1 again)rename at 40%3 of 3 commits
R2a1,200 notes moved, same names, 1 line added1,200 renames, no warning—
R2b1,200 notes moved + renamed, 1 line addedwarning; 1,200 A + 1,200 D2 of 2 (R096)
R3b1,200 two-line notes moved, 1 of 2 lines rewrittenwarning; 1,200 A + 1,200 D1 commit
R51,200 notes moved + renamed, no edits1,200 renames, no warning—
R42 unrelated daily notes from one templatefalse rename at 71%lists the other day

Read the R3b row with care — it is the one that matters most.

Those notes kept their filenames, yet the per-note follow still stopped — because a rewrite of half a two-line note drops it under the similarity bar, and the filename pass cannot rescue a pair that fails on content. The 50% line outranks every other rule in this post.

Frequently Asked Questions

These are the questions people actually type about renamed files and git history, most of them Stack Overflow titles with years of traffic behind them. Each answer below is short enough to act on, and each one points back to the receipt or the manual page that supports it.

Is it possible to move or rename files in git and keep their history?

Yes, if the content stays similar across the commit that moves them. Git stores no rename record; it pairs a deleted path with an added one when more than half the content is unchanged. Commit the move alone, then edit in a second commit. An exact move is always paired, so git log --follow keeps the full history.

Does git mv keep history better than renaming the file yourself?

No. git mv moves the file, removes the old path and adds the new one — the same result as renaming in a file manager and running git add. Pro Git says it "doesn't matter" which way you rename. What matters is how much of the note changed in the same commit.

Why does git log not show history for a renamed note?

Two possible reasons. Plain git log -- <file> stops at a rename unless you add --follow or set git config log.follow true. If --follow also stops, the likeliest cause is that the rename commit changed more than half the note, so git no longer sees a rename. Run git log --follow -M30% -- <file> once to reconnect it.

What does "exhaustive rename detection was skipped due to too many files" mean?

A single commit, or diff, moved more files than git will pair by similarity within its default budget of about 1,000 per side. Git falls back to showing those files as deleted and added. Re-run that one command with git -c diff.renameLimit=<N>, using the number the second warning line suggests.

Does the 1,000-file rename limit delete my notes' history?

No. It affects the commit-wide view — git show, git log --stat, git diff across that commit. In our 1,200-note test, git log --follow on the notes we sampled still found the rename at 96% similarity, because following one file never comes near the limit. The 50% similarity line is what cuts a single note's history.

Should I set a lower rename threshold permanently?

No. Notes built from one template share most of their text, and git already paired two unrelated daily notes as a 71% rename at the default 50% bar in our test. A permanently lower threshold makes those false pairs more common. Use -M30% as a one-off flag to recover history you already lost.

How do I stop an AI agent or a script from breaking my notes' history?

Ask for two commits: one that only moves or renames files, then one that edits them. The move commit is an exact rename, which git always pairs. The same rule covers scheduled auto-commits — run a big reorganization by hand as its own commit before the timer fires.


Our receipts ran on one git version on one operating system — so test your own vault's worst rename before you trust any percentage, ours included.

Git does not remember your renames. It remembers your content — so give it one commit where the content did not change.


mnmnote.com keeps your notes on your own device in open Markdown — plain text that git, or any tool you trust, can read.

Footnotes

  1. "git-diff Documentation," -M[<n>], --find-renames[=<n>]. Git, version 2.56.0. https://git-scm.com/docs/git-diff. Accessed 2026-10-11. ↩ ↩2 ↩3

  2. Scott Chacon and Ben Straub, Pro Git, 2nd ed. (Apress, 2014), §2.2 "Recording Changes to the Repository," "Moving Files." https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository. Accessed 2026-10-11. ↩ ↩2

  3. Linus Torvalds, "Re: Merge with git-pasky II.," git mailing list, 15 April 2005. https://lore.kernel.org/git/[email protected]/. Accessed 2026-10-11. ↩ ↩2

  4. "Git 2.9 Release Notes," Git project, June 2016. https://github.com/git/git/blob/master/Documentation/RelNotes/2.9.0.adoc. Accessed 2026-10-11. ↩

  5. "git-config Documentation," diff.renames. Git, version 2.56.0. https://git-scm.com/docs/git-config. Accessed 2026-10-11. ↩

  6. "git-log Documentation," --follow. Git, version 2.56.0. https://git-scm.com/docs/git-log. Accessed 2026-10-11. ↩ ↩2

  7. "Is it possible to move/rename files in Git and maintain their history?," Stack Overflow, question 2314652, asked 22 February 2010; score and view count as of 11 October 2026. https://stackoverflow.com/questions/2314652. Accessed 2026-10-11. ↩

  8. "gitdiffcore Documentation," "diffcore-rename: For Detecting Renames and Copies." Git, version 2.56.0. https://git-scm.com/docs/gitdiffcore. Accessed 2026-10-11. ↩ ↩2 ↩3

  9. Elijah Newren, "diffcore-rename: guide inexact rename detection based on basenames," git commit bd24aa2f97a08fdd5a4982bc6268f70c6bb7b747, 14 February 2021. https://github.com/git/git/commit/bd24aa2f97a08fdd5a4982bc6268f70c6bb7b747. Accessed 2026-10-11. ↩ ↩2

  10. "git-config Documentation," diff.renameLimit. Git, version 2.56.0. https://git-scm.com/docs/git-config. Accessed 2026-10-11. ↩

  11. Git source code, diffcore-rename.c, too_many_rename_candidates(). https://github.com/git/git/blob/master/diffcore-rename.c. Accessed 2026-10-11. ↩

  12. Elijah Newren, "rename: bump limit defaults yet again," git commit 94b82d56866793018a7a9bcbe20c1c061fa41aa8, 15 July 2021. https://github.com/git/git/commit/94b82d56866793018a7a9bcbe20c1c061fa41aa8. Accessed 2026-10-11. ↩ ↩2 ↩3

  13. "git-config Documentation," merge.renameLimit. Git, version 2.56.0. https://git-scm.com/docs/git-config. Accessed 2026-10-11. ↩

  14. "git-config Documentation," log.follow. Git, version 2.56.0. https://git-scm.com/docs/git-config. Accessed 2026-10-11. ↩

  15. Daniela Steidl, Benjamin Hummel and Elmar Juergens, "Incremental Origin Analysis of Source Code Files," Proceedings of the 11th Working Conference on Mining Software Repositories (MSR '14), pp. 42–51, 2014. DOI 10.1145/2597073.2597111. https://www.cqse.eu/publications/2014-incremental-origin-analysis-of-source-code-files.pdf. Accessed 2026-10-11. ↩ ↩2

  16. André Hora, Danilo Silva, Marco Tulio Valente and Romain Robbes, "Assessing the Threat of Untracked Changes in Software Evolution," Proceedings of the 40th International Conference on Software Engineering (ICSE '18), 2018. DOI 10.1145/3180155.3180212. https://homepages.dcc.ufmg.br/~mtov/pub/2018-icse.pdf. Accessed 2026-10-11. ↩