feat(git-changed): merge-base changed-file detection as a package - #101
Merged
Conversation
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
New unscoped package
git-changed: the git plumbing for "what did I change" — merge-base diff ∪ working tree ∪ untracked — as a library plus a bin, so tools stop re-implementing it. Zero dependencies (deliberate:@pgsql/lintis a consumer and currently has none).We have three copies of this today, and they disagree in ways that are bugs, not style:
pgpmpackaging/check.ts@pgsql/lintchanged.tsstatus --porcelain-uall-uallorigin/$GITHUB_BASE_REFor nothingbase...HEADmerge-basemerge-base,-Mdiff HEADsourcefromThe
-uallrow is the one that bites: without itgit statusreports a brand-new directory as a single entry, so every file in a new pgpm module is invisible to the drift check locally. And the base-existence check matters because$GITHUB_BASE_REFis set on every PR, butorigin/<ref>only exists if it was fetched — the other two hand back a ref that every later git call rejects.Shape
Three things make it usable as tooling glue rather than a git wrapper:
Status is preserved, not flattened.
{ path, relative, status, from, committed, worktree, exists }— a caller can tell an added file from a rename destination, andcommitted/worktreeare independent flags because one file can be both.Degradation is reported, not hidden.
sourceis'merge-base' | 'base' | 'worktree', so a gate can decide for itself:Nothing throws for a missing base — a gate that fails open on a detached checkout checks nothing, which is worse than checking the working tree. Only unusable git or a non-repo
cwdraisesGitChangedError.Filtering is built in, since every consumer wants it:
ext('sql','.SQL','.ts,.tsx',['.ts']all normalize),include/excludeglobs,withindirs,existingOnly. PlusGitChanged, for tools asking several questions of one repo, resolving the root and base once:CLI
git-changed --ext .sql --status git-changed --ext .ts --null | xargs -0 -r prettier --check git-changed --base origin/develop --json--nullwrites no trailing newline (it would become part of the last filename underxargs -0), and the exit code is0whether or not anything changed — an empty list is an answer, documented becausegit-changed && …reads like it should mean something else.Two deviations from repo convention worth calling out:
inquirerer, and no separate-clipackage. This bin is a non-interactive printer in thegit diff --name-onlymould — it has no prompts, and its whole point is being pipeable. Adding a prompt library would put dependencies into a package meant to be depended on by libraries. Argument parsing is ~80 lines and handles--flag value,--flag=value, and repeatable/comma-separated lists.gitis invoked with an argv array, never a shell string (spawnSync('git', ['merge-base', 'HEAD', base])), unlike thegit(\diff ${base}...HEAD`)string interpolation in the implementations this replaces. A branch namedfeat/it's-fine` is then just a string, not a quoting bug.Tests
37 tests, each against a real repo built in a tmpdir — merge base vs base tip (a commit landing on
mainafter the fork must not be attributed to the branch),-uallon a new directory, rename destination +from, deleted paths in and out,$GITHUB_BASE_REFboth fetched and not, the genuinely baseless repo, git-quoted paths (we ird/na"me.sql), subdirectorycwd(git speaks repo-root paths; results are relative tocwd,../and all), each filter, and the CLI's output modes and exit codes.Follow-ups (not here)
@pgsql/lintkeepschangedSqlFilesas a 3-line filter over this;pgpm'scheck.tsdrops ~35 lines and picks up both fixes. Both are separate PRs in their own repos, after this publishes — nothing depends on it yet.Link to Devin session: https://app.devin.ai/sessions/e84444b40007481e9de0285d8b5340b6
Requested by: @pyramation