-
Notifications
You must be signed in to change notification settings - Fork 3.1k
fix(web-shell): disable Push while the branch is behind its upstream #10754
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
9b3ce46
fix(web-shell): disable Push while the branch is behind its upstream
wenshao bf4c301
fix(web-shell): reason about the push destination, not the upstream
wenshao e14460a
Merge branch 'main' into fix/webshell-push-hint-behind
qwen-code-dev-bot 9c30948
fix(web-shell): warn instead of disabling when the push counts look d…
wenshao c2a55dc
fix(web-shell): key the push row's silence on git naming no destination
wenshao 496bc77
Merge branch 'main' into fix/webshell-push-hint-behind
qwen-code-dev-bot 0020a64
Merge branch 'main' into fix/webshell-push-hint-behind
qwen-code-dev-bot 1725d09
fix(web-shell): keep the stale rows on screen through a post-action r…
qwen-code-dev-bot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
100 changes: 100 additions & 0 deletions
100
.qwen/e2e-tests/2026-09-02-webshell-push-nonff-disable.md
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| # Push hints follow the push destination | ||
|
|
||
| ## Scenario | ||
|
|
||
| Open a trusted git workspace in the Web Shell and open the branch picker from | ||
| the sidebar git chip with the repo in each state: | ||
|
|
||
| 1. Tracking upstream, ahead 1 / behind 0 (pushable). | ||
| 2. Behind 3, clean tree, ahead 0. | ||
| 3. Ahead 1 / behind 1 (diverged). | ||
| 4. Conflicted merge in progress on the branch, ahead 1 / behind 1. | ||
| 5. Behind 1 with a dirty tracked file whose incoming change conflicts; click | ||
| Update Project to raise the 409 resolution panel. | ||
| 6. Triangular (fork) workflow with a resolvable push destination: | ||
| `branch.<name>.remote = upstream`, `branch.<name>.pushRemote = origin`, | ||
| **and `push.default = current`** (required — under the default | ||
| `push.default=simple`, git refuses to resolve `@{push}` in this shape and | ||
| the listing names no push destination); behind `upstream/main` by 3, | ||
| ahead of `origin/main` by 2. | ||
| 7. Same config but `push.default` left at its default `simple`: git names no | ||
| push destination, though a bare `git push` still succeeds via the | ||
| configured `pushRemote`. | ||
| 8. `branch.<name>.pushRemote = origin` with `push.default = current`, never | ||
| pushed to origin — the push ref does not exist yet. | ||
| 9. A tracking upstream whose name the branch does not match — from `master`, | ||
| `git push origin master:bar`, `git branch --set-upstream-to=origin/bar`, | ||
| then one local commit — with `push.default` at its default, so the branch | ||
| is ahead 1 of an upstream git will not turn into a push destination. Git | ||
| names no destination *and* refuses the bare push (`exit 128`). | ||
| `push.default = nothing` on an otherwise plain tracking branch is the | ||
| sibling shape. | ||
| 10. A branch with no upstream at all, in a repo that configures a repo-wide | ||
| push override (`remote.pushDefault`, or a `remote.<name>.push` refspec). | ||
| 11. Detached HEAD (`git checkout --detach`). | ||
|
|
||
| ## Checks | ||
|
|
||
| Only state 11 disables Push — a detached HEAD is the one push failure provable | ||
| from local state alone. Everything count-based warns on an enabled row and | ||
| lets git answer authoritatively on click: | ||
|
|
||
| - State 1: Push shows `↑1`, enabled. | ||
| - State 2: Push shows the warning `↓3`, enabled; clicking surfaces git's own | ||
| non-fast-forward rejection in the status line. | ||
| - State 3: Push shows the warning `↑1 ↓1 · diverged`, enabled. | ||
| - State 4: Push shows the warning "Merging", enabled (a push does not consult | ||
| the index). | ||
| - State 5: the panel is up; Push still renders its own hint and stays | ||
| enabled. Clicking Push clears the panel and shows the push outcome. | ||
| - State 6: Update Project shows `↓3 · upstream/main`; Push shows `↑2` | ||
| (push-side counts), enabled. | ||
| - State 7: Push shows **no hint** — git named no destination, so the row | ||
| makes no count claim; enabled. | ||
| - State 8: Push shows "Creates origin/<branch>", enabled. | ||
| - State 9: Push shows **no hint**, enabled. The upstream `↑1` must not appear | ||
| as a push count here: git refuses this push outright, and clicking surfaces | ||
| git's own refusal in the status line. | ||
| - State 10: Push shows "Sets upstream on push", enabled — the daemon pushes a | ||
| branch with no upstream through an explicit `--set-upstream` refspec, which | ||
| ignores `push.default` and the repo-wide override. | ||
| - State 11: Update Project and Push disabled with "Detached HEAD". | ||
| - After a **failed** Update Project against a force-reset upstream (reset the | ||
| remote branch to an ancestor in a second clone, no fetch in between): the | ||
| pull fails, and the re-fetched listing updates the rows in place — the pull | ||
| row leaves its stale `↓n` without reopening the popover. (A *deleted* | ||
| upstream ref defeats the fetch itself; only a prune refreshes that shape, | ||
| as the rule-site comment states.) | ||
| - After a **rejected** Push, both the listing and the working-tree status | ||
| re-read. That is a re-read, not a fetch: a rejected push moves no local ref, | ||
| so git's own message in the status line — not the refreshed counts — is what | ||
| explains the rejection. | ||
|
|
||
| ## Evidence | ||
|
|
||
| Round 2 pivoted from disabling on `behind > 0` to warn-only after review | ||
| measured the disable misfiring across independent config axes | ||
| (`remote.<name>.push` refspecs / Gerrit, forcing refspecs, triangular | ||
| `push.default=simple`, `checkout -b` name-mismatch shapes, stale last-fetch | ||
| counts): remote acceptance is not locally decidable. Round 3 then re-keyed the | ||
| row's silence from "a push override is configured" to "git named no | ||
| destination for a live upstream" — the boundary the rule site states. The old | ||
| key was wrong in both directions: state 9 showed pull-side counts for a push | ||
| git refuses, and state 10 dropped the accurate "Sets upstream on push". The | ||
| `pushConfigured` atom it read had no other consumer, so it and the | ||
| `git config --get-regexp` probe that produced it are gone. Unit coverage pins | ||
| the warn-only rule, the push-side count display, the silence boundary on both | ||
| real-git shapes, the `pushGone` copy, the status-only fallback, and the | ||
| post-failure refresh (both actions, its best-effort failure path, and the | ||
| resolution panel staying usable while the refresh is in flight); core pins the | ||
| push atoms — including that git names no destination in the three silence | ||
| shapes — and a nonzero real-git `pushBehind`, under a hermetic env. | ||
|
|
||
| ```sh | ||
| cd packages/web-shell && npx vitest run \ | ||
| client/components/BranchPickerPopover.test.tsx \ | ||
| client/components/sidebar/WorkspaceSection.test.tsx \ | ||
| client/components/panels/EnvironmentPanel.test.tsx \ | ||
| client/components/ChatEditor.test.tsx | ||
| cd packages/core && npx vitest run src/utils/git-branches.test.ts | ||
| ``` |
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
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
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
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.