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.

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.
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.
| Branch | Test that failed | Did git add run? | What it means |
|---|---|---|---|
Outer else | git status -s against file_pattern was empty | No | Nothing your pattern matches changed at all |
Inner else | git diff --staged was empty after staging | Yes | Something 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
- Scroll up from the sentence and look for
INPUT_ADD_OPTIONS:. - If it is there,
git addran and you are on the inner branch: something changed and staging produced no diff. - 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.
- id: autocommit
uses: stefanzweifel/git-auto-commit-action@v7
- name: Tag the release
if: steps.autocommit.outputs.changes_detected == 'true'
run: ./scripts/tag.shPoint 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.
- uses: stefanzweifel/git-auto-commit-action@v7
with:
file_pattern: 'src/**/*.ts docs/**/*.md'Stop looking for git's sentence in this step
- If your log really does contain
nothing to commit, working tree clean, find therun:step that invoked git directly; the action cannot print that form. - Decide whether that step should be committing at all, given this action is already in the job.
- If it should, give it its own guard rather than letting a non-zero
git commitfail 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 sentence | The state it describes |
|---|---|
nothing to commit, working tree clean | Nothing staged, nothing modified, no untracked files |
no changes added to commit | Files are modified but none are staged |
nothing added to commit but untracked files present | Only untracked files are present |
nothing to commit, on an initial commit | The repository has no commits yet |
nothing to commit, with untracked files hidden | Untracked 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.
- 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_detectedwherever a later step depends on a commit having happened. - Keep
file_patternin 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?
Why does my log say "nothing to commit, working tree clean" instead?
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?
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?
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
- stefanzweifel/git-auto-commit-action: entrypoint.sh, both clean-tree branches and the dirty checks
- stefanzweifel/git-auto-commit-action: action.yml, the changes_detected description naming the CRLF case
- git: wt-status.c, the branch that chooses between the five "nothing" sentences
- git-status documentation, for what the short format reports
- Git reference manual
- GitHub Actions documentation