Skip to content

fix(but-graph): don't pin an orphaned target commit after a history rewrite#14556

Open
ameyypawar wants to merge 1 commit into
gitbutlerapp:masterfrom
ameyypawar:fix/14415-orphaned-target-commit
Open

fix(but-graph): don't pin an orphaned target commit after a history rewrite#14556
ameyypawar wants to merge 1 commit into
gitbutlerapp:masterfrom
ameyypawar:fix/14415-orphaned-target-commit

Conversation

@ameyypawar

Copy link
Copy Markdown
Contributor

Fixes #14415.

Bug

After an upstream history rewrite (git filter-repo / git filter-branch + force-push), GitButler gets stuck in a perpetual "No merge-base found" / "Target branch divergence" state and keeps recreating gitbutler/workspace from the pre-rewrite commit.

Cause

The stored target commit (gitbutler.project.targetCommitId) still exists as an object but is disjoint from the rewritten target ref. In but-graph's initial_tips_from_workspace_metadata, a stored target_commit_id was pinned as an integrated tip after only checking that the object existed (repo.find_commit) — never that it was still reachable from the target. The orphaned commit got pinned, leaving the workspace with no merge-base against its target. (This matches @shanoor's root-cause analysis in the issue.)

Fix

Before pinning a stored target commit, drop it when — on a full clone — it shares no merge-base with the live target-ref tip (a genuine rewrite). The workspace then falls back to the live target tip and the merge-base is restored.

On a shallow clone the commit is kept: gix's merge-base silently treats a below-graft parent as a root, so a below-graft merge-base is indistinguishable from a real rewrite — keeping the stored commit is the conservative choice and matches prior behavior (never dropping a still-reachable target).

Test

Adds ignores_orphaned_target_commit_unreachable_from_target_ref, which pins a disjoint commit as the target and asserts the workspace resolves to the live target tip instead. Fails on master, passes here; the full but-graph suite stays green.

When a target branch's history is rewritten upstream (e.g. `git filter-repo`
or `git filter-branch` followed by a force-push), the stored target commit
(`gitbutler.project.targetCommitId`) points at a commit that still exists as an
object but shares no history with the rewritten target ref. Workspace graph
init pinned it as an integrated tip after only checking that the object
existed, so the workspace ended up with no merge-base against its target --
the perpetual "No merge-base found" / "Target branch divergence" state
described in the issue.

Before pinning a stored target commit, drop it when, on a full clone, it shares
no merge-base with the live target-ref tip (a genuine rewrite); the workspace
then falls back to the live target tip. On a shallow clone the commit is kept,
because gix's merge-base cannot distinguish a below-graft merge-base from a real
rewrite, so keeping it is the conservative choice.

Adds a regression test for the full-clone orphaned-target case.

Fixes gitbutlerapp#14415
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.

Bug: Target branch divergence/No merge-base error loop after Git history rewrite

1 participant