Git Workbench
One Activity Bar view for the feature-branch workflow where every branch is cut from master and
gets a merge request into master and another into staging:
| Section |
What it does |
| Branches |
Every local branch with its sync state and whether it is merged into master and staging (squash merges too). |
| Stashes |
Every stash with its files; apply, pop or delete. |
| Merge Preview |
Whether your branch will conflict with origin/master or origin/staging, which lines, which commits, and why. |
| Cherry-Pick |
Tick commits, see an in-memory preview of each, cherry-pick onto a -stg branch, continue/abort, and undo. |
| Conflicts |
During a merge, rebase or cherry-pick: the conflicting files and a three-pane merge editor to resolve them. |
It replaces three separate extensions (Git Branch Status, Merge Preview and Merge Resolver).
Uninstall those before installing this one. While one of them is still installed, the section it
overlaps stays off and a warning names it, with Show Extensions and Reload Window. If a section
fails to start for another reason, an error says which one and why; Show Log opens the
"Git Workbench" log with the details.
Branches and Stashes
Each branch shows ✓ up to date, ↓N need pull, ↑N not pushed, ↑N ↓M diverged, no upstream or
upstream gone, and master ✓ / staging ✗ for each target. A branch counts as merged when its tip
is in the target, when its commits are there as squash, rebase or cherry-pick copies, or when its
changes are there with identical file contents (the tooltip says when the branch itself was never
merged). The ☁ button fetches, ⟳ refreshes; … shows while the slower checks run.
Each branch row has two buttons:
- Check Merge Conflicts runs Merge Preview for that branch, without checking it out.
- Cherry-Pick Its Commits… opens Cherry-Pick with that branch as the source, onto
<branch>-stg.
Stashes list their message, branch and age; expand one for its files (click for a diff), and hover
for Apply, Pop and Delete. Deleting asks first.
Merge Preview
git merge-tree --write-tree performs each merge in memory: nothing is checked out and your
working tree is never touched. For each conflicting file it finds the clashing lines, blames each
side, and classifies the commits against the base branch:
| Label |
Meaning |
| only on origin/staging |
Staging changed these lines with commits that are not on master; your master merge request is not affected. |
| newer on origin/master |
Master changed them after you cut your branch: update from master and resolve it there. |
| master ↔ staging drift |
Not your code: your side is master code and staging has its own version of it. |
| mixed causes |
Both kinds of change. |
Staging-only commits name the merge request that brought them in (via !4156 (feat/x)). Click a
file for the merge result with conflict markers, a conflict to compare both sides, a commit to see
it. Copy Report as Markdown gives a summary for a merge request. It re-checks after commits,
checkouts and fetches; running the check yourself fetches the targets first.
Choosing what is compared. By default the checked-out branch is checked against
gitWorkbench.targets (origin/master, origin/staging). Each repository can choose its own:
- + on the view title adds a branch to check against: any remote branch (
origin/develop,
origin/release/2.4, a teammate's origin/feat/x), a local branch, or a tag or commit you type.
- ✕ on a branch's row stops checking against it (one branch always stays).
- Reset Branches to Defaults (right-click a branch, or the view's
… menu) goes back to
gitWorkbench.targets. A note in the view says when a repository uses its own branches.
- Click the branch row at the top (marked current branch) to check another local branch without
checking it out; pick Current branch to follow checkouts again.
These choices are remembered per repository (so ADMIN-API and ADMIN-API2 can differ) and only apply
to Merge Preview: Branches and Cherry-Pick keep using gitWorkbench.targets.
Cherry-Pick
On feature-a it offers feature-a's commits onto a new feature-a-stg from origin/staging; on
feature-a-stg, feature-a's commits onto the branch you are on. Click From or Onto to change
either. Commits already on the destination (also under another SHA) are marked. Every ticked commit is
previewed in memory: applies cleanly, conflicts (expand for the file with markers), already applied or after a conflict.
Cherry-Pick Selected confirms with that summary and runs git cherry-pick -x. On conflicts the view
shows Continue (refused while conflict markers remain; stages resolved files), Skip and Abort (which
also removes a branch it created). Show Conflicts opens them in the Conflicts section.
Undo Last Cherry-Pick offers delete or reset while the commits are unpushed, revert otherwise;
Revert Cherry-Picked Commits… reverts any cherry-pick -x commit on the current branch.
Conflicts and the merge editor
Clicking a conflicted file anywhere (Source Control's Merge Changes, the Explorer, Quick Open)
opens it in the merge editor, once per file and merge; close it to edit the text by hand
(mergeResolver.openConflictedFilesInMergeEditor turns this off). The Conflicts section (also in
Source Control while there are conflicts) lists each conflicting file
with what each side did (Modified ↔ Deleted). Accept Yours / Accept Theirs work on files, selections,
folders and repositories. Merge… opens the three-pane editor (Your version | Result | Their
version) with colour-coded blocks, word highlights, aligned scrolling, formatting-only conflict
detection, and who changed each side and when. Files with conflict markers
but no Git conflict get a CodeLens that opens the same editor. Details:
docs/merge-resolver/README.md in the source.
Conflicts work as in PhpStorm:
- Each side of a block has » / « (accept) and ✕ (ignore). Accepting one side of a
conflict leaves the other side open: its arrow turns into ↓, which appends that side after
the first, or you ✕ it. The conflict is resolved once both sides are handled.
- Ctrl/Cmd+click an arrow to resolve the conflict with that side alone, or ✕ to ignore both.
- Right-click a block (or a selection covering several) for Accept Left/Right, Resolve using
Left/Right, Ignore Left/Right, Resolve Automatically, Ignore and Revert (back to
the base text, unresolved).
- Conflicts made only of PHP
use statements are resolved when the merge opens, as PhpStorm's
"Automatically resolve conflicts in import statements" does: the base imports, minus those either
side removed, plus those either side added, each once (also with Resolve Automatically and Resolve
Simple Conflicts; turn it off in the gear menu or with mergeResolver.resolveImportConflicts).
- Typing in the Result never resolves a block: the arrows stay, and accepting afterwards replaces
what you typed. The magic wand only appears while a simple conflict is untouched. Lines that exist
in no version are tinted and counted.
Finishing a merge follows PhpStorm 2026.2's iterative flow:
- The merge window's bottom bar has Accept Left / Accept Right (the whole file, asking first
if you edited the Result), Apply Changes and Save and Close.
- Apply Changes (Ctrl/⌘+Enter) saves the file and marks it resolved, but does not stage it, then
opens the next file. With blocks still open it asks first ("Unprocessed Changes": Save Current
Result saves exactly what the Result shows).
- Save and Close (Escape) keeps your work and goes back to the Conflicts dialog; reopening the
file brings the work back. Unlike PhpStorm, blocks still open are written with conflict markers,
not the base text, so nothing is silently lost.
- The Conflicts dialog (opens when a merge, rebase or cherry-pick stops on conflicts, from the
Conflicts section title, or with Resolve Conflicts…) lists the files under Unresolved and
Resolved, with an
n/m badge of the changes merged so far and Yours/Theirs columns.
- Hover a row for Accept on either side (applies to every selected file).
- Right-click for Accept 'Yours' / Accept 'Theirs' / Revert Conflict Resolution, which puts
the file back to Git's conflict, markers included.
- Resolve All Simple Conflicts applies non-conflicting changes, simple conflicts and import
conflicts in every file not started yet, and reports what is left.
- Resolve Manually (or Review Changes for resolved files, or double-click a file) opens
the merge window for the selection, then the remaining files.
- Accept and Finish stages every file and commits the merge, or continues the rebase or
cherry-pick, in a terminal.
- Close stages the resolved files and, after asking, discards partly resolved ones.
- Set
mergeResolver.openConflictsDialog to false to not open the dialog by itself.
The toolbar follows PhpStorm 2026.2: Previous/Next Difference, Compare Contents (any two of
Base, Left, Middle and Right in VS Code's diff), Collapse Unchanged Fragments, Apply
non-conflicting changes: Left · All · Right, Resolve Simple Conflicts, Revert Conflict
Resolution, Jump to Source, then the gear (Synchronize Scrolling, Ignore Differences,
Highlighting Differences) and Help. Right-clicking outside a block offers Annotate, Show
Whitespaces, Show Line Numbers, Show Indent Guides and Use Soft Wraps. Drag a divider to resize the
panes. The status reads "2 changes, 1 conflict", then "No changes, No conflicts" with the
"All changes have been processed" balloon.
| Key (Windows/Linux · macOS) |
Action |
| F7 / Shift+F7 |
Next / previous difference |
| Ctrl+Alt+R · ⌘⌃→ |
Accept Left Side (also Alt+Shift+Right) |
| Ctrl+Alt+A · ⌘⌃← |
Accept Right Side (also Alt+Shift+Left) |
| F4 |
Jump to Source |
| Ctrl+Shift+D · ⌘⇧D |
Settings (gear) |
| Ctrl+Shift+Tab |
Select the opposite pane |
| Ctrl+F7 / Ctrl+Shift+F7 (⌘ on macOS) |
Next / previous conflict (our extra) |
| Alt+Shift+Delete |
Ignore the current change |
| Ctrl+Alt+Shift+A · ⌘⌥⇧A |
Apply all non-conflicting changes |
| Ctrl+Enter · ⌘Enter |
Apply Changes |
| Escape |
Save and Close |
Settings
Shared by every section:
| Setting |
Default |
Meaning |
gitWorkbench.targets |
["origin/master", "origin/staging"] |
Branches merge requests go into. |
gitWorkbench.baseBranch |
origin/master |
The branch you cut feature branches from. |
gitWorkbench.gitPath |
(built-in Git's) |
git executable, 2.38 or later. |
If you set mergePreview.targets, gitBranchStatus.targetBranches / gitBranchStatus.remote or
mergeResolver.gitPath for the old extensions, those values are used until you set the
gitWorkbench.* ones. Each section's own settings keep their names: gitBranchStatus.autoFetchMinutes
and detectSquashMerges; mergePreview.fetch and autoCheck; mergePreview.cherryPick.branchSuffix
(-stg), defaultFrom and recordOrigin; and the mergeResolver.* merge editor settings.
Development
npm install
npm run build # dist/: extension, merge editor webview, Monaco worker
npm run test:unit # vitest: engine, git layers and real fixture repositories
npm run test:integration # two VS Code runs: the Conflicts suites, then Branches/Preview/Cherry-Pick
npm run package # git-workbench-<version>.vsix
GW_SUITE=resolver or GW_SUITE=workbench runs one integration suite. Source layout:
| Folder |
Contents |
src/common |
Shared log, Git API connection, settings |
src/branches |
Branches and Stashes views |
src/preview |
Merge Preview and Cherry-Pick (git/ and report/ have no editor code) |
src/resolver |
Conflicts view and merge editor (engine/ is pure, webview/ is the UI) |
The history of the three original extensions is in docs/.