Skip to content

fix(core): honor the gitignore \# escape when loading ignore patterns - #1540

Open
wangzhengzhuo05 wants to merge 1 commit into
basicmachines-co:mainfrom
wangzhengzhuo05:fix/bmignore-hash-escape
Open

fix(core): honor the gitignore \# escape when loading ignore patterns#1540
wangzhengzhuo05 wants to merge 1 commit into
basicmachines-co:mainfrom
wangzhengzhuo05:fix/bmignore-hash-escape

Conversation

@wangzhengzhuo05

Copy link
Copy Markdown

What

.bmignore (and project .gitignore) could not express any pattern beginning with #.

Why

Both loaders used a naive line filter that skipped every line starting with # and never
unescaped a leading backslash:

  • #*# was silently dropped as a comment.
  • \#*# was stored literally, and should_ignore_path() matches with fnmatch, which
    has no escape syntax — so it matched nothing.

The failure mode is silent and misleading: the pattern looks right in .bmignore and
simply never loads. It makes Emacs autosave files (#file#) impossible to ignore through
the documented gitignore-style syntax, even though the shipped defaults already cover
*~, *.swp and *.swo. As the issue notes, the matcher itself handles #*# correctly
(it fnmatches each path part) — only the loader was broken.

How

Added one small module-level helper, _parse_ignore_pattern_line(), and routed both the
.bmignore and .gitignore read loops through it:

  • blank lines and comments (leading #) are still skipped;
  • a line starting with \# now yields the pattern with one leading backslash stripped,
    per the gitignore rule "Put a backslash in front of the first hash for patterns that
    begin with a hash"
    — so \#*# loads as #*# and matches.

should_ignore_path() is unchanged, DEFAULT_IGNORE_PATTERNS is unchanged (adding a
default #*# would be a separate product decision), and no dependency was added
(no pathspec).

Tests

Three regression tests in tests/cli/test_ignore_utils.py:

  • test_bmignore_escaped_hash_pattern_loads\#*# in .bmignore loads as #*#, the
    comment line is still skipped, and should_ignore_path("notes/#note.md#") is True.
  • test_bmignore_bare_hash_is_still_a_comment — a bare #*# remains a comment.
  • test_gitignore_escaped_hash_pattern_loads — same escape handling for a project
    .gitignore.
$ .venv/bin/python -m pytest tests/cli/test_ignore_utils.py -q
17 passed
$ ruff check  (both files)         -> All checks passed!
$ ruff format --check (both files) -> 2 files already formatted

Mutation check

Reverting only the escape handling in _parse_ignore_pattern_line() (the \# branch
removed, everything else intact) fails exactly the two tests that guard the fix:

FAILED tests/cli/test_ignore_utils.py::test_bmignore_escaped_hash_pattern_loads
FAILED tests/cli/test_ignore_utils.py::test_gitignore_escaped_hash_pattern_loads
2 failed, 1 passed, 14 deselected

Restoring the fix → 17 passed.

Fixes #1539

.bmignore documents gitignore-style syntax, but the loader skipped any line
beginning with '#' and never unescaped a leading backslash, so no pattern
beginning with a hash could take effect: '#*#' was dropped as a comment and
'\#*#' was stored literally and never matched. Strip one leading backslash when
a line starts with '\#', for both .bmignore and project .gitignore, so e.g.
'\#*#' loads as '#*#' (Emacs autosave files).

Fixes basicmachines-co#1539

Signed-off-by: wangzhengzhuo05 <[email protected]>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fbffd1a117

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +78 to +82
line = raw_line.strip()
if not line or line.startswith("#"):
return None
if line.startswith("\\#"):
return line[1:]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve leading whitespace before unescaping hashes

When a .gitignore contains \#foo, a valid pattern for the filename #foo, strip() removes the significant leading space and the new escape branch converts the remainder to #foo. Basic Memory therefore ignores/index-excludes the wrong file, while git check-ignore --no-index matches only #foo; Git's pattern format discards unescaped trailing spaces, not leading ones. Preserve leading characters when removing the line ending before recognizing the hash escape.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] .bmignore cannot express any pattern beginning with #, despite documenting gitignore-style syntax

1 participant