dffs
Pipe and diff files: execute shell pipelines, diff/compare/join results.
Apply a transform to both sides of a comparison, then diff the results — like Haskell's on:
a ─→ [f] ─→ f(a) ─┐
├─→ [cmp] ─→ result
b ─→ [f] ─→ f(b) ─┘
Three CLIs apply this pattern with different comparators:
| CLI | cmp |
Description |
|---|---|---|
git-diff-x |
diff |
Diff a Git-tracked file at two commits, through a pipeline |
diff-x |
diff |
Diff two files through a pipeline |
comm-x |
comm |
Set operations on two files through a pipeline |
Install
pip install dffs
CLIs
git-diff-x
Diff a Git-tracked file at two commits (or one commit vs. the worktree), after piping both versions through a command pipeline.
Examples
JSON: readable diffs of compact files
A config file is stored as compact JSON (one line, no whitespace). In commit 38856a0, a few fields were updated, but git diff is unreadable:
-{"name":"myapp","version":"1.2.3","settings":{"debug":false,"logLevel":"info","maxRetries":3,...}}
+{"name":"myapp","version":"1.3.0","settings":{"debug":false,"logLevel":"warn","maxRetries":3,...}}
git diff-x with jq pretty-prints both sides before diffing, so you see exactly what changed:
git diff-x -R 38856a0 'jq .' example/config.json
# 3c3
# < "version": "1.2.3",
# ---
# > "version": "1.3.0",
# 6c6
# < "logLevel": "info",
# ---
# > "logLevel": "warn",
# 11c11
# < "analytics": false
# ---
# > "analytics": true
This also normalizes out any whitespace or key-ordering differences — only actual value changes appear.
CSV: ignoring row order
A CSV's rows were reordered in commit 7f0c468:
git diff --stat 7f0c468^..7f0c468 -- example/data.csv
# example/data.csv | 4 ++--
# 1 file changed, 2 insertions(+), 2 deletions(-)
git diff shows a noisy patch, but sorting first reveals the data is unchanged:
git diff-x -R 7f0c468 sort example/data.csv
# (no output — the sorted contents are identical)
CSV: comparing column names
In commit 0bdec95, the city column was replaced with role. Extract and compare just the header:
git diff-x -R 0bdec95 "head -1 | tr , '\n'" example/data.csv
# 3c3
# < city
# ---
# > role
Comparing line counts
Compare line-count (wc -l) of this README, before and after commit 8b7a761:
git-diff-x -R 8b7a761 'wc -l' README.md
# 1c1
# < 16
# ---
# > 206
More examples
# Compare the number of lines (`wc -l`) in file `foo` at the previous vs. current commit
# (`-R HEAD` is equivalent to `-r HEAD^..HEAD`).
git diff-x -R HEAD wc -l foo
# Colorized (`-c`) diff of `md5sum`s of `foo`, at HEAD (last committed value) vs. the current
# worktree content.
git diff-x -c md5sum foo
# Use `-` to separate pipeline commands from paths (when more than one path is to be diffed),
# e.g. this compares the largest 10 numbers in `file{1,2}` (HEAD vs. worktree):
git diff-x 'sort -rn' head - file1 file2
Usage
git-diff-x
# Usage: git-diff-x [OPTIONS] [exec_cmd...] [<path> | - [paths...]]
#
# Diff files at two commits, or one commit and the current worktree, after
# applying an optional command pipeline.
#
# Examples:
#
# # Compare the number of lines (`wc -l`) in file `foo` at the previous vs.
# current commit (`-r HEAD^..HEAD`):
#
# git diff-x -r HEAD^..HEAD wc -l foo
#
# # Colorized (`-c`) diff of `md5sum`s of `foo`, at HEAD (last committed
# value) vs. the current worktree content:
#
# git diff-x -c md5sum foo
#
# # Use `-` to separate pipeline commands from paths (when more than one path
# is to be diffed), e.g. this compares the largest 10 numbers in `file{1,2}`
# (HEAD vs. worktree):
#
# git diff-x 'sort -rn' head - file1 file2
#
# Options:
# -c, --color / --no-color Colorize the output (default: auto, based on
# TTY)
# -r, --refspec TEXT <commit 1>..<commit 2> (compare two commits) or
# <commit> (compare <commit> to the worktree)
# -R, --ref TEXT Diff a specific commit; alias for `-r
# <ref>^..<ref>`
# -t, --staged Compare HEAD vs. staged changes (index)
# -P, --pipefail Check all pipeline commands for errors (like
# bash's `set -o pipefail`); default only checks
# last command
# -s, --shell-executable TEXT Shell to use for executing commands; defaults
# to $SHELL
# -S, --no-shell Don't pass `shell=True` to Python
# `subprocess`es
# -U, --unified INTEGER Number of lines of context to show (passes
# through to `diff`)
# -V, --version Show version and exit
# -v, --verbose Log intermediate commands to stderr
# -w, --ignore-whitespace Ignore whitespace differences (pass `-w` to
# `diff`)
# -x, --exec-cmd TEXT Command(s) to execute before invoking `comm`;
# alternate syntax to passing commands as
# positional arguments
# --help Show this message and exit.
diff-x
The underlying building block — same concept, but for two arbitrary files (not Git commits).
Examples
Given two similar JSON objects, where one is compact and the other is pretty-printed:
echo '{"a":1,"b":2}' > 1.json
echo '{"a":1,"b":3}' | jq > 2.json
diff {1,2}.json outputs the entirety of both objects:
1c1,4
< {"a":1,"b":2}
---
> {
> "a": 1,
> "b": 3
> }
diff-x 'jq .' {1,2}.json pretty-prints each side before diffing:
3c3
< "b": 2
---
> "b": 3
Usage
diff-x
# Usage: diff-x [OPTIONS] [exec_cmd...] <path1> <path2>
#
# Diff two files after running them through a pipeline of other commands.
#
# Options:
# -c, --color / --no-color Colorize the output (default: auto, based on
# TTY)
# -P, --pipefail Check all pipeline commands for errors (like
# bash's `set -o pipefail`); default only checks
# last command
# -s, --shell-executable TEXT Shell to use for executing commands; defaults
# to $SHELL
# -S, --no-shell Don't pass `shell=True` to Python
# `subprocess`es
# -U, --unified INTEGER Number of lines of context to show (passes
# through to `diff`)
# -V, --version Show version and exit
# -v, --verbose Log intermediate commands to stderr
# -w, --ignore-whitespace Ignore whitespace differences (pass `-w` to
# `diff`)
# -x, --exec-cmd TEXT Command(s) to execute before invoking `comm`;
# alternate syntax to passing commands as
# positional arguments
# --help Show this message and exit.
comm-x
comm performs set intersection/difference; comm-x lets you run a pipeline on each input first.
Examples
Given two similar lists of numbers, but in different orders:
seq 10 > 1.txt
seq 10 -2 0 > 2.txt
comm outputs gibberish, because the files aren't in sorted order:
comm 1.txt 2.txt
# 1
# 10
# 2
# 3
# 4
# 5
# 6
# 7
# 8
# comm: file 2 is not in sorted order
# 6
# 4
# 2
# 0
# 9
# comm: file 1 is not in sorted order
# 10
# comm: input is not in sorted order
comm-x sort sorts each file first:
comm-x sort 1.txt 2.txt
# 0
# 1
# 10
# 2
# 3
# 4
# 5
# 6
# 7
# 8
# 9
Shell Integration
Add convenient aliases to your shell by adding this to your ~/.bashrc or ~/.zshrc:
eval "$(dffs-shell-integration bash)"
This provides aliases with the following suffix conventions:
c= color,n= no-color,w= ignore-whitespacer= ref (-R, compare commit to parent),s= refspec (-r),t= staged (--staged)
Examples
| Alias | Expands to |
|---|---|
dx |
diff-x |
dxc |
diff-x --color |
cx |
comm-x |
gdx |
git diff-x |
gdxc |
git diff-x --color |
gdxr |
git diff-x -R (compare commit to parent) |
gdxs |
git diff-x -r (explicit refspec) |
gdxt |
git diff-x --staged |
gdxcr |
git diff-x --color -R |
gdxtw |
git diff-x --staged -w |
To see all available aliases:
dffs-shell-integration bash
To load only aliases for a specific command:
eval "$(dffs-shell-integration bash git-diff-x)"
Metadata
Release files for dffs 0.1.7
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| dffs-0.1.7.tar.gz | 37.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| dffs-0.1.7-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 53.5 kB
Release files / dffs-0.1.7.tar.gz
| Download URL | dffs-0.1.7.tar.gz |
|---|---|
| Size | 37.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9340b783aa4a5a36b289821c59e132707589f2e8e4de15c0ee22c9a9a0ddc613
|
|
BLAKE2b-256 checksum How to use checksums |
efe53b19db0093a92ade0f4a7c983fcda292ed0da5dc48367766ef4f54d0f009
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 15, 2026.
Transparency logRelease files / dffs-0.1.7-py3-none-any.whl
| Download URL | dffs-0.1.7-py3-none-any.whl |
|---|---|
| Size | 16.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
feb40994f197c5b94ea124caf37fd150a8b7729cf9c233b5012b98d24526c3ce
|
|
BLAKE2b-256 checksum How to use checksums |
ea55dae8982c989dbea3aaa73a61648242543e1795e6a95aa9593813b62f4c60
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 15, 2026.
Transparency log