pub struct AttributeResolver { /* private fields */ }Expand description
Answers “would git run our filter for this path, and would git convert its line endings”, the way git answers both.
Resolving rather than naming, since 2026-08-04. The previous build listed
every attribute source carrying a filter line and left the reader to run
git check-attr. That was the last route to a green report on a repository
that stores plaintext: a line below the managed section, or a
.gitattributes in a subdirectory, silently outranks the catch-all, and a
note does not fail a CI gate. Naming also cannot tell an ordinary
*.psd filter=lfs from a line that reaches a secret, so it either cried wolf
or said nothing useful.
text joined filter on 2026-08-04, for the same reason and at the same
severity. The managed section writes -text on every encrypted path, and a
line below it saying secrets/** text puts the conversion back — measured on
git 2.55, with sync freshly run so nothing else in this command had a
complaint: 34 CR bytes eaten out of a 2 MB ciphertext, git add and
git commit both exit 0, and the checkout fails the authentication tag and
leaves no file at all. status printed VERDICT: no findings. over it. An
unresolved filter costs a plaintext secret; this costs the file outright,
and both answer the same question with “your declaration is not enforced”.
The stack reproduced here is git’s, in git’s precedence order — lowest first,
because gix_attributes::Search matches its lists in reverse:
- the built-in
[attr]binarymacro; core.attributesFile, the global file;- the working tree’s
.gitattributes, root first and each directory after it, so the file closest to the path wins; $GIT_DIR/info/attributes, which outranks everything.
Macros ([attr]name …) are honoured only where git honours them: the root
file, the global file and info/attributes. A [attr] line in a
subdirectory is not a macro definition to git and is not one here.
A source that cannot be read is skipped, exactly as git skips it.
Discovery is lazy since 2026-08-07; assembly is not. The tree’s
.gitattributes are probed only in the directories on the ancestor chain of
each path Self::resolve is asked about — git reads the file from nowhere
else, so everything beyond the ancestors was inert for the answer and cost a
read_dir per directory and a file_type() per entry. Measured on a tree
with 5281 directories and 480 000 ignored files (a build directory): git add of one declared file took 220 ms instead of 10 ms, scaling linearly
with the number of directory entries — the only measured cost in the product
that grew with untracked files. When a probe finds a file the whole
gix_attributes::Search is rebuilt from scratch by Self::assemble,
the same code that built it at construction, so precedence is decided by one
sort in one place and lazy discovery cannot reorder anything: patterns from
<dir>/.gitattributes reach only paths under <dir>, which makes the order
of discovery across disjoint branches irrelevant, and the order within a
chain is re-sorted on every rebuild. Rebuilds are bounded by the number of
attribute files on the queried chains — typically zero to two — never by the
number of directories.
Implementations§
Source§impl AttributeResolver
impl AttributeResolver
Sourcepub fn new(
work_tree: &Path,
common_dir: &Path,
global: Option<&Path>,
ignore_case: bool,
staged: Vec<StagedAttributes>,
) -> Self
pub fn new( work_tree: &Path, common_dir: &Path, global: Option<&Path>, ignore_case: bool, staged: Vec<StagedAttributes>, ) -> Self
Loads every attribute source git would consult under work_tree.
global is core.attributesFile; ignore_case is core.ignorecase,
which git applies to attribute matching as well as to path lookup.
common_dir, not this checkout’s git directory: info/ is on git’s
common list, so every linked worktree reads the same
info/attributes. Measured on git 2.55 with one linked worktree and
secrets/** -filter in the main .git/info/attributes: git check-attr filter answers unset in both checkouts, and a file dropped in
.git/worktrees/side/info/attributes is ignored in both. Reading the
worktree’s own directory therefore got it wrong twice over — it missed
the source that decides and would have read one git never consults.
staged is what staged_fallbacks found: the .gitattributes files
git reads from the index because their working-tree file is gone.
They take part in the ordinary precedence, by the directory they would
occupy — git treats the fallback copy exactly as it would treat the
file.
Sourcepub fn resolve(&mut self, relative_path: &[u8]) -> Resolution
pub fn resolve(&mut self, relative_path: &[u8]) -> Resolution
What git resolves for a repository-relative path, on both axes.