Fix patch diff for merge-commit revisions

`list_commits` walked back from the revision head and stopped at the
first commit equal to `base`. A merge commit reaches `base` through one
of its parents, so the walk truncated there and dropped everything the
other parent contributed: a revision whose head merges the target branch
in looked like a single-commit revision.
This commit is contained in:
Rūdolfs Ošiņš
2026-09-03 18:22:43 +02:00
parent e35baf750f
commit 6cb6443cbd
2 changed files with 19 additions and 14 deletions
+13 -11
View File
@@ -781,18 +781,20 @@ pub trait Repo: Profile {
let profile = self.profile();
let repo = profile.storage.repository(rid)?;
let repo = surf::Repository::open(repo.path())?;
let history = repo.history(&head)?;
// Hide `base` from the walk rather than stopping at the first commit
// that equals it. A merge commit reaches `base` through one of its
// parents, so truncating there drops every commit the other parent
// contributes and leaves the revision looking like a single commit.
let mut walk = repo.backend.revwalk()?;
walk.set_sorting(git2::Sort::TOPOLOGICAL | git2::Sort::TIME)?;
walk.push(git::raw::Oid::from_str(&head)?)?;
walk.hide(git::raw::Oid::from_str(&base)?)?;
let commits = history
.take_while(|c| {
if let Ok(c) = c {
c.id.to_string() != base
} else {
false
}
})
.filter_map(|c| c.map(Into::into).ok())
let surf_repo = surf::Repository::open(repo.path())?;
let commits = walk
.filter_map(|oid| oid.ok())
.filter_map(|oid| surf_repo.commit(git::Oid::from(oid)).ok())
.map(Into::into)
.collect();
Ok(commits)
+6 -3
View File
@@ -345,13 +345,16 @@
});
// A single-commit revision has nothing to choose between, so select that
// commit automatically to surface its message.
// commit automatically to surface its message. The diff stays the
// revision's own range: a merge commit's first parent is the side it merged
// into, not the revision base, so narrowing to the commit diff there would
// show the other branch's changes instead of the patch's.
$effect(() => {
if (commitList.length === 1 && !selectedCommit) {
const only = commitList[0];
selectRevision({
headId: only.id,
baseId: only.parents[0],
headId: revision.head,
baseId: revision.base,
commitId: only.id,
commit: only,
});