# sfdx-git-delta: incremental Salesforce deployments from a git diff

> SFDX-Git-Delta is a Salesforce CLI plugin that diffs two git commits and writes the changed metadata into a package.xml plus a destructiveChanges.xml, so a deployment only touches what actually changed. It suits Salesforce teams deploying unpackaged source from a git repository, and it is not a substitute for getting the pipeline right first.

**scolladon/sfdx-git-delta** — Generate the sfdx content in source format from two git commits

- Repository: https://github.com/scolladon/sfdx-git-delta
- Stars: 572 · Forks: 147
- Language: TypeScript
- License: NOASSERTION
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/scolladon-sfdx-git-delta

## Two manifests instead of a full metadata deployment

SFDX-Git-Delta, shortened to SGD, is a Salesforce CLI plugin for generating incremental Salesforce deployment manifests and artifacts. Its stated purpose is to help Salesforce architects and developers do two things with their source deployments.

The first is making deployments faster by identifying the changed metadata since a reference commit. The second is automating destructive deployments by building the destructiveChanges.xml from the deleted or renamed metadata.

Both jobs come out of the same diff. Given two commits, the plugin reads the git repository's object store directly, works out which metadata components were added, modified, deleted or renamed, and writes the results into the file formats the Salesforce CLI expects. The second one is the more interesting of the pair, because a naive source deployment does not delete anything: a component removed from the repository simply stays in the org. Generating destructiveChanges.xml from the diff closes that loop, which is why the plugin is as much about correctness as about speed.

The three commands in the readme's TL;DR section are the whole workflow. Install the plugin:

```sh
sf plugins install sfdx-git-delta
```

Generate the delta between two commits:

```sh
sf sgd source delta --to "HEAD" --from "HEAD~1" --output-dir "."
```

and deploy it:

```sh
sf project deploy start -x package/package.xml --post-destructive-changes destructiveChanges/destructiveChanges.xml
```

The path arguments line up with the layout the plugin writes. The package manifest goes into a package/ directory and the destructive changes into destructiveChanges/, which is why the deploy command passes -x package/package.xml and --post-destructive-changes destructiveChanges/destructiveChanges.xml rather than pointing at a single file.

The plugin is written in TypeScript as an oclif-based Salesforce CLI command. The manifest declares the bin as sf with the commands directory at ./lib/commands, a flexible taxonomy and a topic separator of a space, which is what produces the sf sgd source delta shape of the command name.

## The four conditions the plugin states for itself

The readme has a section that decides whether SGD applies to you, and it is unusually direct about it. If you are not a Salesforce architect or developer, probably not. If you are, it is useful when four conditions hold.

First, the Salesforce project uses a git repository as the source of truth. Second, the metadata is in the Source, or DX, format in the repository. Third, the metadata is unmanaged, which the readme glosses as not building a managed or unlocked package. Fourth, and this is the one that catches people, the repository must be deployable at both ends of the comparison, since a state that cannot be deployed gives an unspecified delta. The readme's example of such a state is two files declaring the same component in one package directory.

That fourth condition is the important one, and it is a correctness constraint rather than a usability one. The plugin computes a difference between two trees. If either tree is not deployable in its own right, the difference between them is not a well-defined set of changes, and the resulting manifest will deploy something other than what you intended.

The third condition rules out packaged projects. The plugin works on unpackaged source, which matches the Salesforce Developers Blog post the readme links on optimising unpackaged deployments using a delta generation tool. If you are building a managed package, the deployment model is different and this tool does not apply.

Two pieces of advice in the same section are worth repeating because they are the difference between a working pipeline and a broken one. Make sure the pipeline works before implementing incremental deployments, otherwise the change just makes it harder to debug. And implement a way to switch back to full deployment in case the incremental one does not behave as expected.

The readme also carries a disclaimer, in capitals, that SFDX-Git-Delta is not an officially supported tool, with an instruction to use it at your own risk and test it before adding it to a pipeline. That is a reasonable position for a Salesforce community tool and it is worth taking at face value.

## Reading git without a git binary

The prerequisites section contains a detail that has become a selling point: no git binary is required.

The plugin reads the repository's git object store in-process. That means it parses the repository directly rather than shelling out to git and parsing its output, and the readme states that this works including for --ref-format=reftable repositories and --object-format=sha256 repositories. The readme adds that the latter is not hosted by GitHub or GitLab today, so you are unlikely to run into one, which is a fair caveat for anyone considering it a blocker.

The reason this matters is portability. A tool that shells out to git inherits git's version, its configuration, its credential helpers and its path handling, and it behaves differently on a developer laptop and on a CI runner. A tool that reads the object store itself has fewer moving parts, and on a runner with no git installed, or with a git version that does not support the repository's extension, it still works.

The runtime requirement is Node v22 or above. The readme explains how to check it: run sf --version and look for a node version of v22 or above, because that is the Node runtime the Salesforce CLI is running under, and if the CLI is running on an older Node the plugin will not work. The troubleshooting path it offers is to install the Salesforce CLI through npm with npm install @salesforce/cli --global, which is a useful hint since the npm-installed CLI and the native installer can end up running different Node versions.

The package manifest is stricter still, declaring engines of node >=22.22.1 and npm >=12. The dependencies explain the design: @scolladon/tsgit is the git object store reader, txml handles the XML serialisation for the manifests, ignore and zod handle file matching and input validation, and the Salesforce packages provide the metadata type definitions.

## Advanced use cases that shape the design

The table of contents lists a long set of advanced use cases, and they are the best available description of what the plugin does beyond the basic diff.

There is a case for testing changes without committing, which means the diff is against the working tree rather than a commit. There is a case for generating a folder containing only the added or modified sources, which is the artefact you would hand to a build step or a packaging process. There is a case for excluding some metadata only from destructiveChanges.xml, so that a component can be removed from the deployment without being deleted from the org, which is a common need when a component is being replaced by something else.

There is a case for explicitly including specific files for inclusion or destruction regardless of the diff, which is how you force a component into the manifest even when git says nothing changed. There is a case for scoping the delta to a specific folder, and another for scoping it to the package directories declared in sfdx-project.json, which matters for a monorepo where several packages sit side by side.

There is a case for running specified tests using the SGD manifest, which turns the delta into a test selection rather than only a deployment selection. And a case for conditioning the deployment on the content of package.xml and destructiveChange.xml, which is the guard rail for a pipeline that should deploy nothing when the diff is empty.

There is also a case for using the module in your own node application, which is a different integration mode from the CLI. The package manifest declares exports at ./lib/main.js and the package type as module, so the module can be imported directly, and the repository has a DESIGN.md at the root for the architectural rationale.

Finally there are cases for handling flow deletion, for decomposed metadata types and for debugging. The last two point at the real complexity of the problem: Salesforce metadata is not a flat set of files, because a single logical component can be split across several files, and a diff that does not understand that produces manifests that reference components that are not really there.

## How the project is tested and shipped

The repository listing shows a testing setup that is more thorough than most plugins of this size, and the vitest configuration files are the clearest evidence.

There are five vitest configuration files: vitest.config.ts, vitest.config.functional.config.ts, vitest.config.integration.config.ts, vitest.config.nut.config.ts and vitest.config.perf.ts, plus a shared vitest.shared.ts. The split suggests unit tests, functional tests, integration tests, a nut test suite and a performance suite, with the performance one backed by a performance dashboard badge in the readme. The unit tests live in __tests__/.

Mutation testing is configured through stryker.conf.mjs with @stryker-mutator/core and a vitest runner, which is a strong signal about how the maintainer treats the code. Mutation testing checks that the test suite actually fails when the implementation is broken, rather than merely passing.

Static analysis is broad: biome.json for formatting and linting, .ls-lint.yml for file naming, .markdownlint.json for the documentation, knip.config.ts for unused code detection, commitlint.config.js with conventional commits, and release-please-config.json for the release process, with CHANGELOG.md generated from it. There are also security-oriented files, SECURITY.md, osv-scanner.toml, .trivyignore and .codeclimate.yml, and .lychee.toml for link checking, since the readme links to a lot of external documentation that can rot.

The tooling/ directory holds project tooling and there is a docs/ directory with the migration guides the readme points to, including docs/migrating-to-v6.0.0.md and docs/migrating-config-merge-base.md.

That last guide deserves attention because it describes a breaking change for programmatic consumers. Config.mergeBase is now a required field, and the guidance is to add mergeBase: false to preserve the previous behaviour. Any team that consumes the module programmatically rather than through the CLI has to make that change, and the readme puts the warning at the top of the page rather than in a changelog.

## Where sfdx-git-delta is the wrong tool

The first limitation is the managed package case, and it is structural rather than a missing feature. The plugin computes differences over files in the repository, and in a managed package the deployment unit is the package rather than the individual metadata component. The readme's own condition list excludes managed and unlocked packages, so anyone whose project is packaged is out of the audience by the maintainer's own account.

The second is that a diff of two trees is only meaningful if both trees are deployable. Two files declaring the same component in one package directory makes the delta unspecified, and the plugin will not warn you about that particular inconsistency because detecting it requires the same understanding of the package layout that the deployment itself applies. You get a manifest, and the failure surfaces later as a deployment error.

The third is that the destructive changes automation is a double-edged feature. Generating destructiveChanges.xml from deleted and renamed metadata is what makes the deployment match the repository, and it is also what makes a mistake destructive. A commit that accidentally removes a metadata file, deployed through a pipeline with this plugin, deletes that component from the org. The readme's advice to test first and to keep a switch back to full deployment is the mitigation, and it is worth taking seriously.

The fourth is the support status. The readme states in capitals that the plugin is not an officially supported tool. There is no Salesforce commitment behind it, it depends on the internal behaviour of the Salesforce CLI and its source-deploy-retrieve library, and a change in how the CLI resolves metadata could change what the plugin emits.

The fifth is the dependency pinning. The Salesforce dependencies are exact versions rather than ranges: @salesforce/core 9.4.0, @salesforce/sf-plugins-core 13.0.7 and @salesforce/source-deploy-retrieve 13.6.0. That keeps a working combination reproducible, but it also means the plugin needs a release before it can move to a newer CLI, and the release list shows it is moving often, with v7.5.0, v7.4.6 and v7.4.5 in the first two weeks of October 2026.

## Conclusion

sfdx-git-delta fits a Salesforce team whose source of truth is a git repository in the DX source format with unmanaged metadata, since it turns a commit range into the two manifests a source deployment needs and reports the total at the end of the run. It is the wrong choice if your metadata is in a managed package or if your repository cannot be deployed at both ends of the diff, because the plugin's own criteria say that produces an unspecified delta. Before adopting it, get a full deployment working first, keep a switch back to full deployment, and note that the plugin carries its own disclaimer that it is not an officially supported tool, with the last push on 2026-10-20 and v7.5.0 released on 2026-10-11.

## FAQ

### What does sfdx-git-delta do?

It is a Salesforce CLI plugin that generates the source format content and destructive changes for a deployment from two git commits, identifying the metadata added, modified, deleted or renamed between them and writing them into package.xml and destructiveChanges.xml.

### How do I install and use sfdx-git-delta?

Run sf plugins install sfdx-git-delta, then generate the delta with sf sgd source delta --to "HEAD" --from "HEAD~1" --output-dir ".", and deploy it with sf project deploy start -x package/package.xml --post-destructive-changes destructiveChanges/destructiveChanges.xml.

### Does sfdx-git-delta need git installed?

No. The readme states that no git binary is required, because the plugin reads the repository's git object store in-process, including reftable and sha256 repositories. Node v22 or above is required, and you can check the version the CLI runs under with sf --version.

### What are the conditions for using sfdx-git-delta?

The project must use a git repository as the source of truth, the metadata must be in the Source or DX format, the metadata must be unmanaged rather than in a managed or unlocked package, and the repository must be deployable at both the --from and --to commits, since a state that cannot be deployed gives an unspecified delta.

### What is the mergeBase breaking change in sfdx-git-delta?

The readme warns that Config.mergeBase is now a required field for programmatic consumers, and that adding mergeBase: false to the config preserves the previous behaviour. The details are in the merge-base migration guide in the repository's docs directory.

### What licence is sfdx-git-delta released under?

The package manifest declares MIT and there is a LICENSE.md file in the repository, while the licence field on the repository records a custom licence that GitHub cannot classify. The last push was on 2026-10-20 and the newest release is v7.5.0 from 2026-10-11.

## Sources

- [Issues](https://github.com/scolladon/sfdx-git-delta/issues)
- [README](https://github.com/scolladon/sfdx-git-delta/blob/main/README.md)
- [Releases](https://github.com/scolladon/sfdx-git-delta/releases)
- [scolladon/sfdx-git-delta on GitHub](https://github.com/scolladon/sfdx-git-delta)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/scolladon-sfdx-git-delta
