Calculate RFC 6902 JSON Patch operations between JSON-compatible Python values. Paths use RFC 6901 JSON Pointer syntax.
Ships as both a Python library and a drift command-line tool.
drift is distributed as a zipapp:
one self-contained executable with its dependencies (PyYAML, tomli-w) bundled
in, so nothing needs to be installed separately. Installing needs nothing but
Python 3.11+, git and pip.
curl -fsSL https://raw.githubusercontent.com/includeamin/drift/main/install.sh | bashOr from a clone:
bash install.shThis clones the latest tagged release, builds the executable and installs it to
~/.local/bin/drift.
bash install.sh --check # report whether a newer release exists
bash install.sh --update # install only if a newer release exists--check exits 0 when you are current and 10 when an update is available,
which makes it easy to use in a shell prompt or a cron job.
| Command | Effect |
|---|---|
bash install.sh |
Install or reinstall the latest release |
bash install.sh --check |
Report whether a newer release exists |
bash install.sh --update |
Install only if a newer release exists |
bash install.sh --ref v0.5.0 |
Install a specific tag, branch or commit |
bash install.sh --local |
Build from the working tree instead of GitHub |
bash install.sh --uninstall |
Remove drift and its cached checkout |
PREFIX (default ~/.local), BIN_DIR and REPO_URL are honoured as
environment variables. A build that fails verification never replaces a working
installation.
Every command reads - as stdin and accepts -o/--output, --indent and
--compact. diff, patch, paths and check also accept --format {json,yaml,toml,xml}, which defaults to detecting the format from the file
extension (falling back to JSON).
Emit the JSON Patch that turns OLD into NEW.
$ drift diff old.json new.json --compact
[{"op": "replace", "path": "/meta/v", "value": 2}, {"op": "add", "path": "/tags/2", "value": "c"}]Add --stats for a summary instead of the operations, and --exit-code to exit
1 when the documents differ:
$ drift diff old.json new.json --stats --compact
{"total": 3, "by_op": {"add": 1, "replace": 2}}Add --pretty for a colored, human-readable rendering instead of JSON Patch:
$ drift diff old.json new.json --pretty
+ /tags/2: "c"
~ /meta/v: 1 → 2Colors are used automatically on a TTY and disabled when piping; pass
--no-color, or set the NO_COLOR/FORCE_COLOR environment variables, to
override the detection.
Apply a JSON Patch array to a document. --in-place rewrites the file.
drift diff old.json new.json -o patch.json
drift patch old.json patch.jsonList the JSON Pointers in a document, one per line.
$ drift paths new.json
/name
/tags/0
/meta/v--values emits a pointer-to-value object instead; --containers,
--include-root, --sort-keys and --max-depth N control the traversal.
Verify that the generated patch round-trips, exiting non-zero if it does not.
$ drift check old.json new.json --compact
{"roundtrip": true, "operations": 3}| Code | Meaning |
|---|---|
0 |
Success |
1 |
Documents differ (--exit-code), round-trip failed, or a patch operation failed |
2 |
Invalid JSON or an I/O error |
from diff import diff, patch
old = {"name": "David"}
new = {"name": "Alex"}
operations = diff(new, old)
assert operations[0].op == "replace"
assert operations[0].path == "/name"
assert operations[0].value == "Alex"
assert patch(old, operations) == newThe returned Delta objects correspond to JSON Patch operation objects and use
the standard op, path, value, and from_path fields. patch also accepts
ordinary operation dictionaries using RFC names, including from.
Arrays use RFC semantics: add inserts, remove shifts later elements, and
replace updates an existing element. The empty path "" addresses the
document root.
- JSON
- YAML
- XML (experimental, lossy — see src/diff/formats/xml_format.py)
- TOML
poetry add git+https://github.com/includeamin/drift.git#tagThe release workflow is the single source of truth for the version. It derives
the next version from the latest git tag and writes the same value to the git
tag, pyproject.toml and diff.__version__, so drift --version always
matches the release you installed. CI fails if those values ever drift apart.