1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
//! Repository discovery and target-tree file enumeration.
use HashMap;
use DefaultHasher;
use ;
use ControlFlow;
use ;
use ;
use crateError;
use crateFileTypeScope;
/// A discovered repository plus the facts the walk needs up front.
pub
/// Discover and open the repository containing `root`.
///
/// # Errors
///
/// Returns [`Error::NotARepository`] when no repository encloses
/// `root`, or [`Error::OpenRepository`] for any other open failure
/// (corrupt repo, permission denied, …).
pub
/// A stable 64-bit digest of the repository's **effective mailmap** — the
/// merged snapshot [`gix::Repository::open_mailmap`] produces, which is
/// exactly what the walk resolves author identities against
/// ([`history::collect_events`](super::history::collect_events)).
///
/// Author identities are canonicalised at *walk* time and stored in the
/// cache as digests, so a mailmap edit changes the recorded events without
/// moving `HEAD` or any field of [`Options`](crate::vcs::Options). Feeding
/// this value into [`cache::fingerprint`](crate::vcs::cache::fingerprint)
/// is what stops a stale event log being replayed — or, worse, spliced
/// into and re-persisted — under a mailmap it was not walked with
/// (issue #1262).
///
/// # Why the parsed snapshot rather than the source bytes
///
/// `open_mailmap` merges up to four sources whose selection is
/// conditional: the working-tree `.mailmap` (uncommitted edits included),
/// `HEAD:.mailmap` for a bare repo *when `mailmap.blob` is unset*, the
/// `mailmap.blob` object, and the `mailmap.file` path. Re-deriving that
/// list here would be a coverage claim nothing checks — a source missed
/// (or one added by a future gix release, the dependency being
/// caret-ranged) silently reproduces this bug for that source. Digesting
/// the merged snapshot instead delegates the source list to gix, so it
/// cannot drift.
///
/// `Snapshot::iter` is documented as ordered by `(old_email, old_name)`,
/// so the digest is deterministic across processes, and `DefaultHasher` is
/// created with fixed keys for the same cross-process stability
/// [`cache::fingerprint`](crate::vcs::cache::fingerprint) relies on. That
/// documented order is why this hashes `entries()` rather than the
/// `Snapshot` itself, which also derives `Hash`: the derive would rest on
/// gix's private field layout, where `iter`'s ordering is part of its
/// contract. The `Vec` is a mailmap's worth of borrowed slices.
///
/// Digesting the parsed entries also means a comment- or whitespace-only
/// `.mailmap` edit does not cost a needless cold walk: the resolution it
/// feeds is unchanged, so replaying is correct.
///
/// # Residual window
///
/// The walk re-opens the mailmap for itself, and nothing makes the two
/// reads agree. An edit can land between them, and so can a transient
/// that changes nothing on disk: `open_mailmap` turns a failed or
/// mid-rewrite read into an empty or partial snapshot rather than an
/// error — a truncate- or rename-then-write save of unchanged content,
/// or a swallowed I/O error. The entry is then stamped with this call's
/// view over events resolved under the walk's.
///
/// Which read saw the content that lasts decides whether it heals. When
/// the walk did, the next run at the same `HEAD` fingerprints
/// differently and overwrites the entry. When this call did — every
/// transient, and an edit later reverted — later runs reproduce the
/// stamp: the pure hit serves the entry, and the incremental splice
/// carries its events forward until they leave the long window, a
/// mailmap or option change moves the fingerprint, or `--clear-cache`
/// runs. Closing the window means threading one snapshot through the
/// walk — see issue #1409.
pub
/// Discover the working-tree root of the repository containing `path`.
///
/// Returns the canonicalised work-tree directory (the same value
/// [`OpenRepo::workdir`] carries), or `None` when `path` is not inside a
/// repository or the repository is bare (no work tree). Backs the public
/// [`crate::vcs::workdir_root`] helper.
///
/// `gix::discover` walks upward from a *directory*; a `path` that names a
/// file (or does not exist) is discovered from its parent directory so the
/// helper accepts either a file or a directory.
pub
/// Resolve a revision spelling to the commit it names, peeling through
/// any tag. Shared by the file-level walk ([`super::build`]) and the
/// single-commit JIT path ([`super::jit`]) so both map a resolution
/// failure to the same [`Error::ResolveRef`].
///
/// # Errors
///
/// Returns [`Error::ResolveRef`] when `reference` does not resolve to a
/// commit (unknown ref, unborn `HEAD`, …).
pub
/// Map a discovery failure to a typed error, distinguishing "no
/// repository here" (a clean user-facing condition) from genuine open
/// failures.
///
/// `gix` 0.88 erased its error types, but the upward search still raises
/// its own typed error inside the tree, so a downcast keeps the exact
/// variant set rather than widening to every `NotFound`-classified cause
/// (a missing `HEAD` inside a found `.git` is a corrupt repository, not
/// "no repository here").
/// Enumerate the in-scope tracked text files at `target_tree`, mapping
/// each repository-relative path to its line count (SLOC).
///
/// Binary blobs (line counts unavailable) and symlinks are skipped, as
/// the issue specifies. `scope` is an additional extension-only filter
/// (issue #576): an out-of-scope file is dropped *before* its (costlier)
/// line-count diff runs, so the history walk never seeds an accumulator
/// for it. Implemented as a diff of the empty tree against the target
/// tree: every in-scope file then appears as an `Addition` whose "added"
/// line count is the file's total line count.
///
/// # Errors
///
/// Returns [`Error::Diff`] if the diff machinery fails, or if a path is
/// not valid UTF-8 on a platform that requires it.
pub
/// Convert a git path (raw bytes) to a `PathBuf`, erroring rather than
/// lossily mangling a non-UTF-8 path that is used as a map key
/// (per the path rules in AGENTS.md).
pub