Added
- Search modes on the log filter — the commit search gains IntelliJ-style Match Case (
Cc) and Regex (.*) toggles. Search is now literal and case-insensitive by default; it used to be handed togit log --grepraw, which silently treated every query as a case-sensitive regex — searchingf.xmatchedfax - Custom date range — the Date filter's dropdown adds an After/Before date pair alongside the presets, sent to git as inclusive day bounds
- Whole-history author filter — the User dropdown now lists every author in the repository (via
git shortlog), most-commits first, with the configureduser.namepinned to the top as "(me)". It used to offer only authors present in the loaded page - Collapse / Expand Linear Branches — the log's View Options menu can now collapse every collapsible linear run in one action, and expand them all back, matching the per-fragment click collapse
- "In N branches" — the details pane for a single selected commit shows which local and remote branches contain it, loaded lazily with Show all / Hide
- Paths filter — a Paths toolbar filter narrows the log to commits touching the given files or folders (one pathspec per line), alongside the file-history chip
- Go to hash / branch / tag — a toolbar action (and ⌘G/Ctrl+G) opens a popup with branch/tag completion; free text resolves as a full or abbreviated commit hash and the log navigates to it, clearing any filters that would hide it
- Graph modes — View Options gains Sort Topologically, First Parent Only, and No Merge Commits, mapped to git's
--topo-order,--first-parent, and--no-merges - Long-edge stubs — edges spanning more than 30 rows collapse into a pair of arrow stubs, IntelliJ style; clicking a stub jumps to the edge's other end. "Show Long Edges" restores full drawing
- Highlighters — My Commits (emphasizes commits by the configured
user.name), Dim Merge Commits, and Fade Other Branches (the existing reachability dimming, now toggleable), all persisted - Presentation toggles — Compact References (first label + "+N"), Tag Names on/off, and Commit Timestamp (committer date instead of author date, now carried by the log format)
- Multi-commit cherry-pick — selecting several commits offers "Cherry-Pick N Commits", applied oldest-first in one
git cherry-pickinvocation so the existing conflict banner covers a mid-sequence stop - Worktrees — a Worktrees command lists linked working trees with their branch and state (main, locked, prunable), opens one in a new window, creates one on a new branch, removes one (offering force when git refuses), and prunes records whose directories are gone
- Git Operations popup — ⌘⌥G / Ctrl+Alt+G opens a quick list of the verbs reached for most: commit, push, update, search commits, blame, resolve conflicts, and worktrees. IntelliJ puts this on Alt+`
- Search Commits — find a commit by message or by hash from the palette, then copy its revision
- Diff ignore policies — the whitespace menu grows from two options to IntelliJ's four: do not ignore, ignore leading and trailing, ignore whitespaces, and ignore whitespaces and empty lines. The last two compare normalized copies of both sides, so re-indenting and re-wrapping stop reading as changes while the chunks still address the real lines
- Apply non-conflicting changes and Resolve automatically — two merge-toolbar actions. The first takes every region only one side actually changed, which loses nothing by definition. The second combines both sides wherever their edits touch different base lines, and deliberately leaves anything overlapping for you: a region where both sides rewrote the same line is never auto-merged
- Blame — "Annotate with Git Blame" in the editor context menu draws an author-and-date column beside every line, with a hover carrying the commit's subject, author, date, and short hash. Blame Options chooses how lines are coloured (by age, by author, or not at all), how names are written (initials, full name, or email), whether whitespace-only changes are ignored, and whether moved lines are followed within a file or across files. Uncommitted lines are marked as such rather than attributed to anyone
- Protected branches —
porcelain.push.protectedBranches(regular expressions, defaulting tomainandmaster) refuses a force push outright instead of confirming it. Force push already used--force-with-lease, which declines when the remote moved in a way porcelain has not seen; the protected list is the guard in front of that - Update Project — one action that fetches and then brings the current branch up to date by merge or rebase, per
porcelain.update.method, carrying uncommitted work across with an autostash and reporting the commits that arrived.porcelain.fetch.tagschooses whether tags come along - Push options and sync counts — set upstream while pushing a new branch, push tags, and skip pre-push hooks; incoming and outgoing counts per branch are read from already-fetched refs, so they cost no network round-trip
- Per-hunk staging — individual hunks can be staged and unstaged: porcelain parses the file's diff, rebuilds a patch from just the chosen hunks (renumbering the ones that follow a skipped hunk), and lets git apply it with context still verified, so a file that changed underneath refuses rather than applying in the wrong place. A change can also be addressed by its line range rather than by hunk, which is how the diff surface reaches exactly the block pointed at: git merges changes closer together than its context width into one hunk, so addressing by hunk would take neighbouring blocks with it
- Commit options — Sign-off beside Amend, plus a commit-options panel with Skip commit hooks and an Author override (rejected before reaching git if it looks like an option). An empty commit message is seeded from
commit.template, or from git's ownMERGE_MSGwhile a merge is in progress - Stash options and .gitignore — stash with keep-index, include-untracked, and a message; turn a stash into a branch; and add a path to
.gitignorewithout duplicating an entry that is already there - Interactive rebase editor — "Interactively Rebase from Here..." on a commit opens a Rebasing Commits table: pick / reword / stop-to-edit / squash / fixup / drop per commit, inline message editing for reword and squash, move up/down to reorder, Reset to restore the loaded plan, and View Git Commands to read the exact
git-rebase-todobefore starting. Porcelain drives realgit rebase -ithroughGIT_SEQUENCE_EDITOR, so hooks, rerere, and the merge machinery behave exactly as git intends; a conflict stops in the existing continue/abort/skip banner - Reword, Squash, Fixup, Undo Commit — edit any commit's message (an amend at HEAD, a reword rebase below it), fold a multi-selection into one commit under a combined message, commit the working tree as a
fixup!of a chosen commit, and undo the tip commit while keeping its changes staged. All are disabled while another Git operation is in progress, with the reason shown - Branch and tag verbs — smart checkout (stash → switch → restore) when local changes block a swap, force-delete that first names the commits it would discard, reset-to-upstream on the checked-out branch, and a Recent section in the branch tree read from the reflog. Tags gain Checkout, Push Tag, Delete Tag, and Delete Tag on Remote, with the remote-bound pair disabled when the repository has no remote
- Manage Remotes — a Name/URL table for adding, renaming, re-pointing, and removing remotes, reachable from the branch pane's settings menu. Remote names and URLs that look like git options are rejected before they reach the command line
- Clean Up Branches — a table of local branches with last-commit date, tracked branch, and merge status, filtered by a directory-style prefix; merged branches are pre-selected and unmerged ones are only ever deleted with an explicit, counted force
- Multi-branch filter — the Branch filter is now multi-select: the dropdown stays open while combining branches with checkboxes, and the log shows the union of everything reachable from the chosen refs, IntelliJ style. A plain click in the branch tree still replaces the filter with that one ref
- Porcelain diff viewer — an optional JetBrains-style diff surface rendered in a webview, behind
porcelain.diff.viewer. Paired centre gutter with each side's own line numbers, curved Bézier connectors between changed regions, per-side revision headers, a live difference count, a change stripe with click-to-jump, and a settings menu whose options apply to that window only and are forgotten when it closes - Per-window diff options — ignore whitespace, highlight granularity (line / word / character / off), collapse unchanged fragments and synchronised scrolling. VS Code's
diffEditor.*equivalents are global settings, so these have no native counterpart - Unified view — the settings menu can switch the diff from side-by-side to a single-column unified layout: old lines then new lines for each change, folds and find included. Modified lines keep their modified colouring on both halves rather than posing as removed/added — an edited line is not an addition. Sync-scroll controls hide in unified mode (one column has nothing to synchronise), and single-file layouts for added or deleted files ignore the toggle
Changed
- 3-way merge editor rebuilt on the diff stack — the merge editor is now three Porcelain diff panes on one shared scroll axis, in the IDEA order: yours on the left, the editable result in the middle, theirs on the right.
node-diff3runs once to seed the result buffer and the conflict regions; every accept, revert or keystroke after that is a splice into the result followed by a re-derive of the two live pair diffs. The verbs ride the gutter connectors — accept (≫/≪), ignore, revert to base, with accept-both reachable IntelliJ-style — the result pane is a real editor built from scratch — click anywhere and type, with selection, clipboard, IME composition and word movement, and typing inside a conflict resolves it — and a single undo/redo timeline covering typed edits and gutter verbs alike, conflict stepping, collapse of unchanged regions and per-pane find come with the shared stack. Conflict colouring follows each side's own state rather than the live diff, in every decision state: a still-takeable side stays red with its connector tapering to the exact row an accept would splice at, while a side that landed — or a conflict resolved by typing — goes quietly green. Apply stays disabled until every conflict is resolved. The legacy merge editor is replaced outright; unlike the diff viewer, this is not gated byporcelain.diff.viewer - Diff titles carry position and both revisions — a diff is now titled
panel-store.ts · 3 of 7 · 07748ba ↔ af89dd2. File-stepper position used to be reported through a transient status-bar message, which the compact floating diff window never shows Next File Diff/Previous File Diffrenamed toNext File/Previous File
Fixed
-
Independent scrolling when synchronised scrolling is off — the left pane was pinned to the top of the file, so only the right side ever moved. Each pane now scrolls on its own, seeded from wherever it already was so switching the toggle never makes the view jump
-
A stalled pane holds its anchor mid-window — scrolling through a large insertion froze the other side with the insertion point at the very top, pushing every line before it off screen. The stalled side is now lifted so its anchor sits in the middle of the pane, easing in and out at the edges of the gap, the way IntelliJ does it
-
The merge Result pane shows zero-height conflicts — when both sides add where the base had nothing, the region occupies no rows in the result, so neither the row colouring nor the pair-diff anchors had anything to draw and the middle pane showed nothing at all. The region now draws its own rule straight through the result, in its own colour
-
Dead palette commands removed — five
porcelain.commitPanel.*commands were declared but never registered, so they appeared in the Command Palette and failed when invoked -
Refresh icon has a home — the Git Log view's title menu was an empty contribution, leaving the declared refresh action unreachable
-
Branch and tag names no longer truncated in diff titles — the title shortened every ref with a blind
substring(0, 7), turningfeature/long-nameintofeature. Only actual object names are abbreviated now -
Unsupported languages are no longer highlighted as TypeScript — the merge editor fell back to TypeScript's grammar for anything outside the six Shiki loads, colouring Go, Python and YAML with confidently wrong rules. The diff viewer and the rebuilt merge editor fall back to plain text instead
Notes
- The Porcelain diff viewer is now the default;
porcelain.diff.viewer: nativerestores VS Code's own diff editor. Working-tree diffs route to the Porcelain viewer too — the working-tree side is a real editor built on the same core as the merge result pane (click anywhere and type, with selection, clipboard, IME composition and one coalescing undo/redo timeline), and Cmd+S/Ctrl+S saves to disk (a dirty dot marks unsaved edits, and a banner offers reload-or-keep if the file changes on disk underneath). Index and historical sides stay read-only, and Edit Source remains the way to the real file. Binary and image diffs and very large text diffs show an in-viewer placeholder with an Open in editor action (images up to 10 MB per side render side by side, heavier ones degrade to the placeholder with both sizes; oversized text offers Show anyway). Find, keyboard navigation and the screen-reader story are all in place