Skip to content

v1.0.0

Choose a tag to compare

@gmr gmr released this 11 Aug 22:06
· 3 commits to main since this release
3dd503e

First release of drift.

.driftignore lists the paths whose changes do not matter. Everything else does.
drift compares two git refs, drops the noise, and reports what is left: the
changes worth acting on, who made them, and how long they have been waiting.

$ drift --tree 1.1.0 main --pretty
{
  "drift_detected": true,
  "mode": "tree",
  "drift_commits": ["0f1e2d3..."],
  "drift_authors": ["Ann Author <ann@example.com>"],
  "drift_paths": ["src/app.py", "uv.lock"],
  "oldest_drift": { "sha": "9a8b7c6...", "age_seconds": 1209600 },
  "newest_drift": { "sha": "0f1e2d3...", "age_seconds": 91800 }
}

Features

  • Accepts any two committish refs: a full or short sha, a tag, a branch,
    origin/main, HEAD~3.
  • .driftignore, in gitignore pattern syntax, read from the tree of the newer
    ref
    rather than from disk, so a run never depends on the worktree and always
    repeats the same answer.
  • Two modes. --log, the default, counts every path the range touched, so work
    that was later reverted still reports. --tree counts only the paths whose
    content actually differs between the two refs, then attributes each back to the
    commits that touched it, which is the honest answer to "is a release worth
    cutting".
  • JSON on stdout: the drift flag, the mode, commit shas, authors, paths, and the
    ages of the oldest and newest drift commit.
  • Flags diverged refs, and lists separately any drifting path that no commit in
    the range explains.
  • --fail-on-drift for CI gates. Exit 0 when the analysis ran, 1 for drift with
    that flag, 2 on failure.
  • Runs entirely in-process through gix.
    No git binary is invoked and none needs to be installed.

Defined behavior

Documented in the README and covered by 30 tests:

  • The range is from..to, exactly what git log from..to lists.
  • Merge commits are traversed but never classified; the walk already reaches the
    commits on their sides.
  • Renames are two literal paths, since rename detection is off. Moving a file into
    an ignored directory still reports drift at the source path.
  • Ages come from the committer timestamp against a single clock reading, so the two
    ends of the window are always consistent.
  • Output is newest-first with sorted path lists, byte for byte stable across runs.

Install

Homebrew:

brew install gmr/utils/drift

Install script:

curl -fsSL https://raw.githubusercontent.com/gmr/drift/main/install.sh | sh

Docker, multi-arch (linux/amd64, linux/arm64):

docker pull ghcr.io/gmr/drift:1.0.0
docker run --rm -v "$PWD:/repo:ro" -w /repo ghcr.io/gmr/drift:1.0.0 --tree 1.1.0 main

The binary is statically linked against musl, so it can be lifted into any base
image, glibc or musl:

COPY --from=ghcr.io/gmr/drift:1.0.0 /usr/local/bin/drift /usr/local/bin/drift

From source:

cargo install --git https://github.com/gmr/drift

Pre-built binaries for x86_64/aarch64 on Linux and macOS are attached below.

Notes

Not published to crates.io: the name drift there belongs to an unrelated
project. Use the Homebrew tap, the install script, the container image, or
cargo install --git.