fix: stop reading a parent-only fork item as deleted in the fork (#10467)

* fix: stop reading a parent-only fork item as deleted in the fork

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* style: condense the deploy-direction helper comments

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: keep the ambiguous half of a one-sided diff out of bulk defaults

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: disable select-all on a removal-only list and cover the hidden source-only row

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: keep parent-only items out of the fork merge list entirely

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: count the fork banner's ahead/behind with the compare page's predicate

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: open the direction the fork banner's button offers

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* chore: cache the sqlx query for the source-only visibility test

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: don't read an unloaded comparison as nothing to deploy

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: treat an in-flight comparison as unknown in the fork banner

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ruben Fiszel
2026-08-03 18:14:41 +00:00
committed by GitHub
co-authored by Claude Opus 5
parent 0827fd285b
commit 689f5d7c75
12 changed files with 303 additions and 84 deletions
@@ -9585,16 +9585,28 @@ async fn compare_workspaces(
.count(),
};
let all_ahead_items_visible = summary.total_ahead
== confirmed_diffs
.iter()
// Each direction accounts for what it carries (see the frontend's
// `diffActionableInDirection`): a lineage merge leaves out a row the fork lacks,
// while the update takes it whatever the counters say — and since it can carry
// `behind = 0`, that side counts rows rather than sums.
let source_only = |d: &WorkspaceDiffRow| {
d.exists_in_source.unwrap_or(false) && !d.exists_in_fork.unwrap_or(false)
};
let merge_carries = |d: &WorkspaceDiffRow| !is_lineage_pair || !source_only(d);
let ahead_sum = |rows: &[WorkspaceDiffRow]| {
rows.iter()
.filter(|d| merge_carries(d))
.map(|s| s.ahead)
.fold(0, |acc, s| acc + s.try_into().unwrap_or(0));
.fold(0i64, |acc, s| acc + i64::from(s))
};
let all_ahead_items_visible = ahead_sum(&visible_diffs) == ahead_sum(&confirmed_diffs);
let all_behind_items_visible = summary.total_behind
== confirmed_diffs
.iter()
.map(|s| s.behind)
.fold(0, |acc, s| acc + s.try_into().unwrap_or(0));
.fold(0, |acc, s| acc + s.try_into().unwrap_or(0))
&& visible_diffs.iter().filter(|d| source_only(d)).count()
== confirmed_diffs.iter().filter(|d| source_only(d)).count();
// Blast-radius guard for the "changes not visible to your user" warning
// (which hides the deploy button). The flag is a pure visibility guarantee —
@@ -9634,7 +9646,9 @@ async fn compare_workspaces(
if visible_keys.contains(&(d.kind.as_str(), d.path.as_str())) {
continue;
}
if d.ahead > 0 {
// Both sides mirror the flags above: a row is only withheld from a direction
// that would have carried it.
if d.ahead > 0 && merge_carries(d) {
hidden_ahead.total += 1;
*hidden_ahead.by_kind.entry(d.kind.clone()).or_default() += 1;
if sees_all_items {
@@ -9643,7 +9657,7 @@ async fn compare_workspaces(
.push(HiddenItem { kind: d.kind.clone(), path: d.path.clone() });
}
}
if d.behind > 0 {
if d.behind > 0 || source_only(d) {
hidden_behind.total += 1;
*hidden_behind.by_kind.entry(d.kind.clone()).or_default() += 1;
if sees_all_items {