Skip to content
Latchkey LogoLatchkey home

git-auto-commit nothing to commit is two different sentences

git-auto-commit nothing to commit is a line the action prints itself, and it is not the line git prints. The action writes Working tree clean. Nothing to commit. from two different places in its shell entry point, and which of the two ran tells you whether the dirty check failed before staging or after it.

Two branches of the action that both print a clean tree, one before staging and one after
Both branches are read from entrypoint.sh on master. The second runs after git add and compares git diff --staged, which is why it exists at all.

What this error means

The step is green, the log ends with a short sentence about a clean tree, and no commit was pushed. The first thing to establish is whose sentence you are reading, because two very similar ones circulate. Working tree clean. Nothing to commit. is the action, capitalised and split into two sentences. nothing to commit, working tree clean is git, lower case with a comma, and the action never lets git reach the state that prints it, so that form in your log came from a run: step of your own. Nothing here is an error in either case: the action exits zero, the step is green, and any downstream step that assumed a commit happened is the thing that will actually break.

Reconstructed from the echo statements in entrypoint.sh, with the file-pattern line that precedes them
INPUT_FILE_PATTERN: .
Working tree clean. Nothing to commit.

Two branches print the same sentence

_main in entrypoint.sh decides once, up front, by calling _git_is_dirty. That helper runs git status -s with your status_options and your file_pattern and reports dirty when the output is non-empty. If it is not dirty, the outer else sets changes_detected to false and prints the sentence. Nothing was staged, because staging never happened.

If it is dirty the action switches branch, runs the add hooks and git add, and then checks a second time with git diff --staged. If that is empty, the inner else sets changes_detected to false and prints the same sentence again. The action's own action.yml explains what that second check is guarding: the output description says the value is false when no matching changes were detected or only CRLF changes were staged.

BranchTest that failedDid git add run?What it means
Outer elsegit status -s against file_pattern was emptyNoNothing your pattern matches changed at all
Inner elsegit diff --staged was empty after stagingYesSomething changed, but staging it produced no diff

Common causes

The generator produced bytes identical to what is committed

The honest, common and entirely correct case. A formatter that has nothing left to format and a codegen step whose output has not changed both land here, on the outer branch, and the action behaving this way is the point of it. The failure, if there is one, is downstream in a step that assumed a commit.

The file pattern does not match what the previous step wrote

The pattern drives both the dirty check and the staging, so a mismatch makes the whole repository look clean to the action. In our experience this is the cause whenever the log says clean and a manual git status in the next step says otherwise.

Only line endings changed, so the staged diff was empty

The inner branch exists for this. A .gitattributes normalisation or a tool that rewrites files with different newlines makes git status report a change and git diff --staged report none. The action documents this in its changes_detected output description, which is the clearest statement of it anywhere.

A downstream step treated the message as a failure

Not a cause of the message but the reason people search for it. Because the step is green and the log looks like an error, workflows often grow a grep on the log text or a step that assumes a commit hash output exists. Both break the first time the tree is legitimately clean.

How to fix it

Work out which branch printed it

  1. Scroll up from the sentence and look for INPUT_ADD_OPTIONS:.
  2. If it is there, git add ran and you are on the inner branch: something changed and staging produced no diff.
  3. If it is not there, the dirty check failed first and nothing your pattern matches was modified.

Gate downstream steps on the output, not on the log

Read changes_detected from the step and branch on it. It is set on both paths, it is the action's answer after both checks, and it does not depend on parsing a sentence that two different programs can print.

.github/workflows/format.yml (illustrative)
      - id: autocommit
        uses: stefanzweifel/git-auto-commit-action@v7
      - name: Tag the release
        if: steps.autocommit.outputs.changes_detected == 'true'
        run: ./scripts/tag.sh

Point the file pattern at the paths your step writes

Set file_pattern to the directories the generator actually touches, and compare the echoed value in the log against the paths in the step above it. Leaving the default of . is fine; what is not fine is a pattern that was written for an older layout.

.github/workflows/format.yml (illustrative)
      - uses: stefanzweifel/git-auto-commit-action@v7
        with:
          file_pattern: 'src/**/*.ts docs/**/*.md'

Stop looking for git's sentence in this step

  1. If your log really does contain nothing to commit, working tree clean, find the run: step that invoked git directly; the action cannot print that form.
  2. Decide whether that step should be committing at all, given this action is already in the job.
  3. If it should, give it its own guard rather than letting a non-zero git commit fail the job.

Git has five ways to say nothing happened, and only one is the famous one

The confusion around this step is mostly borrowed from git, so it is worth separating. The final block of wt_longstatus_print in git's wt-status.c chooses between several sentences depending on what kind of nothing it found, and the one everybody quotes is the last branch, reached only when there is nothing staged, nothing unstaged, no untracked files and the commit is not the first.

None of these appear in a git-auto-commit step, because the action checks before it commits and never calls git commit in that state. Seeing one of them means a run: step in your workflow called git directly.

Git's sentenceThe state it describes
nothing to commit, working tree cleanNothing staged, nothing modified, no untracked files
no changes added to commitFiles are modified but none are staged
nothing added to commit but untracked files presentOnly untracked files are present
nothing to commit, on an initial commitThe repository has no commits yet
nothing to commit, with untracked files hiddenUntracked files were suppressed by an option

The output is written twice on one of the paths

On the inner branch the script sets changes_detected to true before it stages, and then sets it to false again when the staged diff turns out to be empty. Both writes go to the same output name, one after the other, so a workflow reading that output on this path is depending on the second write.

This is worth knowing because it explains why gating on the output is the right move rather than gating on the log. The value is the action's considered answer after both checks; the sentence is only the last thing it printed.

.github/workflows/format.yml (illustrative)
      - id: commit
        uses: stefanzweifel/git-auto-commit-action@v7
        with:
          file_pattern: 'src/**/*.ts'
      - if: steps.commit.outputs.changes_detected == 'true'
        run: echo "there was something to commit"

Why the file pattern can produce a clean tree that is not clean

file_pattern defaults to . and is used in two places: the dirty check and the git add. Both read it through the same expansion, so a pattern that matches nothing makes the repository look clean to this action while git status in the next step shows changes. The action echoes INPUT_FILE_PATTERN: immediately before running the status command, which is the line to compare against what your generator actually wrote.

The second half of that trap is add_options. Options that exclude what the pattern matched, or a pattern that matches files already ignored, leave the staged diff empty and send you to the inner branch with the identical sentence.

Why there is no recorded run on this page

Nothing fails here. Both branches exit zero and the step is green, so a recorded run would be a screenshot of a passing job with one line of output, which proves less than reading the two echo statements that produce it. The interesting question is which branch ran, and that is answered by lines the script prints from its own inputs rather than by anything a runner contributes.

How to prevent it

  • Branch on changes_detected wherever a later step depends on a commit having happened.
  • Keep file_pattern in step with the paths your generators write, and review it when the layout moves.
  • Decide once whether line-ending normalisation belongs in the repository or in the tool, so the inner branch stops firing.
  • Never grep an action's log text for control flow; outputs exist for that and do not change wording between majors.

Frequently asked questions

Does git-auto-commit fail when there is nothing to commit?
No. Both clean-tree branches print a line and let the script end normally, so the step is green and the job continues. If something later in the job breaks, it is because that step assumed a commit existed, not because the action reported a failure.
Why does my log say "nothing to commit, working tree clean" instead?
Because that sentence is git's, printed by wt_longstatus_print in wt-status.c, and this action never calls git commit in a state that would produce it. A run: step in the same job invoked git directly. The action's own wording is Working tree clean. Nothing to commit.
The files definitely changed. Why does the action see a clean tree?
Either the file_pattern does not match the paths that changed, since the same pattern drives the dirty check and the staging, or the change survived git status but produced no staged diff. The second case is the line-ending one the action names in its changes_detected output description.
How do I tell which of the two clean-tree branches ran?
Look for INPUT_ADD_OPTIONS: above the sentence. _add_files echoes it just before running git add, so its presence means staging happened and the second check is what came back empty. Its absence means the first check did.

Related guides

References

A run that produces no commit is still a run you pay for. Latchkey is $0.0025/min at 2 vCPU. Start free → 30-day trial · No credit card