Skip to content
| Marketplace
Sign in
Visual Studio Code>Other>Orgbench: Salesforce Compare & DeployNew to Visual Studio Code? Get it now.
Orgbench: Salesforce Compare & Deploy

Orgbench: Salesforce Compare & Deploy

orgbench

|
1 install
| (0) | Free
Compare Salesforce metadata between two orgs or local git repositories and deploy only the differences you select, with optional permission transfer — field-level security plus object, Apex class and Visualforce page access — and session export and import.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Orgbench — compare two Salesforce orgs and deploy only the difference, without leaving VS Code.

Pick a source and a target — a Salesforce org or a local git repository on either side — choose the metadata you care about, and Orgbench shows what's added and what changed, component by component, with a side-by-side diff on any row. Tick what you want moved. Everything runs on your machine through the Salesforce CLI you're already authenticated with: no cloud service, no vendor account, no managed package in your org, and your metadata never leaves your laptop.

Select Source and Target

A native core, not a web app. Orgbench is a single native binary (written in Rust) driving your local Salesforce CLI — no bundled Node runtime, nothing installed in your org. It runs outside the editor's process, so a large comparison — hundreds of components across dozens of metadata types — neither bloats VS Code's memory nor takes it down. It is the same core that powers the Orgbench plugin for IntelliJ IDEA, so the comparison and deployment rules are identical in both.

What you can do with it

Find and pick exactly what to deploy. Filter by label or API name across objects, fields, Apex, LWC/Aura, Visualforce, layouts, flows — up to 172 metadata types in 14 groups; selections across types accumulate into one session you can export to a file and import back later.

Review Differences with a side-by-side diff

Move the field and its permissions in one go. Field-level security, object CRUD, Apex class and Visualforce page access, tab visibility and record type visibility travel with the components you checked — additively (never revokes) or as a full sync — into exactly the Permission Sets and Profiles you select. Managed-package and standard components get rows too, as permission subjects — their permissions travel while their metadata never deploys. The part change sets and the CLI leave to you.

Select Permission Containers

Choose how much risk you want. Validate only dry-runs the whole package against the target. All or nothing sends it as one deployment that rolls back completely on any failure. Best-effort sends the same package but keeps every component Salesforce accepted and reports the ones it rejected — so a big package lands in one pass, and your next run only deals with what failed. Quick Deploy promotes an already-validated package without uploading or re-running tests.

Deployment Results

Fix it in the diff, ship the fix. The source side of the diff view is editable for any component whose row is a single document — Apex, Visualforce pages, objects, fields, flows, layouts and more — and the deployment sends your edited version. The source org is never modified, the edit is marked on the row and can be reverted, and it lives only inside that comparison session. Bundles and multi-component container files stay read-only.

Deploy one field on a page layout. Switch page layouts to "Select fields and related lists" and move a single field or related list into the higher org without touching anything else on that layout.

Use git as either end. Org → org, org → git, git → org, git → git. A git target ends in one local commit containing only what you selected — Orgbench never pushes and never switches your branches.

Let dependencies pick themselves. Checking a field pulls in its missing object; checking a list view pulls in the fields it references — visibly, as rows you can uncheck.

What it never does

  • Never deletes anything in the target — a component that exists only in the target produces no row and is never touched.
  • Never deploys managed-package or standard-object metadata — such rows exist to carry permissions, and a managed or standard field deploys at most its two history-tracking toggles, spliced into the target's own document, never the source definition.
  • Never creates an object you didn't tick: auto-selected dependencies are visible rows you can uncheck before deploying.
  • Never touches your tokens — authentication stays in the Salesforce CLI, and diagnostics are sanitized before you see them.
  • Partial deployment happens only in the mode built for it: Best-effort keeps what succeeds because you chose that trade-off — and it is not offered against production orgs — while All or nothing rolls back the whole package on any failure. A package carrying Apex or Flows always passes through the test-selection screen before it goes to any org.

Requirements

  • macOS arm64, macOS x64 or Windows x64 (the bundled core is fail-closed on other platforms; Linux is planned). The Windows x64 build is for Windows on x64; it has not been verified on Windows on ARM, and VS Code itself may reject installing an x64-targeted VSIX (targetPlatform: win32-x64) there.
  • VS Code 1.85 or newer.
  • Salesforce CLI (sf) installed and on PATH. On Windows the core also looks for sf.exe, sf.cmd, sf.bat and sf in %LOCALAPPDATA%\sf\client\bin.
  • Two authenticated aliases that resolve to different Salesforce org IDs. The terminal is not the only way to create them: the + button next to the Source or Target dropdown runs the CLI's own sf org login web for you (see the workflow below).

Install from disk

  1. Obtain the VSIX for your platform: orgbench-darwin-arm64.vsix (Apple silicon), orgbench-darwin-x64.vsix (Intel) or orgbench-win32-x64.vsix (Windows x64), or build it (below).
  2. In VS Code, open the Extensions view, choose Install from VSIX… from the … menu, and select the file.
  3. Open the Orgbench view from the Activity Bar.

Workflow

  1. Choose the Source and the Target. Two orgs must resolve to different org IDs; either side can be a local git repository instead — see below. The + button next to either dropdown creates a new alias without leaving the editor: enter the alias name, choose Production, Sandbox or a My Domain URL, and sign in in the browser the Salesforce CLI opens. The new alias is selected on the side you asked from. The extension never sees your credentials, never sets a default org and never overwrites an existing alias — a name already in use is refused before the browser opens. While the login runs, the selection screen stays where it is with its controls disabled and offers Cancel; cancelling leaves your selection untouched.
  2. Pick a Metadata Type and tick the components you want. Selections accumulate across types into one session manifest: switching the type does not discard what you ticked before. Export Session… writes the session's current selections to a scenario file, Import Session… reads one back — see Session export and import below. Reload Orgs re-reads the alias list.
  3. Choose Compare.
  4. Review the differences and tick the rows to deploy. Double-click a row to open both versions of that component side by side. An Apex class, an Apex trigger and a Visualforce page are two documents each — the body and its *-meta.xml sidecar — so such a row opens the sidecar in its own read-only window when the row has attribute differences (an API version, a status or a label change), and the body window whenever the sidecar window does not open or the body differs too. A row whose only difference is a sidecar attribute therefore opens the sidecar alone: there is no body window for it, because the two bodies are identical. The left pane of the diff is editable while orgbench.allowSourceEditing is on and the core marked that row editable — a metadata XML document, or the body of an Apex class, an Apex trigger or a Visualforce page; a sidecar, a bundle, a rule or label sharing one file, a single layout element and any row with nothing to deploy stay read-only. So the deployment can send your own increment instead of the whole current document of the source org: type in it, and about a second and a half after you stop the draft is sent to the core and the row picks up its Edited mark. The draft is saved as you type, so the tab never shows unsaved changes and never asks whether to keep them; closing the tab sends whatever is left, and pressing a deploy button sends everything still unsent before the deployment starts. Orgbench: Revert Edit in the command palette drops the edit and puts the original text back; it acts on the pane your caret was last in, so click into the left pane first. Retyping the original text usually drops the edit too, but only the command is guaranteed to: a body whose line endings are mixed, or one that starts with a byte-order mark, is not loaded byte for byte. The draft is held in memory and never written to disk, and it goes away with the comparison. Revert only works from the comparison screen itself; run it from the test selection or permission screen and it answers "Revert is available on the comparison screen." instead of reverting anything. Pick a permission transfer mode in Settings — search for Orgbench › Fls Mode (the setting is orgbench.flsMode; its label is unchanged from before wave A, but its three values now read Do not transfer permissions, Additive permissions and All permissions) — then start the deployment: against an org the row of final buttons is Validate (--dry-run, nothing is written), Deploy (all or nothing — one atomic package, any failure rolls everything back) and Best Effort (the same single package sent with --ignore-errors, so Salesforce keeps every component it accepted and only the ones it named are Failed); Best Effort is not offered at all against a target the core has recognized as production. Every mode sends the whole selection as one Metadata API deployment — there are no waves or batches, and no ordering between metadata types. Against a git repository there is a single Deploy.
  5. If the package contains Apex classes, Apex triggers or flows and the target is an org, an Apex test screen comes first; an empty selection means no --test-level is passed, and a production org still runs its own local tests and enforces 75% coverage.
  6. With a transfer mode other than Do not transfer permissions, the containers that will receive the permissions are listed for confirmation before the deployment runs. The list distinguishes Existing containers, matched by kind and API name in both orgs, from New Permission Sets that exist only in the source and are eligible for creation, and can be filtered by the Presence column. Each row carries two names: the Setup Label and the metadata full name in the Container column. They differ for many standard profiles — Standard User is Standard, System Administrator is Admin — and only the full name is used to match, retrieve, package and merge. The label shown is the target org's for a matched container and the source org's for a newly creatable Permission Set, so it always names the org where you would go check the result in Setup. The permission result table shows the same pair. Which permissions travel is decided by the rows you checked: a custom field row carries its field-level security, a custom object or platform event row carries the seven object permissions — create, read, edit, delete, View All Records, View All Fields and Modify All Records — an Apex class row and a Visualforce page row carry access to that component, a custom tab row carries that tab's visibility, and a record type row carries whether that record type is visible. A managed-package or standard component gets a row too, so its permissions can be selected even though its own metadata never deploys: the Details column reads Permissions only for a managed Apex class, Visualforce page, tab or record type, a managed or standard object, or a standard tab — the row has no document, so double-clicking it opens no window — and Tracking only for a managed or standard field, where the only metadata that travels is the Track History and Track Feed History toggles, merged into a fresh copy of the target's own field document. Two boundaries: the default record type of the target is never changed by a transfer, and tabs of both kinds are transferred — a custom tab travels as its own metadata component, while a standard tab such as standard-Account travels as a Permissions only row whose name is the permission entry itself. Both kinds of row are visible only when the UI & Navigation and Objects & Fields metadata categories are enabled. A selected source-only Permission Set may be created in the target, populated with only the selected rows' permissions and never a license, a user permission, an application visibility or a Profile.
  7. Read the result tables. Copy details copies only the sanitized diagnostics — never a token. Quick Deploy appears after a successful Validate run that actually ran Apex tests, and deploys the very package Salesforce already validated without re-running them; a validation that ran no tests says so instead, because Salesforce refuses to quick-deploy it. New comparison starts over.

Comparison or deployment can be cancelled from the progress view, and so can a browser login.

Session export and import

Export Session… writes what the current session has selected so far to a JSON scenario file (default name session.json). The button is on five views — object selection, differences, Apex tests, permission containers and results — and each writes what is known by then. The whole file is still serialized and written by the extension's core, but the core does not already hold what goes into it: the webview sends the live marks of the screen the button was pressed on, and the host only picks the path.

The file holds no snapshot of the comparison — no difference rows, no statuses, no attribute values — because such a snapshot goes stale the moment either end changes. It holds no secrets either: metadata names, org aliases, branch names and modes only, and a git end is written as the repository directory's last segment plus the branch, never as an absolute path.

An absent field and an empty one mean different things: no apexTests key means the test view was never reached, "apexTests": [] means it was reached and no test was checked. deployMode travels with deployModeIsFinal, which is true only on the results view. The file carries its own version, independent of the protocol version.

Import Session… is a button on the object selection view that reads a scenario file back in — a version-1 or version-2 file, either one written by Export Session or handed to you by someone else. It restores both endpoints and the session manifest, the checked comparison rows and the permission transfer mode, the checked Apex test classes, and the checked permission containers, each applied once the matching view is built. Every mark is applied exactly once per set, so a checkbox you clear afterward does not come back just because the view is rebuilt for another reason. Import does not navigate anywhere: you still walk the wizard yourself, and each view shows its marks already checked when you reach it. A name the file carries but the extension no longer accepts — renamed, deleted, or changed type since the file was written — does not fail the import; it is listed instead, on the view where it turned out to be unaccepted. A git endpoint is never restored, because the file, like every other output of the extension, carries only the repository directory's last path segment and the branch name, never the absolute path — you choose a git endpoint by hand after importing. The deploy mode recorded in the file is only shown as a text hint next to the deploy summary; it is never pressed on your behalf.

Git source and target

Each side is chosen independently, so org-to-org, org-to-git, git-to-org and git-to-git all work. A repository side is an absolute path plus a branch: Browse… picks the folder, and the extension reads the SFDX layout declared by its sfdx-project.json. A deployment into a repository ends in exactly one local commit and never pushes; the extension never checks out a branch in your repository — another branch is written through a temporary worktree that is removed afterwards. The + button next to the branch dropdown names a new branch, which is created by that commit alone.

Filtering

Every webview table (components, differences, Apex tests, permission containers, and both result tables) shares one per-column filter row directly under its header:

  • Text columns get a free-text box that matches a substring anywhere in the value, case-insensitively; a leading and/or trailing * is optional and ignored.
  • Enum columns — Change, Kind, Presence, Operation, Status — get a multi-select popup listing every value with checkboxes. Two carry a smart default: the differences Change popup hides No change rows, and the permission-results Status popup hides Skipped.
  • The selection column gets a tri-state toggle.

Column filters combine with AND. Filtering only hides rows: a row that is checked and then hidden by a filter keeps its selection, and the selection column's header checkbox selects or clears only the currently visible, filtered rows (hidden selections are left untouched). Filters reset to their defaults when you leave the screen (a new comparison, or stepping between the review and permission-container screens).

Selections (chosen components, deployable rows, and permission containers), together with each table's column filters and sort, are preserved when the view is hidden and shown again, so switching away from the panel never re-checks a container you deselected before deploying or drops the filters you set. Filters and sort persist while you stay on a screen and reset only when you leave it.

Sorting

Click any column header except the selection column to sort the table by that column. Each click cycles the sort: ascending (▲), then descending (▼), then off (original order). One column sorts at a time. Enum columns sort by their displayed label; boolean columns (Read/Edit) sort unchecked-before-checked. Sorting only reorders rows — it never changes which rows a filter has hidden — and, like the column filters, persists while you stay on a screen, resetting to the original order when you leave it.

Settings

  • orgbench.layoutMode — how Page Layouts are compared. whole is the default: one row per layout, compared over the whole document, and a deployment replaces the target layout with the source version, so an element that exists only in the target is gone afterwards. elements compares a layout element by element — one row is a field or a related list — and merges only the selected elements into the target layout. The mode applies to the next Compare; rows already on screen keep the form they were built with.
  • orgbench.allowSourceEditing — allow editing metadata in the diff view. On by default: the left pane is then editable for a row whose document belongs entirely to it — a metadata XML document, or the body of an Apex class, an Apex trigger or a Visualforce page — and the deployment sends the edited document. The *-meta.xml sidecar of those three, a Lightning or Aura bundle, a rule or label that shares a file with its siblings, a single layout element and the target pane stay read-only either way. The key keeps its historical name so a cleared box survives the upgrade. Clearing it does not erase edits that already exist — it gates the FIRST edit of a row, not access to one that already exists, so a row already carrying an edit reopens editable. The value is read when a window is opened, so a window already on screen keeps the form it was opened with.
  • orgbench.flsMode — permission transfer mode used by every deployment: NONE (do not transfer permissions), ADDITIVE or ALL. The key keeps its historical name so existing configurations keep working.
  • orgbench.metadataCategories — which categories of metadata types the Metadata Type dropdown offers. Core alone is the default.
  • orgbench.logLevel — session log detail: off, standard or detailed. No token ever reaches the file, and no response body either, except the sanitized diagnostic of a failed call — the same text the UI shows.

The live progress line (phase, step counter, elapsed time) is always shown while an operation runs; there is no setting for it.

Licensing

Orgbench is free during early access. A paid tier for advanced features is planned; nothing is gated today, and every feature described above is available to everyone.

The license path is fully offline: validation is a local signature check inside the bundled sf-core-server — there is no license server, no phone-home and no network call of any kind, not even a revocation check. Any licensing failure degrades to the free tier and keeps working, and safety is never gated: every boundary the repository's root README.md lists applies in every tier.

Enter or remove a key with the Orgbench: Manage License command — the key icon in the view title opens it — and the picker's own title shows what the core decided, including the reason a key was not accepted. The key and the first-run timestamp live in a single file the core owns, inside your user data directory (~/Library/Application Support/orgbench/ on macOS, %APPDATA%\orgbench\ on Windows), so a key entered in either editor applies in both once that editor's core has restarted and read the file.

There is no telemetry: nothing about you or your orgs is collected or transmitted. The extension is distributed under the End User License Agreement; see also the Privacy Policy and the third-party notices.

Development

The environment variable ORGBENCH_CORE_SERVER_PATH overrides the sf-core-server binary shipped inside the VSIX: set it to the absolute path of your own build in the environment VS Code is started from. It is a development tool and has no setting in the Settings UI; leave it unset to use the bundled binary. A value pointing at something that is not a regular file fails the launch and the refusal names the variable.

Build

The extension is part of the repository's Gradle build:

cd vscode && npm ci
cd vscode && npm test          # tsc typecheck + node:test suite
cd vscode && npm run build     # esbuild: extension-host + webview bundles
./gradlew buildVsixAarch64     # writes vscode/orgbench-darwin-arm64.vsix
./gradlew buildVsixX64         # writes vscode/orgbench-darwin-x64.vsix
./gradlew buildVsixWin32X64    # writes vscode/orgbench-win32-x64.vsix

The three tasks share vscode/bin/, so they run one after another, never in parallel. Building the Windows VSIX from macOS needs cargo install cargo-xwin and rustup target add --toolchain 1.97.0 x86_64-pc-windows-msvc.

Each VSIX bundles exactly one native core binary for its target.

Troubleshooting

The support site https://orgbench.io/ answers questions about the extension and files a ticket; the question-mark icon in the view title opens it, and the link opens only when you choose it.

The Salesforce core process did not start

The extension launches a bundled sf-core-server binary from its bin/ directory. If the view reports that the core process could not start or terminated, confirm the platform is supported (macOS arm64, macOS x64 or Windows x64) and that the VSIX was installed without modification. On Windows the bundled binary is unsigned, so SmartScreen or the antivirus may quarantine it on first launch. The core process log is written to the Orgbench Core output channel.

No aliases are listed

Use the + button next to the Source or Target dropdown to log in to an org and create an alias without leaving the editor, or run sf org list --json in a terminal, authenticate aliases there and use the Reload Orgs button. Entries without an alias are intentionally not shown.

While a browser login is running, the selection screen stays where it is with its controls disabled, shows the live progress line and offers Cancel; cancelling stops the CLI and leaves the browser tab open for you to close. A login that is not finished within ten minutes is reported as such. In a headless or container environment the Salesforce CLI refuses a browser login outright, and the extension reports that instead — authenticate in a terminal there.

Source and target are rejected as the same org

Choose aliases that resolve to different org IDs; two alias names can point to one org.


Salesforce is a trademark of Salesforce, Inc. Orgbench is an independent product and is not affiliated with, endorsed by, or sponsored by Salesforce.

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
© 2026 Microsoft