Skip to content
| Marketplace
Sign in
Visual Studio Code>SCM Providers>JB GitNew to Visual Studio Code? Get it now.
JB Git

JB Git

Bill Hu

|
14 installs
| (1) | Free
An IntelliJ IDEA-inspired Git workspace for Visual Studio Code.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

JB Git

Languages: English · 简体中文

JB Git is a VS Code extension project that brings an IntelliJ IDEA-inspired Git workspace to VS Code.

The implementation is intentionally layered:

  • Git Core executes the user's system git binary with safe argument arrays.
  • Repository state is parsed from machine-readable Git output and refreshed incrementally.
  • The extension host owns Git operations; the UI uses one IntelliJ-inspired Git Webview contributed to VS Code's bottom Panel plus native diff surfaces. That window is split by role: the host and its message handling, the stylesheet, the Webview script, and the protocol type that validates everything crossing between them.
  • Changelists, Shelf, hunk staging, history, conflict actions, Worktrees, Remotes, Stashes, and Submodules are implemented as independent layers.
  • CI validates core behavior on Windows, macOS, and Linux, plus Extension Host activation on VS Code 1.95 and stable.

Current status

The status terms below are intentional: Implemented means the workflow is present in the current tree, Partial means it is usable but does not yet match IDEA's depth, Planned means it is not implemented, and Out of scope is a deliberate non-goal.

IntelliJ IDEA workflow Status JB Git behavior and boundary
Git tool window and Log Partial One bottom Git panel provides Log, Console, Local Changes and Shelf. JB Git's own editors — the merge editor, the interactive rebase sequence editor and branch comparison — are IDEA's dialogs rather than files, so they open beside the code in a group of their own instead of interleaving with source tabs, and they share that one group rather than splitting off a new column each time; jbGit.toolEditorLocation: active puts them back among the file tabs. The graph, filters, branch comparison, progressive 300-commit loading and virtualized large lists are implemented; persistent history indexing and exhaustive very-large-repository search are not. Each local branch carries IDEA's incoming/outgoing markers — ↓n fetched-but-unmerged, ↑n unpushed, right-aligned on the row — and a branch whose upstream was deleted says gone instead of showing zeros. The search box is two-layered the way IDEA's is: typing filters loaded subjects and metadata instantly, Enter hands the text to Git as a whole-history message search (fixed-string, case-insensitive, through the same read path as the ordinary log so graph, selection and virtualization stay one implementation), and a hex entry jumps to that commit even outside the loaded window by re-rooting the log at it. Full commit messages are fetched only for the selected detail pane, keeping large histories responsive. Commits multi-select the way IDEA's log does — Ctrl/Cmd+click toggles, Shift+click and Shift+arrows grow a range — with the details pane summarising the selection; right-clicking it offers Cherry-Pick Selected, which applies the commits sequentially in history order (a conflict stops the batch and says how far it got, so the rest stays yours to redo), and Compare Versions on exactly two, a read-only diff from the older to the newer. The host never trusts the Webview's click order: the log's own order decides. The selection also edits history the way IDEA's log does: Drop Commit removes the selected commits and Squash Selected gathers them at the oldest one and combines their messages, both lowered onto the same unattended rebase plans as the sequence editor — same stash offer, same conflict stop, same refusals (merges in range, selection off the linear history) — with a squash of non-adjacent commits saying in the confirmation that the commits in between are reordered. Edit Commit Message… edits a message in place in the details pane (Ctrl/Cmd+Enter saves, Escape cancels, the draft survives a refresh): on the last commit it is an amend of the message alone, so whatever is staged stays staged for the next commit, and on an older commit it is a reword row lowered onto the same unattended rebase plan — same stash offer, same refusals — with a commit that has already been pushed named before the branch is rewritten. A rewrite that stops on a conflict says so and stops there instead of also claiming the history was rewritten — for Fixup… that means the fixup commit is reported as left standing only when the rewrite never ran, since a paused one still folds it in on Continue. Undo Commit… is IDEA's soft reset of the last commit: the branch moves back to the parent, the commit's changes stay staged, and it is refused for a merge commit or while a Git operation is in progress; the confirmation says when the commit has already reached a remote. Reset Current Branch to Here… offers IDEA's four modes, Soft, Mixed, Hard and Keep. The branch menu adds Checkout and Rebase onto Current, which checks the selected branch out and rebases it onto the branch that was current; local changes are parked in a stash for the whole of it and kept there rather than replayed if the rebase stops on a conflict. Fixup… is IDEA's: the staged changes become part of the chosen commit — an amend when it is the last commit, otherwise a fixup! commit folded in through the same unattended rebase plan, with the target checked against the linear history before anything is committed, and a plain sentence if the fold is declined and the fixup! commit is left on the branch.
HEAD / Index / Working Tree review Implemented Local Changes exposes the two distinct comparisons—HEAD → Index (staged) and Index → Working Tree (unstaged)—with file diff plus text-hunk stage/unstage. Hunk application verifies that the displayed hunk is still current before mutating the Index. A working-tree hunk also carries IDEA's Rollback: that one change goes back to what the Index holds while the file's other changes and anything staged stay as they are, confirmed like every other rollback and backed by a Shelf entry holding exactly the change being discarded — recorded rather than shelved, since shelving takes a file's changes out of the working tree and would throw away the hunks meant to stay. Long Changelists draw their first 500 rows and offer the rest on a click: the pane is rebuilt on every refresh, and a few thousand changed files cost tens of thousands of elements each time. Create Patch… saves the checked changes — the same selection Commit would take, staged content included — as a -p1 patch that git apply and IDEA's own Apply Patch accept.
Commit sources Implemented The commit form has two explicit sources: Staging area (Index) commits exactly the staged snapshot, while Selected files (complete contents) commits the checked files through an isolated temporary Index. A failed selected-file/Changelist commit leaves the user's real Index intact. Unversioned files are listed but never pre-checked, the way IDEA keeps them out of a commit until asked — auto-checking them once made Commit sweep in 2,475 editor-cache files. Checking Amend fills the box with the message of the commit being amended and unchecking restores what was typed, and a history button offers back the last 25 messages that made it into a commit, IDEA's Commit Message History. An Author… field commits as someone else (Name <email>, or a name Git matches against the history), completing from the recent authors and reset after the commit, and a configured commit.template pre-fills an empty box the way IDEA's does — its comment lines (core.commentChar, # unless configured otherwise) are stripped on commit exactly as Git's own editor strips them, and a message that is nothing but those comments is refused with a sentence rather than reaching Git and coming back as "empty commit message".
Changelists Partial Create, rename/describe, activate, delete and move whole files, including rename tracking and selected-Changelist commit. Different changes inside one file can belong to different Changelists the way IDEA's do: expanding a file lists its changes against HEAD with the Changelist each belongs to, Move... reassigns one, and when two edits land in a single hunk the individual lines can be checked and moved with Move Lines... — a line claim is the more specific decision and wins over the claim on its hunk. Committing a Changelist takes exactly its own — the list a file belongs to commits everything the others did not claim, a list that claimed hunks or lines commits only those, and the rest stay in the working tree. A change is remembered by what it changes rather than by line number, so it keeps its Changelist while the lines around it move, follows the file through a rename, and loses its assignment when the change itself is reverted. Committing complete contents says so before it sweeps another Changelist's work in. Task context and changelist-conflict policies remain gaps.
Rollback, Shelf and untracked deletion Implemented The Shelf tab is IDEA's: an entry expands to the files it holds, and its menu carries Unshelve (restore and drop the entry, as IDEA's does), Unshelve and Keep, Unshelve into Changelist…, Show Diff, Rename… and Delete…; JB Git: Open Shelf jumps straight to the tab. A shelf the branch has moved past no longer fails with Git's "patch does not apply": it is restored through a three-way merge, so it arrives as a conflict the merge editor can settle, and the entry is kept until it has been. Rolling back a tracked file first keeps a recovery entry in Shelf. An untracked file is moved through the operating system Trash instead of being permanently deleted. Conflicted paths are not individually rolled back because that could discard one side. An unversioned file's Ignore… is IDEA's: the exact file, its directory or every file with its extension, written to the repository's .gitignore or to this clone's private .git/info/exclude, anchored and escaped so the rule names exactly what was pointed at and added only once.
Branch checkout with local changes Implemented Smart Checkout stores tracked, staged and untracked changes in a temporary stash identified by immutable OID, checks out the branch, then restores the Working Tree and Index. A failed or conflicting restore leaves the stash available for recovery. The Log's Branches pane and the Branches popup share IDEA's groups and one starred set: Recent lists the branches checked out lately (read from HEAD's reflog, both ends of every checkout), Favorites the ones starred with the ★ button — kept per workspace and repository, and forgotten once the branch is deleted — and every local branch with a live upstream offers Update, which fast-forwards it from that upstream without checking it out (the current branch is pulled instead). In the pane the star sits on the branch row and the same actions are in its context menu; a star toggled on either surface shows up on the other, and the reflog behind Recent is re-read only when the refs actually moved.
Push safety Implemented Push fetches before preview, shows and executes the exact local-ref-to-remote-ref target plus outgoing commits, and supports initial upstream setup. Force push is unavailable whenever the destination branch matches a configurable protected-branch pattern; other destinations only offer Force with Lease. A non-fast-forward rejection on the checked-out branch's existing upstream offers pull-with-rebase or pull-with-merge and then previews again.
SHA-1 and SHA-256 object IDs Implemented Log selection and protocol validation accept complete 40- and 64-hex object IDs; Git still remains the authority that resolves and validates the object.
Merge conflict resolution Partial The three-pane editor works the way IDEA's does: the result shows clean file content with no <<<<<<< markers, each conflict is a coloured range that follows edits, and the strips between the panes draw connectors from every side chunk to its result region and carry per-change gutter actions: an arrow applies that side, × ignores the change, and a revert arrow takes a settled change back to unresolved, so applying one side never leaves the other side's gutter empty. Unresolved conflicts are red, applied sides green, hand-edited regions blue and ignored ones grey, and the change you are on is emphasised in whatever state it is in; a marker strip down the right of the result shows every change in the file and jumps to one on click; applying the second side of a conflict keeps both, and a "changes left to resolve" counter gates the staged Apply. Every decision and typing burst is one undo step (Ctrl/Cmd+Z, Shift for redo), a conflict that took nothing from the current branch is drawn as a deletion line rather than being invisible, and the wheel keeps scrolling over the strips. Toolbar per-block choices also answer to plain keys 1/2/3 while focus is outside the editable result. Whole-file choices, navigation (including F7), recoverable drafts, stale-result checks and an editable result remain. Rebase labels correctly identify Rebase Target and Replayed Commit instead of treating stage 2/3 as ordinary ours/theirs. JB Git: Resolve Simple Conflicts replays the merge in diff3 on copies of the three stages, so each conflict carries its base, and settles the blocks with only one possible outcome: both sides made the same edit, only one side changed the base, or the two differ only in whitespace. A partly resolved file is deliberately left unstaged, and a file containing conflict markers of its own is refused rather than framed by guesswork. A Base toggle answers IDEA's "what did this start from?" for the change you are on, floating a frame of the block's common-ancestor text above it when there is room and below it when there is not; because the working tree's conflicts and the diff3 replay are separate computations that need not frame a conflict identically, the base is offered only when the replay produced the same conflicts in the same order with the same two sides, and no base at all otherwise. A Compare… button is IDEA's Compare contents: any two of the four versions — the current branch, the base, the result as it stands right now, and the incoming side — open side by side in a native read-only diff, so the editor's own folding, search and whitespace settings apply to it; the base pairings are hidden for an add/add conflict, which has no common ancestor to compare against. Full semantic alignment remains. A conflicted file's context menu in Local Changes also carries IDEA's Accept Yours and Accept Theirs: one side is taken whole and the path marked resolved, a side that deleted the file is honoured by deleting it, and the sides are named by the running operation, so during a rebase "yours" is the rebase target rather than the replayed commit.
Merge/rebase/cherry-pick/revert/reset operations Partial Start plus continue/abort/skip flows are implemented; history-editing depth remains below IDEA. JB Git: Merge Ref carries IDEA's merge options — no fast-forward, fast-forward only, squash, no commit and allow unrelated histories — with a contradictory combination refused in a sentence before Git runs. JB Git: Rebase onto Ref is IDEA's Rebase dialog: Onto is the branch picked, an optional From commit turns it into --onto so only the commits after that point move, Preserve merges is --rebase-merges, Interactive opens the sequence editor on exactly the commits Git itself would replay onto that branch — a commit whose patch is already there is left out, the way git rebase leaves it out — and a dirty worktree is offered a stash that is restored afterwards or kept if the rebase stops, the same handling the Log's Rebase onto action now has. JB Git: Pull is IDEA's Update Project: with local changes present it offers Stash and Update (restored afterwards, kept if the update stops on a conflict) or Update Anyway, and stays cancellable.
Interactive rebase editor Implemented JB Git: Interactively Rebase from Commit opens a visual sequence editor for reordering — drag a row by its handle or press Alt+↑/↓ — plus pick, reword, edit, squash, fixup and drop, each explained in a tooltip; squash/fixup rows tuck under the commit they join. edit is IDEA's stop-to-amend: the rebase applies that commit and parks (Git exits 0 there, so the extension reads the sequencer state rather than trusting the exit code), tells you to amend or test and Continue, and keeps a parked stash parked instead of restoring it onto the commit being amended. Any row can be followed by an inserted exec row — a shell command run by Git's own shell at that point, a non-zero exit parking the sequence right there — or a break row, which pauses the plan for Continue to resume; both stops are owned by the same Continue/Abort handling an edit stop uses. Squashing into a run whose kept commit stops for editing is refused, because the squash's subject-guarded amend would fire on the user's own legitimate edit. Message changes are lowered onto a todo Git runs unattended, so no editor is spawned and a rebase paused by a conflict still applies the reworded message after Continue. A dirty worktree is offered a stash the way IDEA offers one: the changes are parked only once the rebase actually starts, restored to both working tree and Index when it finishes, and kept in the stash when it stops on a conflict rather than being replayed on top of one. Git's own rebase.autoStash stays off, because it restores unconditionally. Untracked files are not counted as blocking, since Git replays over them happily. Narrower than IDEA: the starting commit must be an ancestor of HEAD, and a range containing merges is refused instead of flattened.
File history and Blame Partial File History and command/output Blame are implemented, including literal special-path handling. Compare with Branch… and Compare with Revision… on a file are IDEA's: the version on another branch or tag, or one picked from the file's own rename-following history, opens against the working copy in the native diff. File History follows a file through renames the way IDEA's does (--follow, which Git only allows for one literal path, so the typed suffix filter in the Log stays a plain path filter), and its details pane shows IDEA's per-revision file list: only the walked file, called by the name that commit gave it — Git's own --follow walk answers which name is the file's at each commit, so an old name survives a rename and an unrelated file sharing a former name is never mistaken for it. Show History for Selection is IDEA's: select lines in the editor and the Log narrows to the commits that changed them, through renames, with a Lines chip next to the path filter that widens back to the whole file. The selection is mapped from the working file onto the committed one through the HEAD diff first — a line typed in place of removed lines inherits those lines, a selection made only of new text is reported as having no history yet, and an unsaved buffer is offered a save since the on-disk diff cannot see it.
Editor-gutter Blame Partial JB Git: Annotate with Git Blame puts an annotation on every line: author, date and optionally the abbreviated object ID, each in its own padded column, tinted by how recent the commit is within that file. Hovering a line gives the subject, author, both the commit's own date and how long ago it was, and links to show the commit in the Log, copy the revision number, annotate the previous revision — which follows a rename backwards because Git reports the path the line had in its previous commit — or Hide Revision, IDEA's way of looking through a reformatting commit: its lines are credited to the change before it (--ignore-rev), hidden revisions accumulate per document until Show Hidden Revisions or the annotation is turned off, and the hover says how many are hidden. An unsaved buffer is annotated through git blame --contents, so the annotation stays on the line the user is reading instead of sliding out of step the moment the document goes dirty, and a commit, amend or checkout re-reads it. IDEA's annotation Options are settings: Ignore Whitespaces (-w) so a reindent stops being the last change to a line, and movement/copy detection within a file (-M) or across the files one commit touched (-C). Putting the caret on an annotated line lights up every other line that commit touched — IDEA does this on hover, which a decoration cannot observe — and JB Git: Annotate Revision annotates the file as of any revision, with that revision's own lines drawn in bold. The hovered subject runs through Issue Navigation, so a configured issue id in it is a tracker link.
Issue navigation Implemented IDEA's Settings | Version Control | Issue Navigation, as the jbGit.issueNavigation setting: each rule pairs a regular expression with a link target, where $0 is the whole match and $1–$9 are capture groups — "[A-Z]+-\\d+" → https://youtrack.example.com/issue/$0, or "#(\\d+)" → an issues URL built from $1. Wherever a commit message is shown — the Log's subject and body, and the Blame hover — matching ids become links to the tracker. An earlier rule wins overlapping text so ordering is predictable, and a malformed pattern (or one that matches the empty string) costs that rule, not the feature. The same compiled module runs in the extension host and is injected into the Webview, so the two cannot drift apart.
Multi-root, nested repositories and Worktrees Partial Discovery, per-repository mutation serialization, linked-worktree metadata watching and debounced external working-file refresh are implemented. Refreshes are tracked per root/generation and rediscovery retains repository identity and its mutation lock. Cross-repository transactional rollback is still planned.
JetBrains-native UI/assets and pixel parity Out of scope JB Git is a clean-room VS Code extension. VS Code renders its native panel/editor chrome; JetBrains source, UI assets, controls and trademarks are not copied.

The broader command inventory also includes branches/tags, fetch/pull/remotes, stash, Worktrees, Submodules, Clone, Sparse Checkout, Patch import and commit export, LFS pull and Bisect. See docs/implementation-plan.md for the prioritized parity gaps rather than treating the inventory as a claim of complete IntelliJ IDEA parity.

Development

npm install
npm run compile
npm test
npm run package

Press F5 in VS Code to launch the Extension Development Host.

Looking at a surface

Nothing in the test suite renders a Webview: the tests read source text, drive real Git, and activate the extension host. That gap once hid a sequence editor whose script did not parse — the panel opened with the right title and nothing in it while every test stayed green. scripts/screenshot.mjs opens one surface in a real VS Code and photographs it, so that failure is one command away:

node scripts/screenshot.mjs --list
node scripts/screenshot.mjs rebase --out /tmp/rebase.png

It builds a throwaway repository per run. On Linux it needs a display and a screen grabber (xvfb, imagemagick, and the usual Electron libraries); on macOS it opens the window on the desktop and photographs it with the system's screencapture, so nothing needs installing beyond the Screen Recording permission. It is a manual tool rather than a CI step, because reading the picture is the point.

Versioning

Every installable update receives a new semantic version and produces a newly named VSIX instead of replacing an earlier package. Patch releases are used for fixes, minor releases for substantial new functionality, and major releases for incompatible changes. Automated tests keep the manifest, lockfile, changelog, and installation documentation aligned.

Releasing

The release pipeline is implemented and automated. Every push to main runs the Release workflow, which:

  1. Runs the unit tests on Linux, macOS and Windows and the extension-host tests against VS Code 1.95.0 and stable.
  2. Raises the patch version and updates the lockfile, changelog and installation docs together.
  3. Packages the VSIX and, when VSCE_PAT is configured, publishes it to the Visual Studio Marketplace.
  4. Pushes a chore: release <version> commit, tags it v<version>, and attaches the VSIX to a GitHub release.

Nothing is published unless the whole matrix passes. To land a change without releasing, put [skip release] in the commit subject:

git commit -m "docs: fix a typo [skip release]"

For a minor or major release, run the workflow manually from the Actions tab and pick the version part to raise; the same steps apply.

Marketplace publishing needs a repository secret named VSCE_PAT: an Azure DevOps personal access token scoped to All accessible organizations with the Marketplace (Manage) scope, created from the Microsoft account that owns the hmc publisher. Creating one requires an Azure DevOps organization, so visit https://aex.dev.azure.com/ — not the Azure portal, which rejects personal Microsoft accounts.

Without that secret the workflow still runs: it tests, versions, tags, and attaches the VSIX to a GitHub release, and warns that the Marketplace upload was skipped so it can be done by hand from the publisher page.

The release commit is pushed with the built-in GITHUB_TOKEN, whose pushes do not start another workflow run, so releases cannot loop.

Scope and attribution

The feature behavior is informed by the public IntelliJ Community Git/VCS implementation and documentation. This project is an independent TypeScript implementation; it does not copy JetBrains source code, UI assets, or trademarks.

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft