Repository navigation
feat(web-shell): unblock git update on dirty working tree #9769
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
Closed
Closed
Changes from 3 commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
4bbd6d7
feat(web-shell): unblock git update on dirty working tree
wenshao ee9b1f2
fix(serve): harden dirty-worktree git pull resolution (#9769)
qwen-code-dev-bot 71a3245
fix(serve): address round-2 review of dirty-worktree git pull (#9769)
qwen-code-dev-bot 5e03ec2
fix(serve): classify dirty-worktree pull failures from repository sta…
qwen-code-dev-bot c6546fd
fix(serve): address round-4 review of dirty-worktree git pull (#9769)
qwen-code-dev-bot a7aff45
fix(serve): address round-5 review of dirty-worktree git pull (#9769)
qwen-code-dev-bot 82cd1ea
fix(serve): address round-6 review of dirty-worktree git pull (#9769)
qwen-code-ci-bot 8eb70af
Merge branch 'main' into feat/git-pull-dirty-worktree
wenshao 280fa7d
fix(serve): address round-7/8 review of dirty-worktree git pull (#9769)
qwen-code-ci-bot 4591a65
fix(serve): address round-9 review of dirty-worktree git pull (#9769)
qwen-code-ci-bot 4998eb1
Merge remote-tracking branch 'origin/main' into feat/git-pull-dirty-w…
qwen-code-ci-bot 302803f
fix(serve): address round-10 review of dirty-worktree git pull (#9769)
qwen-code-ci-bot 5ab98db
fix(core): address round-11 review of dirty-worktree git pull (#9769)
qwen-code-ci-bot de03ee0
Merge branch 'main' into feat/git-pull-dirty-worktree
wenshao a6f32be
fix(core): address round-12 review of dirty-worktree git pull (#9769)
qwen-code-ci-bot 39737cb
Merge branch 'feat/git-pull-dirty-worktree' of https://github.com/wen…
qwen-code-dev-bot 3a47a27
Merge branch 'main' into feat/git-pull-dirty-worktree
wenshao b067bcd
fix(core): address round-14 review of dirty-worktree git pull (#9769)
qwen-code-ci-bot 29342ad
fix(core): address round-15 review of dirty-worktree git pull (#9769)
qwen-code-ci-bot 9b25ae4
Merge branch 'main' into feat/git-pull-dirty-worktree
qwen-code-dev-bot db0d9a2
Merge branch 'main' into feat/git-pull-dirty-worktree
qwen-code-dev-bot 0863e06
fix(core): close gitPull guard holes in collision probes and session …
qwen-code-ci-bot 7dc521e
test(cli): pre-warm the lazy core import in workspace-store tests (#9…
qwen-code-ci-bot 198e3c4
Merge branch 'main' into feat/git-pull-dirty-worktree
qwen-code-dev-bot 7688cc3
Merge branch 'main' into feat/git-pull-dirty-worktree
qwen-code-dev-bot 690817a
Merge branch 'main' into feat/git-pull-dirty-worktree
wenshao 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
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,84 @@ | ||
| # Git pull with a dirty working tree | ||
|
|
||
| ## Goal | ||
|
|
||
| Let the Web Shell "Update Project" action succeed when the workspace has | ||
| uncommitted changes, instead of dead-ending at an opaque | ||
| `dirty_working_tree` error that forces users back to a terminal. | ||
|
|
||
| ## Problem | ||
|
|
||
| The workspace git pull path (branch picker popover → SDK | ||
| `workspaceGitPull` → `POST /workspace(s)/git/pull` → core `gitPull`) ran a | ||
| plain `git pull`. With local modifications to files the incoming merge | ||
| touches, git refuses to merge; the route already classified that failure as | ||
| `409 dirty_working_tree` with a path-redacted git message, but the popover | ||
| only rendered the SDK's raw error string, so nothing actionable surfaced. | ||
|
|
||
| ## Behavior | ||
|
|
||
| `POST /workspace/git/pull` and `POST /workspaces/:workspace/git/pull` | ||
| accept two new boolean options (both default false, mutually exclusive — | ||
| sending both is a 400): | ||
|
|
||
| - `stash`: stash local changes (`git stash push --include-untracked`, so | ||
| untracked files that could block the merge are covered), run the pull, | ||
| then `git stash pop`. If the pull fails, any partial merge/rebase is | ||
| aborted and the stash is popped back, restoring the pre-pull state. If | ||
| the restore fails after a successful pull — a conflict, or an | ||
| untracked-file collision — the response is still a success with | ||
| `stashRestoreConflict: true` and its `output` carries git's failure | ||
| notice; git keeps the stash entry, so nothing is lost. | ||
| - `force`: discard all local changes first (`git reset --hard` + | ||
| `git clean -fd`; ignored files are kept), then pull. Destructive. | ||
| `git reset --hard` acts on the whole repository regardless of the cwd, | ||
| but `git clean -fd` run from a subdirectory only removes untracked | ||
| files inside that subtree, so `force` is refused when the workspace | ||
| cwd is below the repository root (it would destroy tracked changes | ||
| outside the workspace while leaving the untracked files that block the | ||
| merge in place). The pull is validated before anything is | ||
| discarded: the fetch runs first and a diverged branch (local commits | ||
| on both sides) is refused, because the post-discard merge could wedge | ||
| the repository mid-merge; a failed post-discard pull still aborts any | ||
| partial merge/rebase. | ||
|
|
||
| Stash detection compares `refs/stash` before/after the push instead of | ||
| parsing git output, which varies by version and locale. The pull itself | ||
| passes `--no-rebase --no-edit` when rebase is not requested, so divergent | ||
| branches merge instead of fataling on git builds without a `pull.rebase` | ||
| / `pull.ff` policy. | ||
|
|
||
| ## UI | ||
|
|
||
| When the plain pull fails with `409 dirty_working_tree`, the branch picker | ||
| footer switches from the status line to a resolution panel offering: | ||
|
|
||
| - **Stash Changes and Update** — pulls with `stash: true`. | ||
| - **Discard Changes and Update…** — destructive, so it takes a second | ||
| click: the panel swaps to a warning plus a confirm button that pulls with | ||
| `force: true`. | ||
| - **Cancel** — dismisses the panel. | ||
|
|
||
| The panel stays mounted (with a spinner on the clicked button) while its | ||
| own stash/discard pull is in flight, and resets whenever the popover is | ||
| reopened. When the blocking state is an unmerged working tree (conflicting | ||
| restore or abandoned merge), the stash option is replaced by an | ||
| explanation — `git stash push` refuses unmerged entries — leaving discard | ||
| as the recovery path, except in workspaces below the repository root, | ||
| where discarding is unsupported and the conflicts must be resolved from | ||
| a terminal. | ||
|
|
||
| ## Ownership | ||
|
|
||
| Both routes keep their existing scoping: the legacy route is | ||
| legacy-primary-workspace scoped; the qualified route resolves the trusted | ||
| runtime and contained `cwd` exactly as before. The new options only change | ||
| which git commands run inside the resolved workspace and add no new trust | ||
| surface. | ||
|
|
||
| ## Alternatives considered | ||
|
|
||
| Automatic stash on every dirty pull was rejected: silently moving the | ||
| user's changes through a stash is surprising, and the discard option is | ||
| destructive, so both need an explicit choice. `git pull --autostash` | ||
| performs that same stash-then-restore implicitly, so it was not adopted. | ||
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Deferred to the next round: this round implemented the review rounds 7-8 Critical batch (collision-probe restructure to enumeration, cherry-pick/revert foreign-state guard, fail-closed stash probe, merge/rebase of the captured tip, stash-restore note kept outside the message cap, and the serialization-test gate) under the per-round batch cap and the time budget this workflow flagged. This finding stays queued for the next round rather than being dropped. Docs/UI mismatch on the subdirectory unmerged-panel exception stays queued.
顺延至下一轮:本轮在单轮批次上限与工作流时间预算内实现了第 7/8 轮评审的 Critical 批次(将冲突探测重构为枚举比对、cherry-pick/revert 外部状态守卫、暂存探测失败即拒绝、合并/变基到已捕获的上游 tip、让暂存恢复提示在消息截断时保留、并发串行化测试的环境门槛)。该发现继续排队等待下一轮处理,不会被丢弃。