git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH] doc: document backslash in gitignore patterns

From
Jeff King <peff@peff.net>
Date
Oct 29, 2025, 15:32 UTC
Message-ID
<20251029153237.GA1133542@coredump.intra.peff.net>
In-Reply-To
<20251028231945.GA4128296@coredump.intra.peff.net>
On Tue, Oct 28, 2025 at 07:19:45PM -0400, Jeff King wrote:
Show 9 quoted lines
> 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
Previous: Jeff KingNext: Jeff King
Message 16 of 20 in “Probable issue with code/documentation”
  1. Sruteesh KumarOct 10, 2025
  2. match_pathname(): give fnmatch one char of prefix contextJeff King, Oct 14, 2025
  3. Jeff KingOct 14, 2025
  4. Sruteesh KumarOct 22, 2025
  5. Junio C HamanoOct 23, 2025
  6. Sruteesh KumarOct 24, 2025
  7. Jeff KingOct 26, 2025
  8. Jeff KingOct 26, 2025
  9. 0/2 fix "foo**/bar" matching "foobar"Jeff King, Oct 26, 2025
  10. 1/2 match_pathname(): reorder prefix-match checkJeff King, Oct 26, 2025
  11. 2/2 match_pathname(): give fnmatch one char of prefix contextJeff King, Oct 26, 2025
  12. Junio C HamanoOct 26, 2025
  13. Jeff KingOct 27, 2025
  14. Junio C HamanoOct 27, 2025
  15. Jeff KingOct 28, 2025
  16. doc: document backslash in gitignore patternsJeff King, Oct 29, 2025
  17. Jeff KingOct 29, 2025
  18. D. Ben KnobleOct 30, 2025
  19. Jeff KingOct 30, 2025
  20. Ben KnobleOct 30, 2025

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.