From c747e02f298e73b5a56357fb78562b6fc2dbaed5 Mon Sep 17 00:00:00 2001 From: Christoph Brandau Date: Fri, 10 Jul 2026 22:56:48 +0200 Subject: [PATCH] feat(git): improve interactive rebase base handling and reflog display The git module now correctly handles scenarios where the selected base branch is diverged from HEAD. This removes unnecessary ancestor checks during rebase planning, improving overall robustness of the feature. Additionally, the ReflogDialog component was updated to reliably retrieve the current HEAD hash for accurate display in the UI. - Removed strict ancestor checking when starting interactive rebase operations. - Updated App.svelte to accurately find and use the current HEAD entry from reflog data. --- src-tauri/src/git.rs | 47 ++++++++++++++++++++++---------------------- src/App.svelte | 2 +- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src-tauri/src/git.rs b/src-tauri/src/git.rs index d67b573..4179308 100644 --- a/src-tauri/src/git.rs +++ b/src-tauri/src/git.rs @@ -1625,11 +1625,6 @@ pub async fn start_interactive_rebase( } let base_hash = verify_commit(&repo, &base)?; - ensure_ancestor( - &repo, - &base_hash, - "The selected base must be an ancestor of HEAD.", - )?; let available = interactive_rebase_commits_for_repo(&repo, &base_hash)?; validate_rebase_plan(&available, &plan)?; @@ -1765,11 +1760,6 @@ fn interactive_rebase_commits_for_repo( base: &str, ) -> Result, String> { let base_hash = verify_commit(repo, base)?; - ensure_ancestor( - repo, - &base_hash, - "The selected base must be an ancestor of HEAD.", - )?; let range = format!("{base_hash}..HEAD"); let merges = run_git(repo, ["rev-list", "--merges", range.as_str()])?; @@ -1809,20 +1799,6 @@ fn interactive_rebase_commits_for_repo( Ok(commits) } -fn ensure_ancestor(repo: &Path, commit: &str, message: &str) -> Result<(), String> { - let output = git_command() - .arg("-C") - .arg(repo) - .args(["merge-base", "--is-ancestor", commit, "HEAD"]) - .output() - .map_err(|err| format!("Could not start Git. Is Git installed? {err}"))?; - if output.status.success() { - Ok(()) - } else { - Err(message.to_string()) - } -} - fn validate_rebase_plan(commits: &[RebaseCommit], plan: &[RebasePlanItem]) -> Result<(), String> { if commits.is_empty() { return Err("There are no commits to rebase onto the selected base.".to_string()); @@ -5126,6 +5102,29 @@ mod tests { ); } + #[test] + fn interactive_rebase_accepts_a_diverged_base_branch() { + let repo = init_temp_repo("interactive_rebase_diverged"); + commit_initial_file(&repo.path); + let main_branch = git_output_test(&repo.path, ["branch", "--show-current"]); + run_git_test(&repo.path, ["checkout", "-q", "-b", "feature"]); + fs::write(repo.path.join("feature.txt"), "feature\n") + .expect("feature file should be written"); + run_git_test(&repo.path, ["add", "feature.txt"]); + run_git_test(&repo.path, ["commit", "-q", "-m", "feature commit"]); + run_git_test(&repo.path, ["checkout", "-q", main_branch.as_str()]); + fs::write(repo.path.join("main.txt"), "main\n").expect("main file should be written"); + run_git_test(&repo.path, ["add", "main.txt"]); + run_git_test(&repo.path, ["commit", "-q", "-m", "main advanced"]); + run_git_test(&repo.path, ["checkout", "-q", "feature"]); + + let commits = interactive_rebase_commits_for_repo(&repo.path, &main_branch) + .expect("diverged base should be accepted"); + + assert_eq!(commits.len(), 1); + assert_eq!(commits[0].summary, "feature commit"); + } + #[test] fn reflog_restore_creates_a_recovery_branch_without_resetting_existing_branch() { let repo = init_temp_repo("reflog_restore"); diff --git a/src/App.svelte b/src/App.svelte index bf6d18a..abac9f0 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -4221,7 +4221,7 @@ {#if reflogOpen} entry.selector === "HEAD@{0}")?.hash ?? ""} isLoading={reflogLoading} {isBusy} {operation}