# lpm patch / patch-commit / patch-remove (/docs/packages/patch)



```bash
lpm patch <selector>                  # step 1 — extract for editing
lpm patch-commit <staging_dir>        # step 2 — finalize as a .patch file
lpm patch-remove <selector>           # remove a registered patch
```

Two-step workflow for fixing a bug in a dependency without forking it. The patch travels with your repo under `patches/<name>@<version>.patch` and re-applies automatically on every subsequent `lpm install`.

Same shape as [`patch-package`](https://www.npmjs.com/package/patch-package) — but bound to the original store integrity, so a silently-republished version of the dep can never substitute for the version the patch was authored against.

## The workflow [#the-workflow]

```bash
# 1. Extract a clean copy of the store entry to a staging dir
lpm patch lodash@4.17.21
# → /tmp/lpm-patch-XXXXXX/node_modules/lodash/  (path printed)

# 2. Edit files in the staging dir however you want
vim /tmp/lpm-patch-XXXXXX/node_modules/lodash/index.js

# 3. Produce the patch file and register it in package.json
lpm patch-commit /tmp/lpm-patch-XXXXXX
# → patches/lodash@4.17.21.patch
# → package.json :: lpm.patchedDependencies updated
# → lpm.lock :: [patches] checksum updated when a lockfile exists
```

After `patch-commit`, &#x2A;*commit `patches/`, `package.json`, and `lpm.lock`** to your repo. The next `lpm install` (yours, your teammates', CI's) verifies the lockfile-recorded patch checksum, then auto-applies the patch after linking.

## `lpm patch <selector>` [#lpm-patch-selector]

```bash
lpm patch lodash                # bare name — resolves against the project lockfile
lpm patch lodash@4.17.21        # exact pin — works without a lockfile
lpm patch lodash@^4.0.0         # semver range — resolves against the project lockfile
```

| Selector                       | Notes                                                                                               |
| ------------------------------ | --------------------------------------------------------------------------------------------------- |
| Bare name (`lodash`)           | Resolves to whatever exact version is in `lpm.lock`. Requires a lockfile (run `lpm install` first). |
| Exact pin (`lodash@4.17.21`)   | Works without a lockfile. The version must already be in the global store.                          |
| Semver range (`lodash@^4.0.0`) | Resolves to the lockfile's pinned version. Requires a lockfile.                                     |
| Dist-tags (`latest`, `next`)   | Not accepted. Pin to an exact version or range.                                                     |

Steps:

1. Resolves the selector to an exact `(name, version)` pair (using the lockfile for non-exact inputs).
2. Looks up `<name>@<version>` in the global store. Errors with a `lpm install <name>@<version>` hint if not present.
3. Copies the **pristine** store bytes into a unique staging directory under the OS temp root (`/tmp/lpm-patch-XXXXXX/node_modules/<name>/`). "Pristine" matters: re-running `lpm patch` on a project where this dep is already patched still seeds the staging dir from the upstream bytes, so the resulting diff is delta-from-upstream, not delta-from-previously-patched.
4. Writes a `.lpm-patch.json` breadcrumb in the staging root recording the resolved `name`, `version`, exact-pin `key`, and the store source path.
5. Prints the staging path. The dir is intentionally NOT auto-deleted — it must outlive the process so you can edit it.

With `--json`, `lpm patch` emits `success`, `name`, `version`, `key`, `staging_dir`, `package_dir`, and `next_steps[]`. The next step is an object with `description` and a runnable `lpm patch-commit <staging_dir>` command after you edit `package_dir`.

## `lpm patch-commit <staging_dir>` [#lpm-patch-commit-staging_dir]

```bash
lpm patch-commit /tmp/lpm-patch-XXXXXX
```

Steps:

1. Reads the breadcrumb to recover `(name, version, key)`.
2. Re-locates the store baseline from the live store (not the breadcrumb's recorded path), so a store relocation between `patch` and `patch-commit` doesn't break the diff.
3. Generates a unified diff between the staging directory and the pristine baseline. Aborts if the diff is **empty** ("no changes detected") or contains **binary file edits** (patches must be text-only — `originalIntegrity` already covers binary drift on install).
4. Writes `<project>/patches/<key>.patch` atomically (`.tmp` then rename). Scoped names have `/` translated to `__` for cross-platform portability — `@scope/pkg@1.0.0` lands at `patches/@scope__pkg@1.0.0.patch`. Mirrors pnpm.
5. Updates `package.json > lpm > patchedDependencies` with an entry recording the patch path and the original SRI integrity hash.
6. Updates `lpm.lock > [patches]` when a lockfile exists, recording the patch path, patch-file SHA-256, and original integrity.
7. Cleans up the staging directory (best-effort).

The on-disk `package.json > lpm > patchedDependencies` entry looks like:

```json
{
  "lpm": {
    "patchedDependencies": {
      "lodash@4.17.21": {
        "path": "patches/lodash@4.17.21.patch",
        "originalIntegrity": "sha512-..."
      }
    }
  }
}
```

## `lpm patch-remove <selector>` [#lpm-patch-remove-selector]

```bash
lpm patch-remove lodash@4.17.21
lpm patch-remove lodash --dry-run
lpm patch-remove lodash@4.17.21 --keep-file
```

Removes entries from `package.json > lpm > patchedDependencies` and removes matching `lpm.lock > [patches]` records when a lockfile exists. Exact pins (`lodash@4.17.21`) match one manifest entry. Bare names (`lodash`) work only when they uniquely match a single patched version; if multiple patched versions exist, use the exact key.

By default, LPM CLI also deletes the patch file when it is safely inside the project and no remaining patch entry references it. Use `--keep-file` to leave the file on disk. Use `--dry-run` to preview the manifest and file changes without writing.

After removing a patch, run `lpm install` to refresh `node_modules`.

## Drift safety [#drift-safety]

On every install, LPM CLI verifies both patch inputs before applying anything:

* The current patch file's SHA-256 must match `lpm.lock > [patches].sha256`.
* The store entry's integrity must match `originalIntegrity`.

**Drift is a hard error** — your install will refuse to apply the patch if the patch file changed outside `lpm patch-commit`, or if the underlying package's content changed (re-publish under the same version, tampering, etc.). Restore the recorded patch file, or re-author it through `lpm patch` + `lpm patch-commit`.

This is the key safety property versus stock `patch-package`: a republished `lodash@4.17.21` with different bytes can't silently slip past your patch.

## When to use [#when-to-use]

* A dependency has a known bug; the upstream PR isn't merged yet.
* A peer-dep mismatch needs a one-line fix to make installation work.
* A vendored package needs a tiny tweak that doesn't justify forking.

For larger or longer-lived divergence, prefer forking + publishing your own scoped variant.

## See also [#see-also]

* [`lpm install`](/docs/packages/install) — auto-applies patches after linking
* [`lpm sbom`](/docs/packages/sbom) — includes patch metadata in generated SBOMs
* [`package.json` "lpm.patchedDependencies"](/docs/reference/package-json-lpm#patcheddependencies) — the file format
* [Content-addressable store](/docs/packages/content-addressable-store) — what the patch is generated against
