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:
+45
-2
@@ -4334,6 +4334,29 @@ pub async fn list_repository_files(path: String) -> Result<Vec<GitRepositoryFile
|
||||
.await
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct FileHistoryCommit {
|
||||
#[serde(flatten)]
|
||||
commit: GitCommit,
|
||||
matches_working_tree: bool,
|
||||
}
|
||||
|
||||
fn annotate_file_history(repo: &Path, file: &str, commits: Vec<GitCommit>, cancellation: Option<&SearchCancellation>) -> Result<Vec<FileHistoryCommit>, String> {
|
||||
// Keep folder history unchanged: Git diff would omit untracked children.
|
||||
let regular_file = fs::symlink_metadata(repo.join(file)).is_ok_and(|metadata| metadata.is_file());
|
||||
let mut result = Vec::with_capacity(commits.len());
|
||||
for (index, commit) in commits.into_iter().enumerate() {
|
||||
check_search_cancelled(cancellation)?;
|
||||
let matches_working_tree = index == 0 && regular_file && run_git_cancellable(
|
||||
repo, ["diff", "--quiet", "--no-ext-diff", "--no-textconv", &commit.hash, "--", file],
|
||||
cancellation, "Could not compare current file version",
|
||||
).is_ok();
|
||||
check_search_cancelled(cancellation)?;
|
||||
result.push(FileHistoryCommit { commit, matches_working_tree });
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn list_file_history(
|
||||
path: String,
|
||||
@@ -4341,7 +4364,7 @@ pub async fn list_file_history(
|
||||
limit: Option<u32>,
|
||||
request_id: Option<String>,
|
||||
state: tauri::State<'_, SearchCancellationState>,
|
||||
) -> Result<Vec<GitCommit>, String> {
|
||||
) -> Result<Vec<FileHistoryCommit>, String> {
|
||||
let state = state.inner().clone();
|
||||
|
||||
tauri::async_runtime::spawn_blocking(move || {
|
||||
@@ -4354,7 +4377,8 @@ pub async fn list_file_history(
|
||||
search_id: request_id.clone(),
|
||||
});
|
||||
|
||||
let result = list_file_history_core(&repo, file, limit, cancellation.as_ref());
|
||||
let result = list_file_history_core(&repo, file.clone(), limit, cancellation.as_ref())
|
||||
.and_then(|commits| annotate_file_history(&repo, &file, commits, cancellation.as_ref()));
|
||||
|
||||
if let Some(request_id) = request_id.as_deref() {
|
||||
let _ = state.clear(request_id);
|
||||
@@ -10275,6 +10299,25 @@ mod tests {
|
||||
assert_eq!(commits[1].summary, "init");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file_history_marks_only_an_identical_current_file() {
|
||||
let repo = init_temp_repo("history_current_version");
|
||||
commit_initial_file(&repo.path);
|
||||
let matches = || {
|
||||
let commits = list_file_history_core(&repo.path, "old.txt".into(), Some(10), None).unwrap();
|
||||
annotate_file_history(&repo.path, "old.txt", commits, None).unwrap()[0].matches_working_tree
|
||||
};
|
||||
assert!(matches());
|
||||
fs::write(repo.path.join("old.txt"), "local changes\n").unwrap();
|
||||
assert!(!matches());
|
||||
run_git_test(&repo.path, ["add", "old.txt"]);
|
||||
assert!(!matches());
|
||||
run_git_test(&repo.path, ["commit", "-q", "-m", "updated"]);
|
||||
assert!(matches());
|
||||
fs::remove_file(repo.path.join("old.txt")).unwrap();
|
||||
assert!(!matches());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_file_history_returns_commits_for_selected_folder() {
|
||||
let repo = init_temp_repo("folder_history");
|
||||
|
||||
Reference in New Issue
Block a user