fix(git): preserve local-only commits when fetching an existing branch - #1742
Merged
Merged
Conversation
fetchBranchAt force-moved local branches to the fetched head via git branch -f (ordinary) and a forced pull/<n>/head refspec (PR), silently discarding unpushed or divergent commits. The fetch now stages into FETCH_HEAD, classifies ancestry, and moves the branch only through an atomic non-forced refspec: ahead or equal branches are left alone, behind branches fast-forward, and diverged branches fail explicitly. Adds real-git regression tests and hardens the git.fetchBranch prNumber schema. Fixes #1730
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
GitRepositoryService.fetchBranchAtpreviously force-moved local branches to the fetched head:git branch -ffor ordinary fetches and a forced+pull/<n>/head:<branch>refspec for PR fetches. A local branch holding unpushed or divergent commits was silently rewound whenever the branch was not checked out.The method now fetches into
FETCH_HEAD, checks ancestry, and moves the local branch only through a single atomic non-forced refspecgit fetch origin refs/heads/<branch>:refs/heads/<branch>:Local branch "<b>" has diverged from "<src>"; refusing to overwrite local-only commitsbefore altering the branch.origin/<branch>for ordinary fetches,FETCH_HEADfor PR fetches).The non-forced refspec also gives checked-out/linked-worktree protection and atomicity for free: Git refuses non-fast-forward moves and refs checked out in any worktree inside the same ref transaction.
Verified with a new real-git regression suite (19 tests over
RealGitExecutorand disposable bare remotes: ahead/diverged/behind/absent/equal across ordinary and PR variants, worktree protections, local and remote tag-name collisions, fetch-failure preservation), plus a live proof drivinggit.fetchBranchover the authenticated WebSocket RPC: 4/4 scenarios pass (ahead preserved, PR collision rejected explicitly, absent created, behind fast-forwarded). Targeted lint and typecheck clean;features/projects/gitsuite 122/122.Also included:
prNumberin thegit.fetchBranchcontract tightened toint().positive(), and theverify-mcodefeature docs/runtime check updated to cover the new regression file and document the explicit-failure startup behavior.Why
Fixes #1730. Fetching an existing branch (including the deferred PR fetch at thread startup) could destroy local-only work without any warning or error.
Review Notes
FETCH_HEADis a single shared slot, so two concurrentfetchBranchAtcalls on the same repo can race on the ancestry probes. The atomic refspec makes this fail-safe (worst case is a spurious divergence error or a branch created at the wrong head, never lost commits); closing it fully would require per-call temp refs, judged not worth the machinery.Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.