From: Jeff King <peff@peff•net>
To: Junio C Hamano <gitster@pobox•com>
Cc: Sruteesh Kumar <sruteesh.oss@protonmail•com>,
"git@vger•kernel.org" <git@vger•kernel.org>
Subject: [PATCH] doc: document backslash in gitignore patterns
Date: Wed, 29 Oct 2025 11:32:37 -0400 [thread overview]
Message-ID: <20251029153237.GA1133542@coredump.intra.peff.net> (raw)
In-Reply-To: <20251028231945.GA4128296@coredump.intra.peff.net>
On Tue, Oct 28, 2025 at 07:19:45PM -0400, Jeff King wrote:
> So I think the input is really nonsense, and we're following POSIX here
> in rejecting it. I can't fault an alternative implementation too much
> for treating the "\" as a literal char, since that's the only other
> sensible behavior. It's probably what I'd do if I hadn't read that bit
> of POSIX. ;)
>
> But to a certain degree, I think this is a case of "if it hurts, don't
> do it". If you are trying to match "foo\", the correct pattern is
> "foo\\".
This was all non-obvious enough to me (and to the JGit folks) that I
think it might be worth beefing up the docs a little, like this:
-- >8 --
Subject: [PATCH] doc: document backslash in gitignore patterns
Because gitignore patterns are passed to fnmatch, the handling of
backslashes is the same as it is there: it can be used to escape
metacharacters. We do reference fnmatch(3) for more details, but it may
be friendlier to point out this implication explicitly (especially for
people who want to know about backslash handling and search the
documentation for that word). There are also two cases that I've seen
some other backslash-escaping systems handle differently, so let's
describe those:
1. A backslash before any character treats that character literally,
even if it's not otherwise a meta-character. As opposed to
including the backslash itself (like "foo\bar" in shell expands to
"foo\bar") or forbidding it ("foo\zar" is required to produce a
diagnostic in C).
2. A backslash at the end of the string is an invalid pattern (and not
a literal backslash).
This second one in particular was a point of confusion between our
implementation and the one in JGit. Our wildmatch behavior matches what
POSIX specifies for fnmatch, so the code and documentation are in line.
But let's add a test to cover this case. Note that the behavior here
differs between wildmatch itself (which is what gitignore will use) and
pathspec matching (which will only turn to wildmatch if a literal match
fails). So we match "foo\" to "foo\" in pathspecs, but not via
gitignore.
Signed-off-by: Jeff King <peff@peff•net>
---
I like the compact format of the tests in t3070, but it is not at all
obvious what the two lines of zeroes in ones means just from the diff
context. ;)
The first line is wildmatch directly, and the second is pathspec
matching. The surrounding tests, without a second set, default to the
same outcome for both.
Documentation/gitignore.adoc | 5 +++++
t/t3070-wildmatch.sh | 2 ++
2 files changed, 7 insertions(+)
diff --git a/Documentation/gitignore.adoc b/Documentation/gitignore.adoc
index 5e0964ef41..9fccab4ae8 100644
--- a/Documentation/gitignore.adoc
+++ b/Documentation/gitignore.adoc
@@ -111,6 +111,11 @@ PATTERN FORMAT
one of the characters in a range. See fnmatch(3) and the
FNM_PATHNAME flag for a more detailed description.
+ - A backslash ("`\`") can be used to escape any character. E.g., "`\*`"
+ matches a literal asterisk (and "`\a`" matches "`a`", even though
+ there is no need for escaping there). As with fnmatch(3), a backslash
+ at the end of a pattern is an invalid pattern that never matches.
+
Two consecutive asterisks ("`**`") in patterns matched against
full pathname may have special meaning:
diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh
index 3da824117c..655bb1a0f2 100755
--- a/t/t3070-wildmatch.sh
+++ b/t/t3070-wildmatch.sh
@@ -235,6 +235,8 @@ match 1 1 1 1 aaaaaaabababab '*ab'
match 1 1 1 1 'foo*' 'foo\*'
match 0 0 0 0 foobar 'foo\*bar'
match 1 1 1 1 'f\oo' 'f\\oo'
+match 0 0 0 0 \
+ 1 1 1 1 'foo\' 'foo\'
match 1 1 1 1 ball '*[al]?'
match 0 0 0 0 ten '[ten]'
match 1 1 1 1 ten '**[!te]'
--
2.51.2.833.g5cd7b514cb
next prev parent reply other threads:[~2025-10-29 15:32 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-10 14:57 Probable issue with code/documentation Sruteesh Kumar
2025-10-14 0:34 ` [PATCH] match_pathname(): give fnmatch one char of prefix context Jeff King
2025-10-14 3:09 ` Jeff King
2025-10-22 16:19 ` Sruteesh Kumar
2025-10-23 20:28 ` Junio C Hamano
2025-10-24 18:28 ` Sruteesh Kumar
2025-10-26 15:18 ` Jeff King
2025-10-26 15:26 ` Jeff King
2025-10-26 15:40 ` [PATCH v2 0/2] fix "foo**/bar" matching "foobar" Jeff King
2025-10-26 15:41 ` [PATCH v2 1/2] match_pathname(): reorder prefix-match check Jeff King
2025-10-26 15:42 ` [PATCH v2 2/2] match_pathname(): give fnmatch one char of prefix context Jeff King
2025-10-26 23:29 ` [PATCH] " Junio C Hamano
2025-10-27 14:29 ` Jeff King
2025-10-27 15:35 ` Junio C Hamano
2025-10-28 23:19 ` Jeff King
2025-10-29 15:32 ` Jeff King [this message]
2025-10-29 15:55 ` [PATCH] doc: document backslash in gitignore patterns Jeff King
2025-10-30 13:40 ` D. Ben Knoble
2025-10-30 15:08 ` Jeff King
2025-10-30 16:05 ` Ben Knoble
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251029153237.GA1133542@coredump.intra.peff.net \
--to=peff@peff$(echo .)net \
--cc=git@vger$(echo .)kernel.org \
--cc=gitster@pobox$(echo .)com \
--cc=sruteesh.oss@protonmail$(echo .)com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox