Skip to content

fix(uncheck): prevent an empty commit when the fixes undo every staged change - #10

Merged
dinwwwh merged 5 commits into
mainfrom
claude/elegant-ptolemy-f23fb8
Sep 24, 2026
Merged

dinwwwh merged 5 commits into
mainfrom
claude/elegant-ptolemy-f23fb8

Conversation

@dinwwwh

@dinwwwh dinwwwh commented Sep 23, 2026 •

Copy link
Copy Markdown
Member

uncheck staged --fix no longer lets git record an empty commit. git decides there is something to commit before it runs the pre-commit hook, so when the fixes turned the staged content back into what HEAD has, the commit went through with the same tree as its parent. The hook now fails instead, and --allow-empty lets such a commit through. Both follow lint-staged 17.

Fixes

  • A commit whose staged changes the fixes fully undo, such as a staged export const a=1 over a committed export const a = 1;, now fails with a message instead of recording an empty commit. This covers both git commit and git commit <paths>.
  • Unstaged changes are still put back and reported before the failure.
  • Commits on an unborn branch are unaffected.
  • Concluding a merge is never blocked, since git records a merge commit even when its tree is the one HEAD has.

Behavior, as in lint-staged

  • uncheck staged --allow-empty lets the empty commit through, like lint-staged's flag of the same name. uncheck prepare --pre-commit --allow-empty writes it into the hook line.
  • The error message points to --allow-empty, as lint-staged's "prevented an empty git commit" message does.
  • The fixes stay staged after the failure. lint-staged also keeps them: it only reverts to the original state after task or restore errors.
  • A hook cannot tell an amend from a new commit, so a git commit --amend whose staged changes the fixes undo is refused too, as it is with lint-staged.
  • Two differences, where lint-staged blocks commits that git would record:
    • lint-staged decides emptiness from git diff --staged --diff-filter=ACMR, so a commit that only has staged deletions left counts as empty there. uncheck compares the index tree with HEAD^{tree}, so that commit goes through.
    • lint-staged also blocks concluding a merge whose resolved tree the fixes turn back into HEAD's. uncheck skips the check while MERGE_HEAD exists.

Testing

  • A test covers the empty case with an unstaged hunk, --allow-empty, and a commit on an unborn branch. It fails without this change.
  • Another test concludes a merge whose tree the fixes turn back into HEAD's. It fails without the MERGE_HEAD check.
  • The prepare test checks that --allow-empty is written into the hook line.
  • One test from fix(uncheck): keep unstaged changes safe in uncheck staged #8 staged a change the formatter turns back into HEAD's content. It now commits a different base and still covers what it was written for.
  • Checked with uncheck as the real pre-commit hook: HEAD does not move for git commit or git commit <path>, with --allow-empty git records the empty commit, and that merge is recorded as a merge commit.

@pkg-pr-new

pkg-pr-new Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/uncheck@10

commit: 0a8abea

@codecov

codecov Bot commented Sep 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

Completing a merge whose resolved tree the fixes normalize back to HEAD's tree is blocked as an "empty commit", even though git would create that merge commit. lint-staged shares the limitation, but the PR body documents --amend and not merges — the inline note has a cheap guard.

Reviewed changes

  • staged.ts — after staging the fixes, compares the index tree (write-tree) against HEAD^{tree} and fails when they match, adding a headTree helper that treats an unborn HEAD as no tree.
  • command.test.ts — adds a test for the empty case (with an unstaged hunk) and for a commit on an unborn branch; changes one prior test's base commit so its fix no longer produces an empty tree.
  • README.md — documents the new failure.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (default — pick a model for stronger reviews) | 𝕏

Comment thread packages/uncheck/src/commands/staged.ts Outdated

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No new issues in the incremental changes — --allow-empty is implemented cleanly and covered by tests. The merge-commit concern from the prior review is unaffected by this commit and still stands.

Reviewed changes since the prior review

  • staged.ts — adds an --allow-empty flag; the guard becomes empty && !allowEmpty, and the message now points at the flag instead of git commit --no-verify.
  • prepare.ts — adds --allow-empty and writes it into the hook command after --fix.
  • command.test.ts — extends the empty-case test with a passing --allow-empty run and asserts the hook line in the prepare test.
  • README.md — notes --allow-empty in both the staged and prepare paragraphs.

Pullfrog  | Fix it ➔ | View workflow run | Using DeepSeek Flash (default — pick a model for stronger reviews) | 𝕏

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found — the merge-commit concern from the prior review is now addressed with a working regression test.

Reviewed changes — reviewed the delta since the prior pullfrog review (b318240 → 0a8abea).

  • staged.ts — added a merging helper (git rev-parse -q --verify MERGE_HEAD, mapped to false on GitFailed) and gated the empty check on !(yield* merging(cwd)), so a merge whose resolved tree the fixes normalize back to HEAD^{tree} is no longer blocked. Verified --verify MERGE_HEAD exits non-zero outside a merge and prints its SHA during one.
  • command.test.ts — added a regression test that concludes a --no-commit --no-ff merge after re-staging content the formatter turns back into HEAD's; its resolved tree matches HEAD^{tree}, so it fails without the new guard.

Pullfrog  | View workflow run | Using DeepSeek Flash (default — pick a model for stronger reviews) | 𝕏

@dinwwwh
dinwwwh merged commit 8bf0300 into main Sep 24, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant