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
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
//! What:
//! A private copy of the real index,
//! in its own directory beside it,
//! removed when
//! the copy is dropped.
//! Why:
//! Predicting what `git add` would stage means running it against an index nobody
//! else reads,
//! so the real index never changes before the policies have answered.
//! The copy keeps the real index's access and modification times:
//! Git re-reads an
//! entry whose stat data is not older than the index file,
//! and a copy with a fresh
//! time would make it trust stat data from the same second as a later edit of the
//! same size,
//! so the prediction could miss that edit (the installed wrapper's
//! `index-file-timestamps.ts`,
//! issue #544).
//!
//! In TS you'd write (pseudocode):
//! ```ts
//! // await using index = await createPrivateIndex(realIndexPath); // removed on dispose
//! ```
/// Import the layer's failure type and its closed list of causes.
use crate;
/// `Path`/`PathBuf` are borrowed/owned filesystem paths of raw OS bytes.
use ;
/// What:
/// `AtomicU64` is a counter that several threads may increase without a lock;
/// `Ordering::Relaxed` asks only that each increase is counted once.
/// Why:
/// Two private indexes made by one process in the same nanosecond still get
/// different directory names.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// let created = 0n;
/// ```
use ;
/// The name prefix of every private index directory;
/// the installed wrapper uses the same one.
pub const PRIVATE_INDEX_PREFIX: &str = "cli-git-add-policy-";
/// How many unique names this process has made,
/// for private index directories and sibling files.
static CREATED: AtomicU64 = new;
/// What:
/// A name part no other file of this process or another one chose:
/// the process ID,
/// the time in nanoseconds and this process's count,
/// joined by `-`.
/// Why:
/// Private index directories and the direct fix's sibling files are created beside
/// files other processes use,
/// so their names must not collide.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// const uniqueSuffix = () => `${process.pid}-${nowNanoseconds()}-${created++}`;
/// ```
/// What:
/// A private index file in a directory this value owns.
/// Fields are private,
/// so the
/// directory can be removed only by dropping the value.
/// Why:
/// Every path out of a prediction,
/// failures included,
/// removes the directory.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// class PrivateIndex { readonly #directory: string; readonly #index: string; [Symbol.dispose]() {} }
/// ```
/// What:
/// Build the failure of a private index that could not be prepared.
/// `&std::io::Error` borrows the operating system's reason.
/// Why:
/// The message names the step and the reason;
/// the directory is under the Git
/// directory,
/// never a candidate pathname.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// function privateIndexFailure(step: string, error: Error): CandidateError;
/// ```
/// What:
/// Create a directory only this user can read,
/// failing when it already exists.
/// Why:
/// The copy holds the repository's staged entries;
/// a fresh directory also proves
/// no other process chose the same name.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// await mkdir(directory, { mode: 0o700 }); // throws EEXIST
/// ```
/// What:
/// Copy the real index into `destination` and give the copy the real index's times.
/// `Ok(false)` means the real index does not exist,
/// which Git reads as an empty index.
/// Why:
/// A missing index needs no copy:
/// Git treats a missing index file as empty,
/// so the
/// private index path can simply name a file that does not exist yet.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// async function copyIndex(source: string, destination: string): Promise<boolean>;
/// ```
/// What:
/// `impl PrivateIndex { ... }` attaches the constructor and the accessor.
/// Why:
/// The directory is created and filled in one step,
/// so no caller sees a half-made copy.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// class PrivateIndex { static create(realIndex: string): PrivateIndex; get path(): string }
/// ```
/// What:
/// `impl Drop for PrivateIndex` runs `drop` when the value goes out of scope,
/// like
/// a `[Symbol.dispose]` that the language calls on every exit path.
/// Why:
/// The directory and everything Git wrote into it are removed whether the
/// prediction succeeded or failed.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// [Symbol.dispose]() { rmSync(this.#directory, { recursive: true, force: true }); }
/// ```
/// Copy,
/// timestamp and removal controls stay out of the release executable.