Skip to content

fix: derive needs_restack from git, not stale parent_head - #141

Merged
rohoswagger merged 1 commit into
mainfrom
fix/needs-restack-from-git
Aug 11, 2026
Merged

fix: derive needs_restack from git, not stale parent_head#141
rohoswagger merged 1 commit into
mainfrom
fix/needs-restack-from-git

Conversation

@rohoswagger

Copy link
Copy Markdown
Owner

ez status answered "does this branch need a restack?" by comparing the parent tip against the recorded parent_head:

git::rev_parse(&meta.parent).map(|tip| tip != meta.parent_head)

That SHA is only a cache of where the branch forked from its parent. It goes stale whenever history moves outside ez — a hand-rolled git rebase, an amend in another worktree, a force-pushed parent — so a branch sitting cleanly on top of its parent was reported as needing a restack, in both --json and the human warning.

ez checkout already asked git the right question (git::is_ancestor), so the two commands disagreed about the same branch.

Fix

Shared helper in cmd/restack.rs that asks git instead of the cache:

pub(crate) fn branch_needs_restack(branch: &str, parent: &str) -> bool {
    if git::rev_parse(parent).is_err() {
        return false;
    }
    !git::is_ancestor(parent, branch)
}
  • cmd/status.rs — both the --json and human paths use it. The human path also no longer ?-fails when the parent ref is gone.
  • cmd/checkout.rs — routed through the same helper so the two can't drift apart again.

The restack engine is unchanged. It keys off parent_head too, but with a stale SHA effective_old_base already falls back to git's merge base and the resulting git rebase --onto X X branch is a no-op that just refreshes the metadata — that's the healing path, working as intended.

Tests

  • Two unit tests in restack.rs: one rebases outside ez and asserts the stale cache produces no false positive (and that a genuinely moved parent still returns true); one covers the missing-parent guard.
  • Rewrote the two integration tests in tests/navigation_status_cli.rs that were asserting the buggy behaviour — they only corrupted parent_head and expected a warning. They now advance trunk for the positive case, plus new inverse tests: stale cache alone means needs_restack: false and no warning printed.

`ez status` answered "does this branch need a restack?" by comparing the
parent tip against the recorded `parent_head`. That SHA is only a cache of
where the branch forked from its parent, and it goes stale whenever history
moves outside ez — a hand-rolled `git rebase`, an amend in another worktree,
a force-pushed parent. A branch sitting cleanly on top of its parent was
therefore reported as needing a restack, in both `--json` and the human
warning.

`ez checkout` already asked git the right question, so the two commands
disagreed about the same branch. Add a shared `restack::branch_needs_restack`
that tests whether the parent tip is in the branch's history, and route both
commands through it.

The restack engine is unchanged: with a stale `parent_head` it already falls
back to git's merge base, and the resulting no-op rebase is what refreshes
the cache.
@rohoswagger
rohoswagger merged commit 799e282 into main Aug 11, 2026
6 checks passed
rohoswagger added a commit that referenced this pull request Aug 11, 2026
Three stale-metadata and cleanup fixes since v0.3.2 (#141, #142, #143, #144).

Also backfills the 0.3.0–0.3.2 rows in the version history table, which were
never recorded.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant