Skip to content

fix: parse git's compact {old => new} rename notation in commit file list#188

Closed
rubensa wants to merge 1 commit into
phil294:masterfrom
rubensa:fix/brace-rename-parsing
Closed

fix: parse git's compact {old => new} rename notation in commit file list#188
rubensa wants to merge 1 commit into
phil294:masterfrom
rubensa:fix/brace-rename-parsing

Conversation

@rubensa

@rubensa rubensa commented Jul 1, 2026

Copy link
Copy Markdown

Fixes #187.

Problem

When a commit renames a directory, git reports the moved files with its compact
rename notation, e.g. src/{old => new}/file.txt. The file-list parser
(git_numstat_summary_to_changes_array in web/src/components/CommitDiff.vue, fed by
git show --numstat --summary) only understood the full-path form old => new.

For a numstat row like src/{old => new}/file.txt it did:

path = path.split(' => ')[0]?.replaceAll('{', '') || ''
// "src/{old => new}/file.txt"
//   .split(' => ')[0]   -> "src/{old"
//   .replaceAll('{','') -> "src/old"   <-- truncated at the brace; "/file.txt" is lost

So every file renamed under the same directory collapsed onto the same key
(src/old) and the entries overwrote each other in the accumulator — only the last one
survived. The --summary rename branch had the same assumption, so it couldn't reconcile
either. Result: files silently missing from the commit's file list, and a basename that
also appeared as a plain add/delete pair could show up duplicated.

Fix

Add a small split_rename() helper that expands git's compact form
prefix/{old => new}/suffix (and the degenerate {old => new}, dir/{ => new}/f,
dir/{old => }/f, and plain old => new) into the real old/new paths, and key the
entries on the real old path in both the numstat branch and the --summary rename
branch. No change to the git command or to the FileDiff shape.

Verification

Built and type-checked locally against a fresh checkout:

  • npm run build (vite + esbuild) — passes.

  • vue-tsc --noEmitno new errors from this change (the one remaining
    CommitDiff.vue diagnostic is pre-existing and present on the unmodified tree, just
    line-shifted).

  • Functional check on the directory-rename repro below (src/old/src/new/):

    • before: 1 of 2 files shown — both brace-renames collapse into one bogus
      src/old key, so the second overwrites the first
    • after: both files shown, each with correct rename metadata

    The number of dropped files scales with the directory: an N-file directory rename
    collapses all N brace-renamed entries onto a single key, so only one survives (plus
    any siblings that happen to appear as plain add/delete pairs, which can then look
    duplicated).

Note: npm run lint currently crashes in a fresh checkout on src/global.d.ts
(a @typescript-eslint/TS version issue, unrelated to this change), so I couldn't run
the lint gate — the type-check and build above cover this file.

Reproduce the original bug

git init demo && cd demo
mkdir -p src/old
printf 'a\n'   > src/old/keep.txt
printf 'a\nb\n' > src/old/edit.txt
git add -A && git commit -m init
git mv src/old src/new                 # rename the directory (both files move)
git commit -am "rename dir"

Open the rename dir commit: before this fix at least one renamed file is missing from
the list; with more files in the directory, several go missing and a basename can appear
duplicated.

…list

Files renamed under a renamed directory collapsed onto a single key and
overwrote each other, so only some appeared in the file list (and a plain
add/delete sibling could show duplicated). Add split_rename() to expand the
brace form to the real old/new paths instead of truncating at the '{'.
@rubensa

rubensa commented Jul 1, 2026

Copy link
Copy Markdown
Author

Superseded by #189.

While validating this fix I found that once renamed files correctly appear in the file list, opening their diff is still broken (#131) — the diff/view handlers use a single path for both revisions, so the child side resolves the pre-rename path (which doesn't exist there) and renders empty. That shares the same root cause as this PR (renames not tracked through to per-revision paths), so #189 fixes both #187 and #131 together and includes this PR's change as its first commit.

Closing in favour of #189.

@rubensa rubensa closed this Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Files missing/duplicated in commit file list when a directory is renamed ({old => new} brace notation not parsed)

1 participant