feat(file-history): mark latest history entry identical to working tree

Annotate file history entries with matches_working_tree and surface that
information in the UI so the most recent commit can be identified as
"current version" when its content equals the working tree.

- Backend: add FileHistoryCommit and annotate_file_history(...) which checks
  (only for regular files) whether the newest commit's blob matches the
  working tree via `git diff --quiet`. list_file_history now returns the
  annotated commits.
- Types: add optional matches_working_tree to GitCommit shape used by the UI.
- UI: show a "Current version"/"Aktueller Stand" badge and disable Diff/Restore
  actions for entries that match the working tree (text localized for de/en).

Also add a test that verifies the matching behavior across file edits,
staging, committing and deletion. No external API breaking changes.
This commit is contained in:
2026-09-18 20:20:46 +02:00
parent aeacb50ea6
commit 096f62907c
4 changed files with 57 additions and 4 deletions
+1
View File
@@ -6730,6 +6730,7 @@
<module.default
{fileHistory}
filePath={selectedExplorerPath}
language={appLanguage}
{isBusy}
isLoading={fileHistoryLoading}
error={fileHistoryError}
+9 -2
View File
@@ -13,6 +13,7 @@
isBusy: boolean;
isLoading: boolean;
error?: string;
language?: "de" | "en";
onDiff: (commit: GitCommit) => void;
onRestore: (commit: GitCommit) => void;
onClose: () => void;
@@ -24,6 +25,7 @@
isBusy = false,
isLoading = false,
error = "",
language = "en",
onDiff = () => {},
onRestore = () => {},
onClose = () => {},
@@ -86,14 +88,15 @@
<div>
<strong title={item.summary}>{item.summary}</strong>
<span><code>{item.short_hash}</code> · {item.author_name}</span>
{#if item.matches_working_tree}<span class="current-version">{language === "de" ? "Aktueller Stand" : "Current version"}</span>{/if}
</div>
</div>
<time datetime={item.date}>{formatCommitDate(item.date)}</time>
<div class="file-history-dialog-actions">
<button class="btn-sm" type="button" onclick={() => onDiff(item)} disabled={isBusy} title="Show changes against the working tree">
<button class="btn-sm" type="button" onclick={() => onDiff(item)} disabled={isBusy || item.matches_working_tree} title={item.matches_working_tree ? (language === "de" ? "Identisch mit der aktuellen Datei" : "Identical to the current file") : "Show changes against the working tree"}>
<GitCompare size={14} aria-hidden="true" /> Diff
</button>
<button class="btn-sm" type="button" onclick={() => onRestore(item)} disabled={isBusy} title="Restore this file from the selected commit">
<button class="btn-sm" type="button" onclick={() => onRestore(item)} disabled={isBusy || item.matches_working_tree} title={item.matches_working_tree ? (language === "de" ? "Identisch mit der aktuellen Datei" : "Identical to the current file") : "Restore this file from the selected commit"}>
<RotateCcw size={14} aria-hidden="true" /> Restore
</button>
</div>
@@ -104,3 +107,7 @@
</div>
</div>
</div>
<style>
.file-history-dialog-commit .current-version{display:inline-flex;width:fit-content;margin-top:4px;padding:2px 6px;border:1px solid color-mix(in srgb,var(--color-accent) 25%,transparent);border-radius:4px;background:color-mix(in srgb,var(--color-accent) 8%,transparent);color:var(--color-accent);font-size:10px;font-weight:600}
</style>
+2
View File
@@ -260,6 +260,8 @@ export interface GitStash {
}
export interface GitCommit {
/** Set for the latest file-history entry when its diff against the working tree is empty. */
matches_working_tree?: boolean;
hash: string;
short_hash: string;
summary: string;