Documentation/Working with Scribe
Ignore rules & paths3 min read

Ignore rules and path semantics

On this page

Canonical paths

A path is UTF-8, /-separated, relative, with no empty, ., or .. components, at most 8192 bytes, components at most 255 bytes. Rejected on every platform at add and submit time: backslashes, control characters, < > : " | ? *, reserved device names (CON, PRN, AUX, NUL, COM1-COM9, LPT1-LPT9, with or without extension), components ending in a dot or space, and anything under .scribe/. Symbolic links, junctions, and reparse points are never followed, added, or synced through; they are reported.

Case and normalization collisions: the server keeps a current_fold index of NFC-normalized, Unicode-lowercased paths per branch and rejects a submission that would create two paths mapping to the same key (path_collision), including within one submission. Windows/NTFS and macOS/APFS would otherwise map them to one file. Matching of ignore rules is case-sensitive on every platform.

Long paths: Windows paths over 240 characters are opened with the \\?\ extended-length prefix. A 4,000-character path is tested on Linux and Windows (platform_behaviour.rs); macOS caps most syscalls at 1,024 bytes and is not tested for long paths.

Executable bits are stored in file records; applied on Unix, stored only on Windows.

Ignore files

  • Syntax: Git-style via the ignore crate's matcher, with comments, blank lines, \# and \! escapes, *, ?, character classes, **, trailing-slash directory patterns, leading-slash anchoring to the rule file's directory, negation, UTF-8, spaces, LF or CRLF, trailing-space escaping. Unclosed character classes are literal (as in Git). A malformed rule (for example an invalid range) is reported, and commands that discover files refuse to proceed rather than treating the file as empty.
  • Sources and precedence, lowest to highest: --global-ignore <file>; workspace-local .scribe/ignore; versioned .scribeignore files from the root to the deepest applicable directory. Within one file the last matching rule wins. Deeper versioned files override shallower ones.
  • Excluded parents: an ignored directory is not traversed, so a negation in a deeper file cannot recover its contents. scribe ignore explain reports via_parent in that case.
  • Ignore rules affect only untracked discovery and recursive add. Tracked files are always scanned; scribe status --scan reports modifications and deletions of tracked files regardless of rules. Rules never delete data and are not access control. Versioned ignore files are ordinary tracked files.
  • No .gitignore, no global Git configuration, no hidden-file policy, no engine templates are read.
  • scribe add --force <path> overrides ignore matching only; it never overrides containment, .scribe/, links, or name validity.
  • scribe ignore explain <path> prints ignored/not ignored, the deciding file, line, and pattern, and whether a parent directory decided.

Fingerprints

The baseline records size and modification time as reported by the filesystem. Equal fingerprints skip hashing in status; status --verify hashes. Fingerprints are caches, not proof of unchanged bytes. Resolution differs by filesystem (NTFS 100 ns ticks; some filesystems 1 or 2 s), so only exact equality is meaningful.

Concurrent external writers

Submit re-stats a file after hashing it and re-hashes every chunk range while uploading, so a file that changes during a submit is refused. A before/after stat is not an atomic snapshot: a writer that modifies a file between the final stat and the upload read is caught by the chunk digest check; a writer that modifies and restores bytes within one fingerprint tick is not. Editors should be quiescent during submit.

Source docs/ignore-and-paths.mdSnapshot 93d02b17