threads / bug / 64167

[bug] git check-ignore returns the wrong exit code with -v when only a negative pattern matches

Subject: [bug] git check-ignore returns the wrong exit code with -v when only a negative pattern matches

## tl;dr

4 messages between Sep 18, 2025 and Nov 29, 2025.

replies: 3people: 3as markdown or json

David Goldstein· Sep 18, 2025, 17:28 UTC · lore

Hey folks, I think I found a git check-ignore bug. According to the docs, git check-ignore should only exit 0 if a file is ignored, but if an untracked file matches a negative pattern in .gitignore (or the file can be tracked if --no-index is also used), then git check-ignore -v <file> exits 0 when it should exit 1; without -v the exit code is correct (0).

https://github.com/dgoldstein0/git_bug_repro has a self-contained reproduction + repeated explanation.

This exists in all git versions I've tested, but I haven't tried to get the latest dev version to check if it's still a problem in the latest version.

Jeff King· Sep 18, 2025, 18:25 UTC · re: David Goldstein · lore

Re: [bug] git check-ignore returns the wrong exit code with -v when only a negative pattern matches

On Thu, Sep 18, 2025 at 10:28:05AM -0700, David Goldstein wrote:
Show 13 quoted lines
> Hey folks, I think I found a git check-ignore bug.  According to the
> docs, git check-ignore should only exit 0 if a file is ignored, but if
> an untracked file matches a negative pattern in .gitignore (or the
> file can be tracked if --no-index is also used), then git check-ignore
> -v <file> exits 0 when it should exit 1; without -v the exit code is
> correct (0).
> 
> https://github.com/dgoldstein0/git_bug_repro has a self-contained
> reproduction + repeated explanation.
> 
> This exists in all git versions I've tested, but I haven't tried to
> get the latest dev version to check if it's still a problem in the
> latest version.

I can reproduce it here with the latest version. I've never looked at the check-ignore code before, but it looks like the issue is something like:

  1. We "count" ignored files by seeing if the matched "pattern"
     variable is left non-NULL.
  2. In non-verbose mode, we set the pattern to NULL when it is a
     negative pattern. Makes sense.
  3. In verbose mode, we don't do that because we need to show the
     pattern. So we accidentally count the entry as ignored.
So something like this makes your repo behave as you expected:
diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c
index 644c9a414f..808c0e5ff4 100644
--- a/builtin/check-ignore.c
+++ b/builtin/check-ignore.c
@@ -117,7 +117,7 @@ static int check_ignore(struct dir_struct *dir,
 		}
 		if (!quiet && (pattern || show_non_matching))
 			output_pattern(pathspec.items[i].original, pattern);
-		if (pattern)
+		if (pattern && !(pattern->flags & PATTERN_FLAG_NEGATIVE))
 			num_ignored++;
 	}
 	free(seen);

AFAICT it has been this way since the inception of the code. I haven't
ever used the exit code of check-ignore. I wonder if the current
behavior is actually useful, along the lines of "exit 0 if any output
was shown, and 1 otherwise". That would justify a difference in behavior
between running with "-v" and without. But again, I've never used the
exit code so I'm not sure in what circumstances it would be useful.

-Peff
Junio C Hamano· Sep 18, 2025, 20:19 UTC · re: Jeff King · lore

Re: [bug] git check-ignore returns the wrong exit code with -v when only a negative pattern matches

Jeff King <peff@peff.net> writes:
Show 6 quoted lines
> AFAICT it has been this way since the inception of the code. I haven't
> ever used the exit code of check-ignore. I wonder if the current
> behavior is actually useful, along the lines of "exit 0 if any output
> was shown, and 1 otherwise". That would justify a difference in behavior
> between running with "-v" and without. But again, I've never used the
> exit code so I'm not sure in what circumstances it would be useful.

I very much agree with your assessment, as my understanding is that the command is primarily for debugging your .gitignore pattern by eyeballing the output from it (as opposed to a serious tool to see if a particular path is or is not ignored), so I am not surprised if its exit code handling is buggy, and I am not suprirsed at all if nobody has even noticed it is buggy ;-).

David Goldstein· Nov 29, 2025, 05:02 UTC · re: Junio C Hamano · lore

Re: [bug] git check-ignore returns the wrong exit code with -v when only a negative pattern matches

I meant to reply with some info about how I discovered this and just realized I forgot. Anyhow I don't think it'll change how yall prioritize.

I was reviewing a change to swap grep for ripgrep, and was trying to understand why we got different results with ripgrep with and without --no-ignore. Hence, I was testing some of the files that showed up as differences with git check-ignore - which didn't really answer the question either. So I ended up using git check-ignore -v and inspecting exit codes as a way to try to verify my own sanity, trying to understand how a file could be both ignored but not ignored, hence stumbling over this bug. In the end I figured out that the differences were that my repository had tracked files which matched .gitignore patterns (which therefore aren't ignored files), and ripgrep doesn't know what is tracked or not, just what matches .gitignore patterns. That fully explained the apparent misbehavior (afaict ripgrep doesn't call git check-ignore in any way), but along the way I found this check-ignore edge case.

On Thu, Sep 18, 2025 at 3:19 PM Junio C Hamano <gitster@pobox.com> wrote:
Show 18 quoted lines
>
> Jeff King <peff@peff.net> writes:
>
> > AFAICT it has been this way since the inception of the code. I haven't
> > ever used the exit code of check-ignore. I wonder if the current
> > behavior is actually useful, along the lines of "exit 0 if any output
> > was shown, and 1 otherwise". That would justify a difference in behavior
> > between running with "-v" and without. But again, I've never used the
> > exit code so I'm not sure in what circumstances it would be useful.
>
> I very much agree with your assessment, as my understanding is that
> the command is primarily for debugging your .gitignore pattern by
> eyeballing the output from it (as opposed to a serious tool to see
> if a particular path is or is not ignored), so I am not surprised if
> its exit code handling is buggy, and I am not suprirsed at all if
> nobody has even noticed it is buggy ;-).
>
>

← back to recent threads