##// END OF EJS Templates
dirstate-v2: Replace the 32-bit `mode` field with two bits...
Simon Sapin -
r49009:4d5a1325 default
parent child Browse files
Show More
@@ -1,484 +1,490
1 1 The *dirstate* is what Mercurial uses internally to track
2 2 the state of files in the working directory,
3 3 such as set by commands like `hg add` and `hg rm`.
4 4 It also contains some cached data that help make `hg status` faster.
5 5 The name refers both to `.hg/dirstate` on the filesystem
6 6 and the corresponding data structure in memory while a Mercurial process
7 7 is running.
8 8
9 9 The original file format, retroactively dubbed `dirstate-v1`,
10 10 is described at https://www.mercurial-scm.org/wiki/DirState.
11 11 It is made of a flat sequence of unordered variable-size entries,
12 12 so accessing any information in it requires parsing all of it.
13 13 Similarly, saving changes requires rewriting the entire file.
14 14
15 15 The newer `dirsate-v2` file format is designed to fix these limitations
16 16 and make `hg status` faster.
17 17
18 18 User guide
19 19 ==========
20 20
21 21 Compatibility
22 22 -------------
23 23
24 24 The file format is experimental and may still change.
25 25 Different versions of Mercurial may not be compatible with each other
26 26 when working on a local repository that uses this format.
27 27 When using an incompatible version with the experimental format,
28 28 anything can happen including data corruption.
29 29
30 30 Since the dirstate is entirely local and not relevant to the wire protocol,
31 31 `dirstate-v2` does not affect compatibility with remote Mercurial versions.
32 32
33 33 When `share-safe` is enabled, different repositories sharing the same store
34 34 can use different dirstate formats.
35 35
36 36 Enabling `dirsate-v2` for new local repositories
37 37 ------------------------------------------------
38 38
39 39 When creating a new local repository such as with `hg init` or `hg clone`,
40 40 the `exp-dirstate-v2` boolean in the `format` configuration section
41 41 controls whether to use this file format.
42 42 This is disabled by default as of this writing.
43 43 To enable it for a single repository, run for example::
44 44
45 45 $ hg init my-project --config format.exp-dirstate-v2=1
46 46
47 47 Checking the format of an existing local repsitory
48 48 --------------------------------------------------
49 49
50 50 The `debugformat` commands prints information about
51 51 which of multiple optional formats are used in the current repository,
52 52 including `dirstate-v2`::
53 53
54 54 $ hg debugformat
55 55 format-variant repo
56 56 fncache: yes
57 57 dirstate-v2: yes
58 58 […]
59 59
60 60 Upgrading or downgrading an existing local repository
61 61 -----------------------------------------------------
62 62
63 63 The `debugupgrade` command does various upgrades or downgrades
64 64 on a local repository
65 65 based on the current Mercurial version and on configuration.
66 66 The same `format.exp-dirstate-v2` configuration is used again.
67 67
68 68 Example to upgrade::
69 69
70 70 $ hg debugupgrade --config format.exp-dirstate-v2=1
71 71
72 72 Example to downgrade to `dirstate-v1`::
73 73
74 74 $ hg debugupgrade --config format.exp-dirstate-v2=0
75 75
76 76 Both of this commands do nothing but print a list of proposed changes,
77 77 which may include changes unrelated to the dirstate.
78 78 Those other changes are controlled by their own configuration keys.
79 79 Add `--run` to a command to actually apply the proposed changes.
80 80
81 81 Backups of `.hg/requires` and `.hg/dirstate` are created
82 82 in a `.hg/upgradebackup.*` directory.
83 83 If something goes wrong, restoring those files should undo the change.
84 84
85 85 Note that upgrading affects compatibility with older versions of Mercurial
86 86 as noted above.
87 87 This can be relevant when a repository’s files are on a USB drive
88 88 or some other removable media, or shared over the network, etc.
89 89
90 90 Internal filesystem representation
91 91 ==================================
92 92
93 93 Requirements file
94 94 -----------------
95 95
96 96 The `.hg/requires` file indicates which of various optional file formats
97 97 are used by a given repository.
98 98 Mercurial aborts when seeing a requirement it does not know about,
99 99 which avoids older version accidentally messing up a respository
100 100 that uses a format that was introduced later.
101 101 For versions that do support a format, the presence or absence of
102 102 the corresponding requirement indicates whether to use that format.
103 103
104 104 When the file contains a `exp-dirstate-v2` line,
105 105 the `dirstate-v2` format is used.
106 106 With no such line `dirstate-v1` is used.
107 107
108 108 High level description
109 109 ----------------------
110 110
111 111 Whereas `dirstate-v1` uses a single `.hg/disrtate` file,
112 112 in `dirstate-v2` that file is a "docket" file
113 113 that only contains some metadata
114 114 and points to separate data file named `.hg/dirstate.{ID}`,
115 115 where `{ID}` is a random identifier.
116 116
117 117 This separation allows making data files append-only
118 118 and therefore safer to memory-map.
119 119 Creating a new data file (occasionally to clean up unused data)
120 120 can be done with a different ID
121 121 without disrupting another Mercurial process
122 122 that could still be using the previous data file.
123 123
124 124 Both files have a format designed to reduce the need for parsing,
125 125 by using fixed-size binary components as much as possible.
126 126 For data that is not fixed-size,
127 127 references to other parts of a file can be made by storing "pseudo-pointers":
128 128 integers counted in bytes from the start of a file.
129 129 For read-only access no data structure is needed,
130 130 only a bytes buffer (possibly memory-mapped directly from the filesystem)
131 131 with specific parts read on demand.
132 132
133 133 The data file contains "nodes" organized in a tree.
134 134 Each node represents a file or directory inside the working directory
135 135 or its parent changeset.
136 136 This tree has the same structure as the filesystem,
137 137 so a node representing a directory has child nodes representing
138 138 the files and subdirectories contained directly in that directory.
139 139
140 140 The docket file format
141 141 ----------------------
142 142
143 143 This is implemented in `rust/hg-core/src/dirstate_tree/on_disk.rs`
144 144 and `mercurial/dirstateutils/docket.py`.
145 145
146 146 Components of the docket file are found at fixed offsets,
147 147 counted in bytes from the start of the file:
148 148
149 149 * Offset 0:
150 150 The 12-bytes marker string "dirstate-v2\n" ending with a newline character.
151 151 This makes it easier to tell a dirstate-v2 file from a dirstate-v1 file,
152 152 although it is not strictly necessary
153 153 since `.hg/requires` determines which format to use.
154 154
155 155 * Offset 12:
156 156 The changeset node ID on the first parent of the working directory,
157 157 as up to 32 binary bytes.
158 158 If a node ID is shorter (20 bytes for SHA-1),
159 159 it is start-aligned and the rest of the bytes are set to zero.
160 160
161 161 * Offset 44:
162 162 The changeset node ID on the second parent of the working directory,
163 163 or all zeros if there isn’t one.
164 164 Also 32 binary bytes.
165 165
166 166 * Offset 76:
167 167 Tree metadata on 44 bytes, described below.
168 168 Its separation in this documentation from the rest of the docket
169 169 reflects a detail of the current implementation.
170 170 Since tree metadata is also made of fields at fixed offsets, those could
171 171 be inlined here by adding 76 bytes to each offset.
172 172
173 173 * Offset 120:
174 174 The used size of the data file, as a 32-bit big-endian integer.
175 175 The actual size of the data file may be larger
176 176 (if another Mercurial processis in appending to it
177 177 but has not updated the docket yet).
178 178 That extra data must be ignored.
179 179
180 180 * Offset 124:
181 181 The length of the data file identifier, as a 8-bit integer.
182 182
183 183 * Offset 125:
184 184 The data file identifier.
185 185
186 186 * Any additional data is current ignored, and dropped when updating the file.
187 187
188 188 Tree metadata in the docket file
189 189 --------------------------------
190 190
191 191 Tree metadata is similarly made of components at fixed offsets.
192 192 These offsets are counted in bytes from the start of tree metadata,
193 193 which is 76 bytes after the start of the docket file.
194 194
195 195 This metadata can be thought of as the singular root of the tree
196 196 formed by nodes in the data file.
197 197
198 198 * Offset 0:
199 199 Pseudo-pointer to the start of root nodes,
200 200 counted in bytes from the start of the data file,
201 201 as a 32-bit big-endian integer.
202 202 These nodes describe files and directories found directly
203 203 at the root of the working directory.
204 204
205 205 * Offset 4:
206 206 Number of root nodes, as a 32-bit big-endian integer.
207 207
208 208 * Offset 8:
209 209 Total number of nodes in the entire tree that "have a dirstate entry",
210 210 as a 32-bit big-endian integer.
211 211 Those nodes represent files that would be present at all in `dirstate-v1`.
212 212 This is typically less than the total number of nodes.
213 213 This counter is used to implement `len(dirstatemap)`.
214 214
215 215 * Offset 12:
216 216 Number of nodes in the entire tree that have a copy source,
217 217 as a 32-bit big-endian integer.
218 218 At the next commit, these files are recorded
219 219 as having been copied or moved/renamed from that source.
220 220 (A move is recorded as a copy and separate removal of the source.)
221 221 This counter is used to implement `len(dirstatemap.copymap)`.
222 222
223 223 * Offset 16:
224 224 An estimation of how many bytes of the data file
225 225 (within its used size) are unused, as a 32-bit big-endian integer.
226 226 When appending to an existing data file,
227 227 some existing nodes or paths can be unreachable from the new root
228 228 but they still take up space.
229 229 This counter is used to decide when to write a new data file from scratch
230 230 instead of appending to an existing one,
231 231 in order to get rid of that unreachable data
232 232 and avoid unbounded file size growth.
233 233
234 234 * Offset 20:
235 235 These four bytes are currently ignored
236 236 and reset to zero when updating a docket file.
237 237 This is an attempt at forward compatibility:
238 238 future Mercurial versions could use this as a bit field
239 239 to indicate that a dirstate has additional data or constraints.
240 240 Finding a dirstate file with the relevant bit unset indicates that
241 241 it was written by a then-older version
242 242 which is not aware of that future change.
243 243
244 244 * Offset 24:
245 245 Either 20 zero bytes, or a SHA-1 hash as 20 binary bytes.
246 246 When present, the hash is of ignore patterns
247 247 that were used for some previous run of the `status` algorithm.
248 248
249 249 * (Offset 44: end of tree metadata)
250 250
251 251 Optional hash of ignore patterns
252 252 --------------------------------
253 253
254 254 The implementation of `status` at `rust/hg-core/src/dirstate_tree/status.rs`
255 255 has been optimized such that its run time is dominated by calls
256 256 to `stat` for reading the filesystem metadata of a file or directory,
257 257 and to `readdir` for listing the contents of a directory.
258 258 In some cases the algorithm can skip calls to `readdir`
259 259 (saving significant time)
260 260 because the dirstate already contains enough of the relevant information
261 261 to build the correct `status` results.
262 262
263 263 The default configuration of `hg status` is to list unknown files
264 264 but not ignored files.
265 265 In this case, it matters for the `readdir`-skipping optimization
266 266 if a given file used to be ignored but became unknown
267 267 because `.hgignore` changed.
268 268 To detect the possibility of such a change,
269 269 the tree metadata contains an optional hash of all ignore patterns.
270 270
271 271 We define:
272 272
273 273 * "Root" ignore files as:
274 274
275 275 - `.hgignore` at the root of the repository if it exists
276 276 - And all files from `ui.ignore.*` config.
277 277
278 278 This set of files is sorted by the string representation of their path.
279 279
280 280 * The "expanded contents" of an ignore files is the byte string made
281 281 by the concatenation of its contents followed by the "expanded contents"
282 282 of other files included with `include:` or `subinclude:` directives,
283 283 in inclusion order. This definition is recursive, as included files can
284 284 themselves include more files.
285 285
286 286 This hash is defined as the SHA-1 of the concatenation (in sorted
287 287 order) of the "expanded contents" of each "root" ignore file.
288 288 (Note that computing this does not require actually concatenating
289 289 into a single contiguous byte sequence.
290 290 Instead a SHA-1 hasher object can be created
291 291 and fed separate chunks one by one.)
292 292
293 293 The data file format
294 294 --------------------
295 295
296 296 This is implemented in `rust/hg-core/src/dirstate_tree/on_disk.rs`
297 297 and `mercurial/dirstateutils/v2.py`.
298 298
299 299 The data file contains two types of data: paths and nodes.
300 300
301 301 Paths and nodes can be organized in any order in the file, except that sibling
302 302 nodes must be next to each other and sorted by their path.
303 303 Contiguity lets the parent refer to them all
304 304 by their count and a single pseudo-pointer,
305 305 instead of storing one pseudo-pointer per child node.
306 306 Sorting allows using binary seach to find a child node with a given name
307 307 in `O(log(n))` byte sequence comparisons.
308 308
309 309 The current implemention writes paths and child node before a given node
310 310 for ease of figuring out the value of pseudo-pointers by the time the are to be
311 311 written, but this is not an obligation and readers must not rely on it.
312 312
313 313 A path is stored as a byte string anywhere in the file, without delimiter.
314 314 It is refered to by one or more node by a pseudo-pointer to its start, and its
315 315 length in bytes. Since there is no delimiter,
316 316 when a path is a substring of another the same bytes could be reused,
317 317 although the implementation does not exploit this as of this writing.
318 318
319 319 A node is stored on 43 bytes with components at fixed offsets. Paths and
320 320 child nodes relevant to a node are stored externally and referenced though
321 321 pseudo-pointers.
322 322
323 323 All integers are stored in big-endian. All pseudo-pointers are 32-bit integers
324 324 counting bytes from the start of the data file. Path lengths and positions
325 325 are 16-bit integers, also counted in bytes.
326 326
327 327 Node components are:
328 328
329 329 * Offset 0:
330 330 Pseudo-pointer to the full path of this node,
331 331 from the working directory root.
332 332
333 333 * Offset 4:
334 334 Length of the full path.
335 335
336 336 * Offset 6:
337 337 Position of the last `/` path separator within the full path,
338 338 in bytes from the start of the full path,
339 339 or zero if there isn’t one.
340 340 The part of the full path after this position is the "base name".
341 341 Since sibling nodes have the same parent, only their base name vary
342 342 and needs to be considered when doing binary search to find a given path.
343 343
344 344 * Offset 8:
345 345 Pseudo-pointer to the "copy source" path for this node,
346 346 or zero if there is no copy source.
347 347
348 348 * Offset 12:
349 349 Length of the copy source path, or zero if there isn’t one.
350 350
351 351 * Offset 14:
352 352 Pseudo-pointer to the start of child nodes.
353 353
354 354 * Offset 18:
355 355 Number of child nodes, as a 32-bit integer.
356 356 They occupy 43 times this number of bytes
357 357 (not counting space for paths, and further descendants).
358 358
359 359 * Offset 22:
360 360 Number as a 32-bit integer of descendant nodes in this subtree,
361 361 not including this node itself,
362 362 that "have a dirstate entry".
363 363 Those nodes represent files that would be present at all in `dirstate-v1`.
364 364 This is typically less than the total number of descendants.
365 365 This counter is used to implement `has_dir`.
366 366
367 367 * Offset 26:
368 368 Number as a 32-bit integer of descendant nodes in this subtree,
369 369 not including this node itself,
370 370 that represent files tracked in the working directory.
371 371 (For example, `hg rm` makes a file untracked.)
372 372 This counter is used to implement `has_tracked_dir`.
373 373
374 374 * Offset 30:
375 375 Some boolean values packed as bits of a single byte.
376 376 Starting from least-significant, bit masks are::
377 377
378 378 WDIR_TRACKED = 1 << 0
379 379 P1_TRACKED = 1 << 1
380 380 P2_INFO = 1 << 2
381 381 HAS_MODE_AND_SIZE = 1 << 3
382 382 HAS_MTIME = 1 << 4
383 MODE_EXEC_PERM = 1 << 5
384 MODE_IS_SYMLINK = 1 << 7
385
383 386
384 387 Other bits are unset. The meaning of these bits are:
385 388
386 389 `WDIR_TRACKED`
387 390 Set if the working directory contains a tracked file at this node’s path.
388 391 This is typically set and unset by `hg add` and `hg rm`.
389 392
390 393 `P1_TRACKED`
391 394 set if the working directory’s first parent changeset
392 395 (whose node identifier is found in tree metadata)
393 396 contains a tracked file at this node’s path.
394 397 This is a cache to reduce manifest lookups.
395 398
396 399 `P2_INFO`
397 400 Set if the file has been involved in some merge operation.
398 401 Either because it was actually merged,
399 402 or because the version in the second parent p2 version was ahead,
400 403 or because some rename moved it there.
401 404 In either case `hg status` will want it displayed as modified.
402 405
403 406 Files that would be mentioned at all in the `dirstate-v1` file format
404 407 have a node with at least one of the above three bits set in `dirstate-v2`.
405 408 Let’s call these files "tracked anywhere",
406 409 and "untracked" the nodes with all three of these bits unset.
407 410 Untracked nodes are typically for directories:
408 411 they hold child nodes and form the tree structure.
409 412 Additional untracked nodes may also exist.
410 413 Although implementations should strive to clean up nodes
411 414 that are entirely unused, other untracked nodes may also exist.
412 415 For example, a future version of Mercurial might in some cases
413 416 add nodes for untracked files or/and ignored files in the working directory
414 417 in order to optimize `hg status`
415 418 by enabling it to skip `readdir` in more cases.
416 419
417 When a node is for a file tracked anywhere,
418 the rest of the node data is three fields:
420 When a node is for a file tracked anywhere:
421 - If `HAS_MODE_AND_SIZE` is set, the file is expected
422 to be a symbolic link or a normal file based on `MODE_IS_SYMLINK`.
423 - If `HAS_MODE_AND_SIZE` is set, the file’s owner is expected
424 to have execute permission or not based on `MODE_EXEC_PERM`.
425 - If `HAS_MODE_AND_SIZE` is unset,
426 the expected type of file and permission are unknown.
427 The rest of the node data is three fields:
419 428
420 429 * Offset 31:
421 If `HAS_MODE_AND_SIZE` is unset, four zero bytes.
422 Otherwise, a 32-bit integer for the Unix mode (as in `stat_result.st_mode`)
423 expected for this file to be considered clean.
424 Only the `S_IXUSR` bit (owner has execute permission) is considered.
430 4 unused bytes, set to zero
425 431
426 432 * Offset 35:
427 433 If `HAS_MODE_AND_SIZE` is unset, four zero bytes.
428 434 Otherwise, a 32-bit integer for expected size of the file
429 435 truncated to its 31 least-significant bits.
430 436 Unlike in dirstate-v1, negative values are not used.
431 437
432 438 * Offset 39:
433 439 If `HAS_MTIME` is unset, four zero bytes.
434 440 Otherwise, a 32-bit integer for expected modified time of the file
435 441 (as in `stat_result.st_mtime`),
436 442 truncated to its 31 least-significant bits.
437 443 Unlike in dirstate-v1, negative values are not used.
438 444
439 445 If an untracked node `HAS_MTIME` *unset*, this space is unused:
440 446
441 447 * Offset 31:
442 448 12 unused bytes, set to zero
443 449
444 450 If an untracked node `HAS_MTIME` *set*,
445 451 what follows is the modification time of a directory
446 452 represented similarly to the C `timespec` struct:
447 453
448 454 * Offset 31:
449 455 4 unused bytes, set to zero
450 456
451 457 * Offset 35:
452 458 The number of seconds elapsed since the Unix epoch,
453 459 truncated to its lower 31 bits,
454 460 as a 32-bit integer.
455 461
456 462 * Offset 39:
457 463 The sub-second number of nanoseconds elapsed since the Unix epoch,
458 464 as 32-bit integer.
459 465 Always greater than or equal to zero, and strictly less than a billion.
460 466
461 467 The presence of a directory modification time means that at some point,
462 468 this path in the working directory was observed:
463 469
464 470 - To be a directory
465 471 - With the given modification time
466 472 - That time was already strictly in the past when observed,
467 473 meaning that later changes cannot happen in the same clock tick
468 474 and must cause a different modification time
469 475 (unless the system clock jumps back and we get unlucky,
470 476 which is not impossible but deemed unlikely enough).
471 477 - All direct children of this directory
472 478 (as returned by `std::fs::read_dir`)
473 479 either have a corresponding dirstate node,
474 480 or are ignored by ignore patterns whose hash is in tree metadata.
475 481
476 482 This means that if `std::fs::symlink_metadata` later reports
477 483 the same modification time
478 484 and ignored patterns haven’t changed,
479 485 a run of status that is not listing ignored files
480 486 can skip calling `std::fs::read_dir` again for this directory,
481 487 and iterate child dirstate nodes instead.
482 488
483 489
484 490 * (Offset 43: end of this node)
@@ -1,1080 +1,1083
1 1 # This file is automatically @generated by Cargo.
2 2 # It is not intended for manual editing.
3 version = 3
4
3 5 [[package]]
4 6 name = "adler"
5 7 version = "0.2.3"
6 8 source = "registry+https://github.com/rust-lang/crates.io-index"
7 9 checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
8 10
9 11 [[package]]
10 12 name = "aho-corasick"
11 13 version = "0.7.15"
12 14 source = "registry+https://github.com/rust-lang/crates.io-index"
13 15 checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
14 16 dependencies = [
15 17 "memchr",
16 18 ]
17 19
18 20 [[package]]
19 21 name = "ansi_term"
20 22 version = "0.11.0"
21 23 source = "registry+https://github.com/rust-lang/crates.io-index"
22 24 checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
23 25 dependencies = [
24 26 "winapi",
25 27 ]
26 28
27 29 [[package]]
28 30 name = "atty"
29 31 version = "0.2.14"
30 32 source = "registry+https://github.com/rust-lang/crates.io-index"
31 33 checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
32 34 dependencies = [
33 35 "hermit-abi",
34 36 "libc",
35 37 "winapi",
36 38 ]
37 39
38 40 [[package]]
39 41 name = "autocfg"
40 42 version = "1.0.1"
41 43 source = "registry+https://github.com/rust-lang/crates.io-index"
42 44 checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
43 45
44 46 [[package]]
45 47 name = "bitflags"
46 48 version = "1.2.1"
47 49 source = "registry+https://github.com/rust-lang/crates.io-index"
48 50 checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
49 51
50 52 [[package]]
51 53 name = "bitmaps"
52 54 version = "2.1.0"
53 55 source = "registry+https://github.com/rust-lang/crates.io-index"
54 56 checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2"
55 57 dependencies = [
56 58 "typenum",
57 59 ]
58 60
59 61 [[package]]
60 62 name = "block-buffer"
61 63 version = "0.9.0"
62 64 source = "registry+https://github.com/rust-lang/crates.io-index"
63 65 checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
64 66 dependencies = [
65 67 "generic-array",
66 68 ]
67 69
68 70 [[package]]
69 71 name = "byteorder"
70 72 version = "1.3.4"
71 73 source = "registry+https://github.com/rust-lang/crates.io-index"
72 74 checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
73 75
74 76 [[package]]
75 77 name = "bytes-cast"
76 78 version = "0.2.0"
77 79 source = "registry+https://github.com/rust-lang/crates.io-index"
78 80 checksum = "0d434f9a4ecbe987e7ccfda7274b6f82ea52c9b63742565a65cb5e8ba0f2c452"
79 81 dependencies = [
80 82 "bytes-cast-derive",
81 83 ]
82 84
83 85 [[package]]
84 86 name = "bytes-cast-derive"
85 87 version = "0.1.0"
86 88 source = "registry+https://github.com/rust-lang/crates.io-index"
87 89 checksum = "cb936af9de38476664d6b58e529aff30d482e4ce1c5e150293d00730b0d81fdb"
88 90 dependencies = [
89 91 "proc-macro2",
90 92 "quote",
91 93 "syn",
92 94 ]
93 95
94 96 [[package]]
95 97 name = "cc"
96 98 version = "1.0.66"
97 99 source = "registry+https://github.com/rust-lang/crates.io-index"
98 100 checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
99 101 dependencies = [
100 102 "jobserver",
101 103 ]
102 104
103 105 [[package]]
104 106 name = "cfg-if"
105 107 version = "0.1.10"
106 108 source = "registry+https://github.com/rust-lang/crates.io-index"
107 109 checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
108 110
109 111 [[package]]
110 112 name = "cfg-if"
111 113 version = "1.0.0"
112 114 source = "registry+https://github.com/rust-lang/crates.io-index"
113 115 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
114 116
115 117 [[package]]
116 118 name = "chrono"
117 119 version = "0.4.19"
118 120 source = "registry+https://github.com/rust-lang/crates.io-index"
119 121 checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
120 122 dependencies = [
121 123 "libc",
122 124 "num-integer",
123 125 "num-traits",
124 126 "time",
125 127 "winapi",
126 128 ]
127 129
128 130 [[package]]
129 131 name = "clap"
130 132 version = "2.33.3"
131 133 source = "registry+https://github.com/rust-lang/crates.io-index"
132 134 checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
133 135 dependencies = [
134 136 "ansi_term",
135 137 "atty",
136 138 "bitflags",
137 139 "strsim",
138 140 "textwrap",
139 141 "unicode-width",
140 142 "vec_map",
141 143 ]
142 144
143 145 [[package]]
144 146 name = "const_fn"
145 147 version = "0.4.4"
146 148 source = "registry+https://github.com/rust-lang/crates.io-index"
147 149 checksum = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826"
148 150
149 151 [[package]]
150 152 name = "cpufeatures"
151 153 version = "0.1.4"
152 154 source = "registry+https://github.com/rust-lang/crates.io-index"
153 155 checksum = "ed00c67cb5d0a7d64a44f6ad2668db7e7530311dd53ea79bcd4fb022c64911c8"
154 156 dependencies = [
155 157 "libc",
156 158 ]
157 159
158 160 [[package]]
159 161 name = "cpython"
160 162 version = "0.6.0"
161 163 source = "registry+https://github.com/rust-lang/crates.io-index"
162 164 checksum = "8094679a4e9bfc8035572162624bc800eda35b5f9eff2537b9cd9aacc3d9782e"
163 165 dependencies = [
164 166 "libc",
165 167 "num-traits",
166 168 "paste",
167 169 "python27-sys",
168 170 "python3-sys",
169 171 ]
170 172
171 173 [[package]]
172 174 name = "crc32fast"
173 175 version = "1.2.1"
174 176 source = "registry+https://github.com/rust-lang/crates.io-index"
175 177 checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
176 178 dependencies = [
177 179 "cfg-if 1.0.0",
178 180 ]
179 181
180 182 [[package]]
181 183 name = "crossbeam-channel"
182 184 version = "0.4.4"
183 185 source = "registry+https://github.com/rust-lang/crates.io-index"
184 186 checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87"
185 187 dependencies = [
186 188 "crossbeam-utils 0.7.2",
187 189 "maybe-uninit",
188 190 ]
189 191
190 192 [[package]]
191 193 name = "crossbeam-channel"
192 194 version = "0.5.0"
193 195 source = "registry+https://github.com/rust-lang/crates.io-index"
194 196 checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
195 197 dependencies = [
196 198 "cfg-if 1.0.0",
197 199 "crossbeam-utils 0.8.1",
198 200 ]
199 201
200 202 [[package]]
201 203 name = "crossbeam-deque"
202 204 version = "0.8.0"
203 205 source = "registry+https://github.com/rust-lang/crates.io-index"
204 206 checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9"
205 207 dependencies = [
206 208 "cfg-if 1.0.0",
207 209 "crossbeam-epoch",
208 210 "crossbeam-utils 0.8.1",
209 211 ]
210 212
211 213 [[package]]
212 214 name = "crossbeam-epoch"
213 215 version = "0.9.1"
214 216 source = "registry+https://github.com/rust-lang/crates.io-index"
215 217 checksum = "a1aaa739f95311c2c7887a76863f500026092fb1dce0161dab577e559ef3569d"
216 218 dependencies = [
217 219 "cfg-if 1.0.0",
218 220 "const_fn",
219 221 "crossbeam-utils 0.8.1",
220 222 "lazy_static",
221 223 "memoffset",
222 224 "scopeguard",
223 225 ]
224 226
225 227 [[package]]
226 228 name = "crossbeam-utils"
227 229 version = "0.7.2"
228 230 source = "registry+https://github.com/rust-lang/crates.io-index"
229 231 checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
230 232 dependencies = [
231 233 "autocfg",
232 234 "cfg-if 0.1.10",
233 235 "lazy_static",
234 236 ]
235 237
236 238 [[package]]
237 239 name = "crossbeam-utils"
238 240 version = "0.8.1"
239 241 source = "registry+https://github.com/rust-lang/crates.io-index"
240 242 checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d"
241 243 dependencies = [
242 244 "autocfg",
243 245 "cfg-if 1.0.0",
244 246 "lazy_static",
245 247 ]
246 248
247 249 [[package]]
248 250 name = "ctor"
249 251 version = "0.1.16"
250 252 source = "registry+https://github.com/rust-lang/crates.io-index"
251 253 checksum = "7fbaabec2c953050352311293be5c6aba8e141ba19d6811862b232d6fd020484"
252 254 dependencies = [
253 255 "quote",
254 256 "syn",
255 257 ]
256 258
257 259 [[package]]
258 260 name = "derive_more"
259 261 version = "0.99.11"
260 262 source = "registry+https://github.com/rust-lang/crates.io-index"
261 263 checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c"
262 264 dependencies = [
263 265 "proc-macro2",
264 266 "quote",
265 267 "syn",
266 268 ]
267 269
268 270 [[package]]
269 271 name = "difference"
270 272 version = "2.0.0"
271 273 source = "registry+https://github.com/rust-lang/crates.io-index"
272 274 checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
273 275
274 276 [[package]]
275 277 name = "digest"
276 278 version = "0.9.0"
277 279 source = "registry+https://github.com/rust-lang/crates.io-index"
278 280 checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
279 281 dependencies = [
280 282 "generic-array",
281 283 ]
282 284
283 285 [[package]]
284 286 name = "either"
285 287 version = "1.6.1"
286 288 source = "registry+https://github.com/rust-lang/crates.io-index"
287 289 checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
288 290
289 291 [[package]]
290 292 name = "env_logger"
291 293 version = "0.7.1"
292 294 source = "registry+https://github.com/rust-lang/crates.io-index"
293 295 checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
294 296 dependencies = [
295 297 "atty",
296 298 "humantime",
297 299 "log",
298 300 "regex",
299 301 "termcolor",
300 302 ]
301 303
302 304 [[package]]
303 305 name = "flate2"
304 306 version = "1.0.19"
305 307 source = "registry+https://github.com/rust-lang/crates.io-index"
306 308 checksum = "7411863d55df97a419aa64cb4d2f167103ea9d767e2c54a1868b7ac3f6b47129"
307 309 dependencies = [
308 310 "cfg-if 1.0.0",
309 311 "crc32fast",
310 312 "libc",
311 313 "libz-sys",
312 314 "miniz_oxide",
313 315 ]
314 316
315 317 [[package]]
316 318 name = "format-bytes"
317 319 version = "0.2.2"
318 320 source = "registry+https://github.com/rust-lang/crates.io-index"
319 321 checksum = "1c4e89040c7fd7b4e6ba2820ac705a45def8a0c098ec78d170ae88f1ef1d5762"
320 322 dependencies = [
321 323 "format-bytes-macros",
322 324 "proc-macro-hack",
323 325 ]
324 326
325 327 [[package]]
326 328 name = "format-bytes-macros"
327 329 version = "0.3.0"
328 330 source = "registry+https://github.com/rust-lang/crates.io-index"
329 331 checksum = "b05089e341a0460449e2210c3bf7b61597860b07f0deae58da38dbed0a4c6b6d"
330 332 dependencies = [
331 333 "proc-macro-hack",
332 334 "proc-macro2",
333 335 "quote",
334 336 "syn",
335 337 ]
336 338
337 339 [[package]]
338 340 name = "generic-array"
339 341 version = "0.14.4"
340 342 source = "registry+https://github.com/rust-lang/crates.io-index"
341 343 checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
342 344 dependencies = [
343 345 "typenum",
344 346 "version_check",
345 347 ]
346 348
347 349 [[package]]
348 350 name = "getrandom"
349 351 version = "0.1.15"
350 352 source = "registry+https://github.com/rust-lang/crates.io-index"
351 353 checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
352 354 dependencies = [
353 355 "cfg-if 0.1.10",
354 356 "libc",
355 357 "wasi 0.9.0+wasi-snapshot-preview1",
356 358 ]
357 359
358 360 [[package]]
359 361 name = "glob"
360 362 version = "0.3.0"
361 363 source = "registry+https://github.com/rust-lang/crates.io-index"
362 364 checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
363 365
364 366 [[package]]
365 367 name = "hermit-abi"
366 368 version = "0.1.17"
367 369 source = "registry+https://github.com/rust-lang/crates.io-index"
368 370 checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
369 371 dependencies = [
370 372 "libc",
371 373 ]
372 374
373 375 [[package]]
374 376 name = "hg-core"
375 377 version = "0.1.0"
376 378 dependencies = [
377 379 "bitflags",
378 380 "byteorder",
379 381 "bytes-cast",
380 382 "clap",
381 383 "crossbeam-channel 0.4.4",
382 384 "derive_more",
383 385 "flate2",
384 386 "format-bytes",
385 387 "home",
386 388 "im-rc",
387 389 "itertools",
388 390 "lazy_static",
391 "libc",
389 392 "log",
390 393 "memmap2",
391 394 "micro-timer",
392 395 "pretty_assertions",
393 396 "rand",
394 397 "rand_distr",
395 398 "rand_pcg",
396 399 "rayon",
397 400 "regex",
398 401 "same-file",
399 402 "sha-1",
400 403 "stable_deref_trait",
401 404 "tempfile",
402 405 "twox-hash",
403 406 "zstd",
404 407 ]
405 408
406 409 [[package]]
407 410 name = "hg-cpython"
408 411 version = "0.1.0"
409 412 dependencies = [
410 413 "cpython",
411 414 "crossbeam-channel 0.4.4",
412 415 "env_logger",
413 416 "hg-core",
414 417 "libc",
415 418 "log",
416 419 "stable_deref_trait",
417 420 ]
418 421
419 422 [[package]]
420 423 name = "home"
421 424 version = "0.5.3"
422 425 source = "registry+https://github.com/rust-lang/crates.io-index"
423 426 checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654"
424 427 dependencies = [
425 428 "winapi",
426 429 ]
427 430
428 431 [[package]]
429 432 name = "humantime"
430 433 version = "1.3.0"
431 434 source = "registry+https://github.com/rust-lang/crates.io-index"
432 435 checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
433 436 dependencies = [
434 437 "quick-error",
435 438 ]
436 439
437 440 [[package]]
438 441 name = "im-rc"
439 442 version = "15.0.0"
440 443 source = "registry+https://github.com/rust-lang/crates.io-index"
441 444 checksum = "3ca8957e71f04a205cb162508f9326aea04676c8dfd0711220190d6b83664f3f"
442 445 dependencies = [
443 446 "bitmaps",
444 447 "rand_core",
445 448 "rand_xoshiro",
446 449 "sized-chunks",
447 450 "typenum",
448 451 "version_check",
449 452 ]
450 453
451 454 [[package]]
452 455 name = "itertools"
453 456 version = "0.9.0"
454 457 source = "registry+https://github.com/rust-lang/crates.io-index"
455 458 checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
456 459 dependencies = [
457 460 "either",
458 461 ]
459 462
460 463 [[package]]
461 464 name = "jobserver"
462 465 version = "0.1.21"
463 466 source = "registry+https://github.com/rust-lang/crates.io-index"
464 467 checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2"
465 468 dependencies = [
466 469 "libc",
467 470 ]
468 471
469 472 [[package]]
470 473 name = "lazy_static"
471 474 version = "1.4.0"
472 475 source = "registry+https://github.com/rust-lang/crates.io-index"
473 476 checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
474 477
475 478 [[package]]
476 479 name = "libc"
477 480 version = "0.2.81"
478 481 source = "registry+https://github.com/rust-lang/crates.io-index"
479 482 checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb"
480 483
481 484 [[package]]
482 485 name = "libz-sys"
483 486 version = "1.1.2"
484 487 source = "registry+https://github.com/rust-lang/crates.io-index"
485 488 checksum = "602113192b08db8f38796c4e85c39e960c145965140e918018bcde1952429655"
486 489 dependencies = [
487 490 "cc",
488 491 "pkg-config",
489 492 "vcpkg",
490 493 ]
491 494
492 495 [[package]]
493 496 name = "log"
494 497 version = "0.4.11"
495 498 source = "registry+https://github.com/rust-lang/crates.io-index"
496 499 checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
497 500 dependencies = [
498 501 "cfg-if 0.1.10",
499 502 ]
500 503
501 504 [[package]]
502 505 name = "maybe-uninit"
503 506 version = "2.0.0"
504 507 source = "registry+https://github.com/rust-lang/crates.io-index"
505 508 checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
506 509
507 510 [[package]]
508 511 name = "memchr"
509 512 version = "2.3.4"
510 513 source = "registry+https://github.com/rust-lang/crates.io-index"
511 514 checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
512 515
513 516 [[package]]
514 517 name = "memmap2"
515 518 version = "0.4.0"
516 519 source = "registry+https://github.com/rust-lang/crates.io-index"
517 520 checksum = "de5d3112c080d58ce560081baeaab7e1e864ca21795ddbf533d5b1842bb1ecf8"
518 521 dependencies = [
519 522 "libc",
520 523 "stable_deref_trait",
521 524 ]
522 525
523 526 [[package]]
524 527 name = "memoffset"
525 528 version = "0.6.1"
526 529 source = "registry+https://github.com/rust-lang/crates.io-index"
527 530 checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87"
528 531 dependencies = [
529 532 "autocfg",
530 533 ]
531 534
532 535 [[package]]
533 536 name = "micro-timer"
534 537 version = "0.3.1"
535 538 source = "registry+https://github.com/rust-lang/crates.io-index"
536 539 checksum = "2620153e1d903d26b72b89f0e9c48d8c4756cba941c185461dddc234980c298c"
537 540 dependencies = [
538 541 "micro-timer-macros",
539 542 "scopeguard",
540 543 ]
541 544
542 545 [[package]]
543 546 name = "micro-timer-macros"
544 547 version = "0.3.1"
545 548 source = "registry+https://github.com/rust-lang/crates.io-index"
546 549 checksum = "e28a3473e6abd6e9aab36aaeef32ad22ae0bd34e79f376643594c2b152ec1c5d"
547 550 dependencies = [
548 551 "proc-macro2",
549 552 "quote",
550 553 "scopeguard",
551 554 "syn",
552 555 ]
553 556
554 557 [[package]]
555 558 name = "miniz_oxide"
556 559 version = "0.4.3"
557 560 source = "registry+https://github.com/rust-lang/crates.io-index"
558 561 checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d"
559 562 dependencies = [
560 563 "adler",
561 564 "autocfg",
562 565 ]
563 566
564 567 [[package]]
565 568 name = "num-integer"
566 569 version = "0.1.44"
567 570 source = "registry+https://github.com/rust-lang/crates.io-index"
568 571 checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
569 572 dependencies = [
570 573 "autocfg",
571 574 "num-traits",
572 575 ]
573 576
574 577 [[package]]
575 578 name = "num-traits"
576 579 version = "0.2.14"
577 580 source = "registry+https://github.com/rust-lang/crates.io-index"
578 581 checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
579 582 dependencies = [
580 583 "autocfg",
581 584 ]
582 585
583 586 [[package]]
584 587 name = "num_cpus"
585 588 version = "1.13.0"
586 589 source = "registry+https://github.com/rust-lang/crates.io-index"
587 590 checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
588 591 dependencies = [
589 592 "hermit-abi",
590 593 "libc",
591 594 ]
592 595
593 596 [[package]]
594 597 name = "opaque-debug"
595 598 version = "0.3.0"
596 599 source = "registry+https://github.com/rust-lang/crates.io-index"
597 600 checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
598 601
599 602 [[package]]
600 603 name = "output_vt100"
601 604 version = "0.1.2"
602 605 source = "registry+https://github.com/rust-lang/crates.io-index"
603 606 checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9"
604 607 dependencies = [
605 608 "winapi",
606 609 ]
607 610
608 611 [[package]]
609 612 name = "paste"
610 613 version = "1.0.5"
611 614 source = "registry+https://github.com/rust-lang/crates.io-index"
612 615 checksum = "acbf547ad0c65e31259204bd90935776d1c693cec2f4ff7abb7a1bbbd40dfe58"
613 616
614 617 [[package]]
615 618 name = "pkg-config"
616 619 version = "0.3.19"
617 620 source = "registry+https://github.com/rust-lang/crates.io-index"
618 621 checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
619 622
620 623 [[package]]
621 624 name = "ppv-lite86"
622 625 version = "0.2.10"
623 626 source = "registry+https://github.com/rust-lang/crates.io-index"
624 627 checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
625 628
626 629 [[package]]
627 630 name = "pretty_assertions"
628 631 version = "0.6.1"
629 632 source = "registry+https://github.com/rust-lang/crates.io-index"
630 633 checksum = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427"
631 634 dependencies = [
632 635 "ansi_term",
633 636 "ctor",
634 637 "difference",
635 638 "output_vt100",
636 639 ]
637 640
638 641 [[package]]
639 642 name = "proc-macro-hack"
640 643 version = "0.5.19"
641 644 source = "registry+https://github.com/rust-lang/crates.io-index"
642 645 checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
643 646
644 647 [[package]]
645 648 name = "proc-macro2"
646 649 version = "1.0.24"
647 650 source = "registry+https://github.com/rust-lang/crates.io-index"
648 651 checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
649 652 dependencies = [
650 653 "unicode-xid",
651 654 ]
652 655
653 656 [[package]]
654 657 name = "python27-sys"
655 658 version = "0.6.0"
656 659 source = "registry+https://github.com/rust-lang/crates.io-index"
657 660 checksum = "5826ddbc5366eb0b0492040fdc25bf50bb49092c192bd45e80fb7a24dc6832ab"
658 661 dependencies = [
659 662 "libc",
660 663 "regex",
661 664 ]
662 665
663 666 [[package]]
664 667 name = "python3-sys"
665 668 version = "0.6.0"
666 669 source = "registry+https://github.com/rust-lang/crates.io-index"
667 670 checksum = "b78af21b29594951a47fc3dac9b9eff0a3f077dec2f780ee943ae16a668f3b6a"
668 671 dependencies = [
669 672 "libc",
670 673 "regex",
671 674 ]
672 675
673 676 [[package]]
674 677 name = "quick-error"
675 678 version = "1.2.3"
676 679 source = "registry+https://github.com/rust-lang/crates.io-index"
677 680 checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
678 681
679 682 [[package]]
680 683 name = "quote"
681 684 version = "1.0.7"
682 685 source = "registry+https://github.com/rust-lang/crates.io-index"
683 686 checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
684 687 dependencies = [
685 688 "proc-macro2",
686 689 ]
687 690
688 691 [[package]]
689 692 name = "rand"
690 693 version = "0.7.3"
691 694 source = "registry+https://github.com/rust-lang/crates.io-index"
692 695 checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
693 696 dependencies = [
694 697 "getrandom",
695 698 "libc",
696 699 "rand_chacha",
697 700 "rand_core",
698 701 "rand_hc",
699 702 ]
700 703
701 704 [[package]]
702 705 name = "rand_chacha"
703 706 version = "0.2.2"
704 707 source = "registry+https://github.com/rust-lang/crates.io-index"
705 708 checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
706 709 dependencies = [
707 710 "ppv-lite86",
708 711 "rand_core",
709 712 ]
710 713
711 714 [[package]]
712 715 name = "rand_core"
713 716 version = "0.5.1"
714 717 source = "registry+https://github.com/rust-lang/crates.io-index"
715 718 checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
716 719 dependencies = [
717 720 "getrandom",
718 721 ]
719 722
720 723 [[package]]
721 724 name = "rand_distr"
722 725 version = "0.2.2"
723 726 source = "registry+https://github.com/rust-lang/crates.io-index"
724 727 checksum = "96977acbdd3a6576fb1d27391900035bf3863d4a16422973a409b488cf29ffb2"
725 728 dependencies = [
726 729 "rand",
727 730 ]
728 731
729 732 [[package]]
730 733 name = "rand_hc"
731 734 version = "0.2.0"
732 735 source = "registry+https://github.com/rust-lang/crates.io-index"
733 736 checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
734 737 dependencies = [
735 738 "rand_core",
736 739 ]
737 740
738 741 [[package]]
739 742 name = "rand_pcg"
740 743 version = "0.2.1"
741 744 source = "registry+https://github.com/rust-lang/crates.io-index"
742 745 checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
743 746 dependencies = [
744 747 "rand_core",
745 748 ]
746 749
747 750 [[package]]
748 751 name = "rand_xoshiro"
749 752 version = "0.4.0"
750 753 source = "registry+https://github.com/rust-lang/crates.io-index"
751 754 checksum = "a9fcdd2e881d02f1d9390ae47ad8e5696a9e4be7b547a1da2afbc61973217004"
752 755 dependencies = [
753 756 "rand_core",
754 757 ]
755 758
756 759 [[package]]
757 760 name = "rayon"
758 761 version = "1.5.0"
759 762 source = "registry+https://github.com/rust-lang/crates.io-index"
760 763 checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674"
761 764 dependencies = [
762 765 "autocfg",
763 766 "crossbeam-deque",
764 767 "either",
765 768 "rayon-core",
766 769 ]
767 770
768 771 [[package]]
769 772 name = "rayon-core"
770 773 version = "1.9.0"
771 774 source = "registry+https://github.com/rust-lang/crates.io-index"
772 775 checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a"
773 776 dependencies = [
774 777 "crossbeam-channel 0.5.0",
775 778 "crossbeam-deque",
776 779 "crossbeam-utils 0.8.1",
777 780 "lazy_static",
778 781 "num_cpus",
779 782 ]
780 783
781 784 [[package]]
782 785 name = "redox_syscall"
783 786 version = "0.1.57"
784 787 source = "registry+https://github.com/rust-lang/crates.io-index"
785 788 checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
786 789
787 790 [[package]]
788 791 name = "regex"
789 792 version = "1.4.2"
790 793 source = "registry+https://github.com/rust-lang/crates.io-index"
791 794 checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c"
792 795 dependencies = [
793 796 "aho-corasick",
794 797 "memchr",
795 798 "regex-syntax",
796 799 "thread_local",
797 800 ]
798 801
799 802 [[package]]
800 803 name = "regex-syntax"
801 804 version = "0.6.21"
802 805 source = "registry+https://github.com/rust-lang/crates.io-index"
803 806 checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
804 807
805 808 [[package]]
806 809 name = "remove_dir_all"
807 810 version = "0.5.3"
808 811 source = "registry+https://github.com/rust-lang/crates.io-index"
809 812 checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
810 813 dependencies = [
811 814 "winapi",
812 815 ]
813 816
814 817 [[package]]
815 818 name = "rhg"
816 819 version = "0.1.0"
817 820 dependencies = [
818 821 "chrono",
819 822 "clap",
820 823 "derive_more",
821 824 "env_logger",
822 825 "format-bytes",
823 826 "hg-core",
824 827 "home",
825 828 "lazy_static",
826 829 "log",
827 830 "micro-timer",
828 831 "regex",
829 832 "users",
830 833 ]
831 834
832 835 [[package]]
833 836 name = "same-file"
834 837 version = "1.0.6"
835 838 source = "registry+https://github.com/rust-lang/crates.io-index"
836 839 checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
837 840 dependencies = [
838 841 "winapi-util",
839 842 ]
840 843
841 844 [[package]]
842 845 name = "scopeguard"
843 846 version = "1.1.0"
844 847 source = "registry+https://github.com/rust-lang/crates.io-index"
845 848 checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
846 849
847 850 [[package]]
848 851 name = "sha-1"
849 852 version = "0.9.6"
850 853 source = "registry+https://github.com/rust-lang/crates.io-index"
851 854 checksum = "8c4cfa741c5832d0ef7fab46cabed29c2aae926db0b11bb2069edd8db5e64e16"
852 855 dependencies = [
853 856 "block-buffer",
854 857 "cfg-if 1.0.0",
855 858 "cpufeatures",
856 859 "digest",
857 860 "opaque-debug",
858 861 ]
859 862
860 863 [[package]]
861 864 name = "sized-chunks"
862 865 version = "0.6.2"
863 866 source = "registry+https://github.com/rust-lang/crates.io-index"
864 867 checksum = "1ec31ceca5644fa6d444cc77548b88b67f46db6f7c71683b0f9336e671830d2f"
865 868 dependencies = [
866 869 "bitmaps",
867 870 "typenum",
868 871 ]
869 872
870 873 [[package]]
871 874 name = "stable_deref_trait"
872 875 version = "1.2.0"
873 876 source = "registry+https://github.com/rust-lang/crates.io-index"
874 877 checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
875 878
876 879 [[package]]
877 880 name = "static_assertions"
878 881 version = "1.1.0"
879 882 source = "registry+https://github.com/rust-lang/crates.io-index"
880 883 checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
881 884
882 885 [[package]]
883 886 name = "strsim"
884 887 version = "0.8.0"
885 888 source = "registry+https://github.com/rust-lang/crates.io-index"
886 889 checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
887 890
888 891 [[package]]
889 892 name = "syn"
890 893 version = "1.0.54"
891 894 source = "registry+https://github.com/rust-lang/crates.io-index"
892 895 checksum = "9a2af957a63d6bd42255c359c93d9bfdb97076bd3b820897ce55ffbfbf107f44"
893 896 dependencies = [
894 897 "proc-macro2",
895 898 "quote",
896 899 "unicode-xid",
897 900 ]
898 901
899 902 [[package]]
900 903 name = "tempfile"
901 904 version = "3.1.0"
902 905 source = "registry+https://github.com/rust-lang/crates.io-index"
903 906 checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
904 907 dependencies = [
905 908 "cfg-if 0.1.10",
906 909 "libc",
907 910 "rand",
908 911 "redox_syscall",
909 912 "remove_dir_all",
910 913 "winapi",
911 914 ]
912 915
913 916 [[package]]
914 917 name = "termcolor"
915 918 version = "1.1.2"
916 919 source = "registry+https://github.com/rust-lang/crates.io-index"
917 920 checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
918 921 dependencies = [
919 922 "winapi-util",
920 923 ]
921 924
922 925 [[package]]
923 926 name = "textwrap"
924 927 version = "0.11.0"
925 928 source = "registry+https://github.com/rust-lang/crates.io-index"
926 929 checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
927 930 dependencies = [
928 931 "unicode-width",
929 932 ]
930 933
931 934 [[package]]
932 935 name = "thread_local"
933 936 version = "1.0.1"
934 937 source = "registry+https://github.com/rust-lang/crates.io-index"
935 938 checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
936 939 dependencies = [
937 940 "lazy_static",
938 941 ]
939 942
940 943 [[package]]
941 944 name = "time"
942 945 version = "0.1.44"
943 946 source = "registry+https://github.com/rust-lang/crates.io-index"
944 947 checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
945 948 dependencies = [
946 949 "libc",
947 950 "wasi 0.10.0+wasi-snapshot-preview1",
948 951 "winapi",
949 952 ]
950 953
951 954 [[package]]
952 955 name = "twox-hash"
953 956 version = "1.6.0"
954 957 source = "registry+https://github.com/rust-lang/crates.io-index"
955 958 checksum = "04f8ab788026715fa63b31960869617cba39117e520eb415b0139543e325ab59"
956 959 dependencies = [
957 960 "cfg-if 0.1.10",
958 961 "rand",
959 962 "static_assertions",
960 963 ]
961 964
962 965 [[package]]
963 966 name = "typenum"
964 967 version = "1.12.0"
965 968 source = "registry+https://github.com/rust-lang/crates.io-index"
966 969 checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
967 970
968 971 [[package]]
969 972 name = "unicode-width"
970 973 version = "0.1.8"
971 974 source = "registry+https://github.com/rust-lang/crates.io-index"
972 975 checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
973 976
974 977 [[package]]
975 978 name = "unicode-xid"
976 979 version = "0.2.1"
977 980 source = "registry+https://github.com/rust-lang/crates.io-index"
978 981 checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
979 982
980 983 [[package]]
981 984 name = "users"
982 985 version = "0.11.0"
983 986 source = "registry+https://github.com/rust-lang/crates.io-index"
984 987 checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032"
985 988 dependencies = [
986 989 "libc",
987 990 "log",
988 991 ]
989 992
990 993 [[package]]
991 994 name = "vcpkg"
992 995 version = "0.2.11"
993 996 source = "registry+https://github.com/rust-lang/crates.io-index"
994 997 checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb"
995 998
996 999 [[package]]
997 1000 name = "vec_map"
998 1001 version = "0.8.2"
999 1002 source = "registry+https://github.com/rust-lang/crates.io-index"
1000 1003 checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
1001 1004
1002 1005 [[package]]
1003 1006 name = "version_check"
1004 1007 version = "0.9.2"
1005 1008 source = "registry+https://github.com/rust-lang/crates.io-index"
1006 1009 checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
1007 1010
1008 1011 [[package]]
1009 1012 name = "wasi"
1010 1013 version = "0.9.0+wasi-snapshot-preview1"
1011 1014 source = "registry+https://github.com/rust-lang/crates.io-index"
1012 1015 checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
1013 1016
1014 1017 [[package]]
1015 1018 name = "wasi"
1016 1019 version = "0.10.0+wasi-snapshot-preview1"
1017 1020 source = "registry+https://github.com/rust-lang/crates.io-index"
1018 1021 checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
1019 1022
1020 1023 [[package]]
1021 1024 name = "winapi"
1022 1025 version = "0.3.9"
1023 1026 source = "registry+https://github.com/rust-lang/crates.io-index"
1024 1027 checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
1025 1028 dependencies = [
1026 1029 "winapi-i686-pc-windows-gnu",
1027 1030 "winapi-x86_64-pc-windows-gnu",
1028 1031 ]
1029 1032
1030 1033 [[package]]
1031 1034 name = "winapi-i686-pc-windows-gnu"
1032 1035 version = "0.4.0"
1033 1036 source = "registry+https://github.com/rust-lang/crates.io-index"
1034 1037 checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
1035 1038
1036 1039 [[package]]
1037 1040 name = "winapi-util"
1038 1041 version = "0.1.5"
1039 1042 source = "registry+https://github.com/rust-lang/crates.io-index"
1040 1043 checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
1041 1044 dependencies = [
1042 1045 "winapi",
1043 1046 ]
1044 1047
1045 1048 [[package]]
1046 1049 name = "winapi-x86_64-pc-windows-gnu"
1047 1050 version = "0.4.0"
1048 1051 source = "registry+https://github.com/rust-lang/crates.io-index"
1049 1052 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
1050 1053
1051 1054 [[package]]
1052 1055 name = "zstd"
1053 1056 version = "0.5.3+zstd.1.4.5"
1054 1057 source = "registry+https://github.com/rust-lang/crates.io-index"
1055 1058 checksum = "01b32eaf771efa709e8308605bbf9319bf485dc1503179ec0469b611937c0cd8"
1056 1059 dependencies = [
1057 1060 "zstd-safe",
1058 1061 ]
1059 1062
1060 1063 [[package]]
1061 1064 name = "zstd-safe"
1062 1065 version = "2.0.5+zstd.1.4.5"
1063 1066 source = "registry+https://github.com/rust-lang/crates.io-index"
1064 1067 checksum = "1cfb642e0d27f64729a639c52db457e0ae906e7bc6f5fe8f5c453230400f1055"
1065 1068 dependencies = [
1066 1069 "libc",
1067 1070 "zstd-sys",
1068 1071 ]
1069 1072
1070 1073 [[package]]
1071 1074 name = "zstd-sys"
1072 1075 version = "1.4.17+zstd.1.4.5"
1073 1076 source = "registry+https://github.com/rust-lang/crates.io-index"
1074 1077 checksum = "b89249644df056b522696b1bb9e7c18c87e8ffa3e2f0dc3b0155875d6498f01b"
1075 1078 dependencies = [
1076 1079 "cc",
1077 1080 "glob",
1078 1081 "itertools",
1079 1082 "libc",
1080 1083 ]
@@ -1,46 +1,47
1 1 [package]
2 2 name = "hg-core"
3 3 version = "0.1.0"
4 4 authors = ["Georges Racinet <gracinet@anybox.fr>"]
5 5 description = "Mercurial pure Rust core library, with no assumption on Python bindings (FFI)"
6 6 edition = "2018"
7 7
8 8 [lib]
9 9 name = "hg"
10 10
11 11 [dependencies]
12 12 bitflags = "1.2"
13 13 bytes-cast = "0.2"
14 14 byteorder = "1.3.4"
15 15 derive_more = "0.99"
16 16 home = "0.5"
17 17 im-rc = "15.0.*"
18 18 itertools = "0.9"
19 19 lazy_static = "1.4.0"
20 libc = "0.2"
20 21 rand = "0.7.3"
21 22 rand_pcg = "0.2.1"
22 23 rand_distr = "0.2.2"
23 24 rayon = "1.3.0"
24 25 regex = "1.3.9"
25 26 sha-1 = "0.9.6"
26 27 twox-hash = "1.5.0"
27 28 same-file = "1.0.6"
28 29 stable_deref_trait = "1.2.0"
29 30 tempfile = "3.1.0"
30 31 crossbeam-channel = "0.4"
31 32 micro-timer = "0.3.0"
32 33 log = "0.4.8"
33 34 memmap2 = {version = "0.4", features = ["stable_deref_trait"]}
34 35 zstd = "0.5.3"
35 36 format-bytes = "0.2.2"
36 37
37 38 # We don't use the `miniz-oxide` backend to not change rhg benchmarks and until
38 39 # we have a clearer view of which backend is the fastest.
39 40 [dependencies.flate2]
40 41 version = "1.0.16"
41 42 features = ["zlib"]
42 43 default-features = false
43 44
44 45 [dev-dependencies]
45 46 clap = "*"
46 47 pretty_assertions = "0.6.1"
@@ -1,707 +1,725
1 1 //! The "version 2" disk representation of the dirstate
2 2 //!
3 3 //! See `mercurial/helptext/internals/dirstate-v2.txt`
4 4
5 5 use crate::dirstate::TruncatedTimestamp;
6 6 use crate::dirstate_tree::dirstate_map::{self, DirstateMap, NodeRef};
7 7 use crate::dirstate_tree::path_with_basename::WithBasename;
8 8 use crate::errors::HgError;
9 9 use crate::utils::hg_path::HgPath;
10 10 use crate::DirstateEntry;
11 11 use crate::DirstateError;
12 12 use crate::DirstateParents;
13 13 use bitflags::bitflags;
14 14 use bytes_cast::unaligned::{U16Be, U32Be};
15 15 use bytes_cast::BytesCast;
16 16 use format_bytes::format_bytes;
17 17 use std::borrow::Cow;
18 18 use std::convert::{TryFrom, TryInto};
19 19
20 20 /// Added at the start of `.hg/dirstate` when the "v2" format is used.
21 21 /// This a redundant sanity check more than an actual "magic number" since
22 22 /// `.hg/requires` already governs which format should be used.
23 23 pub const V2_FORMAT_MARKER: &[u8; 12] = b"dirstate-v2\n";
24 24
25 25 /// Keep space for 256-bit hashes
26 26 const STORED_NODE_ID_BYTES: usize = 32;
27 27
28 28 /// … even though only 160 bits are used for now, with SHA-1
29 29 const USED_NODE_ID_BYTES: usize = 20;
30 30
31 31 pub(super) const IGNORE_PATTERNS_HASH_LEN: usize = 20;
32 32 pub(super) type IgnorePatternsHash = [u8; IGNORE_PATTERNS_HASH_LEN];
33 33
34 34 /// Must match the constant of the same name in
35 35 /// `mercurial/dirstateutils/docket.py`
36 36 const TREE_METADATA_SIZE: usize = 44;
37 37
38 38 /// Make sure that size-affecting changes are made knowingly
39 39 #[allow(unused)]
40 40 fn static_assert_size_of() {
41 41 let _ = std::mem::transmute::<TreeMetadata, [u8; TREE_METADATA_SIZE]>;
42 42 let _ = std::mem::transmute::<DocketHeader, [u8; TREE_METADATA_SIZE + 81]>;
43 43 let _ = std::mem::transmute::<Node, [u8; 43]>;
44 44 }
45 45
46 46 // Must match `HEADER` in `mercurial/dirstateutils/docket.py`
47 47 #[derive(BytesCast)]
48 48 #[repr(C)]
49 49 struct DocketHeader {
50 50 marker: [u8; V2_FORMAT_MARKER.len()],
51 51 parent_1: [u8; STORED_NODE_ID_BYTES],
52 52 parent_2: [u8; STORED_NODE_ID_BYTES],
53 53
54 54 metadata: TreeMetadata,
55 55
56 56 /// Counted in bytes
57 57 data_size: Size,
58 58
59 59 uuid_size: u8,
60 60 }
61 61
62 62 pub struct Docket<'on_disk> {
63 63 header: &'on_disk DocketHeader,
64 64 uuid: &'on_disk [u8],
65 65 }
66 66
67 67 /// Fields are documented in the *Tree metadata in the docket file*
68 68 /// section of `mercurial/helptext/internals/dirstate-v2.txt`
69 69 #[derive(BytesCast)]
70 70 #[repr(C)]
71 71 struct TreeMetadata {
72 72 root_nodes: ChildNodes,
73 73 nodes_with_entry_count: Size,
74 74 nodes_with_copy_source_count: Size,
75 75 unreachable_bytes: Size,
76 76 unused: [u8; 4],
77 77
78 78 /// See *Optional hash of ignore patterns* section of
79 79 /// `mercurial/helptext/internals/dirstate-v2.txt`
80 80 ignore_patterns_hash: IgnorePatternsHash,
81 81 }
82 82
83 83 /// Fields are documented in the *The data file format*
84 84 /// section of `mercurial/helptext/internals/dirstate-v2.txt`
85 85 #[derive(BytesCast)]
86 86 #[repr(C)]
87 87 pub(super) struct Node {
88 88 full_path: PathSlice,
89 89
90 90 /// In bytes from `self.full_path.start`
91 91 base_name_start: PathSize,
92 92
93 93 copy_source: OptPathSlice,
94 94 children: ChildNodes,
95 95 pub(super) descendants_with_entry_count: Size,
96 96 pub(super) tracked_descendants_count: Size,
97 97 flags: Flags,
98 98 data: Entry,
99 99 }
100 100
101 101 bitflags! {
102 102 #[derive(BytesCast)]
103 103 #[repr(C)]
104 104 struct Flags: u8 {
105 105 const WDIR_TRACKED = 1 << 0;
106 106 const P1_TRACKED = 1 << 1;
107 107 const P2_INFO = 1 << 2;
108 108 const HAS_MODE_AND_SIZE = 1 << 3;
109 109 const HAS_MTIME = 1 << 4;
110 const MODE_EXEC_PERM = 1 << 5;
111 const MODE_IS_SYMLINK = 1 << 7;
110 112 }
111 113 }
112 114
113 115 #[derive(BytesCast, Copy, Clone, Debug)]
114 116 #[repr(C)]
115 117 struct Entry {
116 mode: U32Be,
118 _padding: U32Be,
117 119 size: U32Be,
118 120 mtime: U32Be,
119 121 }
120 122
121 123 /// Duration since the Unix epoch
122 124 #[derive(BytesCast, Copy, Clone)]
123 125 #[repr(C)]
124 126 struct PackedTimestamp {
125 127 _padding: U32Be,
126 128 truncated_seconds: U32Be,
127 129 nanoseconds: U32Be,
128 130 }
129 131
130 132 /// Counted in bytes from the start of the file
131 133 ///
132 134 /// NOTE: not supporting `.hg/dirstate` files larger than 4 GiB.
133 135 type Offset = U32Be;
134 136
135 137 /// Counted in number of items
136 138 ///
137 139 /// NOTE: we choose not to support counting more than 4 billion nodes anywhere.
138 140 type Size = U32Be;
139 141
140 142 /// Counted in bytes
141 143 ///
142 144 /// NOTE: we choose not to support file names/paths longer than 64 KiB.
143 145 type PathSize = U16Be;
144 146
145 147 /// A contiguous sequence of `len` times `Node`, representing the child nodes
146 148 /// of either some other node or of the repository root.
147 149 ///
148 150 /// Always sorted by ascending `full_path`, to allow binary search.
149 151 /// Since nodes with the same parent nodes also have the same parent path,
150 152 /// only the `base_name`s need to be compared during binary search.
151 153 #[derive(BytesCast, Copy, Clone)]
152 154 #[repr(C)]
153 155 struct ChildNodes {
154 156 start: Offset,
155 157 len: Size,
156 158 }
157 159
158 160 /// A `HgPath` of `len` bytes
159 161 #[derive(BytesCast, Copy, Clone)]
160 162 #[repr(C)]
161 163 struct PathSlice {
162 164 start: Offset,
163 165 len: PathSize,
164 166 }
165 167
166 168 /// Either nothing if `start == 0`, or a `HgPath` of `len` bytes
167 169 type OptPathSlice = PathSlice;
168 170
169 171 /// Unexpected file format found in `.hg/dirstate` with the "v2" format.
170 172 ///
171 173 /// This should only happen if Mercurial is buggy or a repository is corrupted.
172 174 #[derive(Debug)]
173 175 pub struct DirstateV2ParseError;
174 176
175 177 impl From<DirstateV2ParseError> for HgError {
176 178 fn from(_: DirstateV2ParseError) -> Self {
177 179 HgError::corrupted("dirstate-v2 parse error")
178 180 }
179 181 }
180 182
181 183 impl From<DirstateV2ParseError> for crate::DirstateError {
182 184 fn from(error: DirstateV2ParseError) -> Self {
183 185 HgError::from(error).into()
184 186 }
185 187 }
186 188
187 189 impl<'on_disk> Docket<'on_disk> {
188 190 pub fn parents(&self) -> DirstateParents {
189 191 use crate::Node;
190 192 let p1 = Node::try_from(&self.header.parent_1[..USED_NODE_ID_BYTES])
191 193 .unwrap()
192 194 .clone();
193 195 let p2 = Node::try_from(&self.header.parent_2[..USED_NODE_ID_BYTES])
194 196 .unwrap()
195 197 .clone();
196 198 DirstateParents { p1, p2 }
197 199 }
198 200
199 201 pub fn tree_metadata(&self) -> &[u8] {
200 202 self.header.metadata.as_bytes()
201 203 }
202 204
203 205 pub fn data_size(&self) -> usize {
204 206 // This `unwrap` could only panic on a 16-bit CPU
205 207 self.header.data_size.get().try_into().unwrap()
206 208 }
207 209
208 210 pub fn data_filename(&self) -> String {
209 211 String::from_utf8(format_bytes!(b"dirstate.{}", self.uuid)).unwrap()
210 212 }
211 213 }
212 214
213 215 pub fn read_docket(
214 216 on_disk: &[u8],
215 217 ) -> Result<Docket<'_>, DirstateV2ParseError> {
216 218 let (header, uuid) =
217 219 DocketHeader::from_bytes(on_disk).map_err(|_| DirstateV2ParseError)?;
218 220 let uuid_size = header.uuid_size as usize;
219 221 if header.marker == *V2_FORMAT_MARKER && uuid.len() == uuid_size {
220 222 Ok(Docket { header, uuid })
221 223 } else {
222 224 Err(DirstateV2ParseError)
223 225 }
224 226 }
225 227
226 228 pub(super) fn read<'on_disk>(
227 229 on_disk: &'on_disk [u8],
228 230 metadata: &[u8],
229 231 ) -> Result<DirstateMap<'on_disk>, DirstateV2ParseError> {
230 232 if on_disk.is_empty() {
231 233 return Ok(DirstateMap::empty(on_disk));
232 234 }
233 235 let (meta, _) = TreeMetadata::from_bytes(metadata)
234 236 .map_err(|_| DirstateV2ParseError)?;
235 237 let dirstate_map = DirstateMap {
236 238 on_disk,
237 239 root: dirstate_map::ChildNodes::OnDisk(read_nodes(
238 240 on_disk,
239 241 meta.root_nodes,
240 242 )?),
241 243 nodes_with_entry_count: meta.nodes_with_entry_count.get(),
242 244 nodes_with_copy_source_count: meta.nodes_with_copy_source_count.get(),
243 245 ignore_patterns_hash: meta.ignore_patterns_hash,
244 246 unreachable_bytes: meta.unreachable_bytes.get(),
245 247 };
246 248 Ok(dirstate_map)
247 249 }
248 250
249 251 impl Node {
250 252 pub(super) fn full_path<'on_disk>(
251 253 &self,
252 254 on_disk: &'on_disk [u8],
253 255 ) -> Result<&'on_disk HgPath, DirstateV2ParseError> {
254 256 read_hg_path(on_disk, self.full_path)
255 257 }
256 258
257 259 pub(super) fn base_name_start<'on_disk>(
258 260 &self,
259 261 ) -> Result<usize, DirstateV2ParseError> {
260 262 let start = self.base_name_start.get();
261 263 if start < self.full_path.len.get() {
262 264 let start = usize::try_from(start)
263 265 // u32 -> usize, could only panic on a 16-bit CPU
264 266 .expect("dirstate-v2 base_name_start out of bounds");
265 267 Ok(start)
266 268 } else {
267 269 Err(DirstateV2ParseError)
268 270 }
269 271 }
270 272
271 273 pub(super) fn base_name<'on_disk>(
272 274 &self,
273 275 on_disk: &'on_disk [u8],
274 276 ) -> Result<&'on_disk HgPath, DirstateV2ParseError> {
275 277 let full_path = self.full_path(on_disk)?;
276 278 let base_name_start = self.base_name_start()?;
277 279 Ok(HgPath::new(&full_path.as_bytes()[base_name_start..]))
278 280 }
279 281
280 282 pub(super) fn path<'on_disk>(
281 283 &self,
282 284 on_disk: &'on_disk [u8],
283 285 ) -> Result<dirstate_map::NodeKey<'on_disk>, DirstateV2ParseError> {
284 286 Ok(WithBasename::from_raw_parts(
285 287 Cow::Borrowed(self.full_path(on_disk)?),
286 288 self.base_name_start()?,
287 289 ))
288 290 }
289 291
290 292 pub(super) fn has_copy_source<'on_disk>(&self) -> bool {
291 293 self.copy_source.start.get() != 0
292 294 }
293 295
294 296 pub(super) fn copy_source<'on_disk>(
295 297 &self,
296 298 on_disk: &'on_disk [u8],
297 299 ) -> Result<Option<&'on_disk HgPath>, DirstateV2ParseError> {
298 300 Ok(if self.has_copy_source() {
299 301 Some(read_hg_path(on_disk, self.copy_source)?)
300 302 } else {
301 303 None
302 304 })
303 305 }
304 306
305 307 fn has_entry(&self) -> bool {
306 308 self.flags.intersects(
307 309 Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_INFO,
308 310 )
309 311 }
310 312
311 313 pub(super) fn node_data(
312 314 &self,
313 315 ) -> Result<dirstate_map::NodeData, DirstateV2ParseError> {
314 316 if self.has_entry() {
315 317 Ok(dirstate_map::NodeData::Entry(self.assume_entry()))
316 318 } else if let Some(mtime) = self.cached_directory_mtime()? {
317 319 Ok(dirstate_map::NodeData::CachedDirectory { mtime })
318 320 } else {
319 321 Ok(dirstate_map::NodeData::None)
320 322 }
321 323 }
322 324
323 325 pub(super) fn cached_directory_mtime(
324 326 &self,
325 327 ) -> Result<Option<TruncatedTimestamp>, DirstateV2ParseError> {
326 328 Ok(
327 329 if self.flags.contains(Flags::HAS_MTIME) && !self.has_entry() {
328 330 Some(self.data.as_timestamp()?)
329 331 } else {
330 332 None
331 333 },
332 334 )
333 335 }
334 336
337 fn synthesize_unix_mode(&self) -> u32 {
338 let file_type = if self.flags.contains(Flags::MODE_IS_SYMLINK) {
339 libc::S_IFLNK
340 } else {
341 libc::S_IFREG
342 };
343 let permisions = if self.flags.contains(Flags::MODE_EXEC_PERM) {
344 0o755
345 } else {
346 0o644
347 };
348 file_type | permisions
349 }
350
335 351 fn assume_entry(&self) -> DirstateEntry {
336 352 // TODO: convert through raw bits instead?
337 353 let wdir_tracked = self.flags.contains(Flags::WDIR_TRACKED);
338 354 let p1_tracked = self.flags.contains(Flags::P1_TRACKED);
339 355 let p2_info = self.flags.contains(Flags::P2_INFO);
340 356 let mode_size = if self.flags.contains(Flags::HAS_MODE_AND_SIZE) {
341 Some((self.data.mode.into(), self.data.size.into()))
357 Some((self.synthesize_unix_mode(), self.data.size.into()))
342 358 } else {
343 359 None
344 360 };
345 361 let mtime = if self.flags.contains(Flags::HAS_MTIME) {
346 362 Some(self.data.mtime.into())
347 363 } else {
348 364 None
349 365 };
350 366 DirstateEntry::from_v2_data(
351 367 wdir_tracked,
352 368 p1_tracked,
353 369 p2_info,
354 370 mode_size,
355 371 mtime,
356 372 )
357 373 }
358 374
359 375 pub(super) fn entry(
360 376 &self,
361 377 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
362 378 if self.has_entry() {
363 379 Ok(Some(self.assume_entry()))
364 380 } else {
365 381 Ok(None)
366 382 }
367 383 }
368 384
369 385 pub(super) fn children<'on_disk>(
370 386 &self,
371 387 on_disk: &'on_disk [u8],
372 388 ) -> Result<&'on_disk [Node], DirstateV2ParseError> {
373 389 read_nodes(on_disk, self.children)
374 390 }
375 391
376 392 pub(super) fn to_in_memory_node<'on_disk>(
377 393 &self,
378 394 on_disk: &'on_disk [u8],
379 395 ) -> Result<dirstate_map::Node<'on_disk>, DirstateV2ParseError> {
380 396 Ok(dirstate_map::Node {
381 397 children: dirstate_map::ChildNodes::OnDisk(
382 398 self.children(on_disk)?,
383 399 ),
384 400 copy_source: self.copy_source(on_disk)?.map(Cow::Borrowed),
385 401 data: self.node_data()?,
386 402 descendants_with_entry_count: self
387 403 .descendants_with_entry_count
388 404 .get(),
389 405 tracked_descendants_count: self.tracked_descendants_count.get(),
390 406 })
391 407 }
392 408 }
393 409
394 410 impl Entry {
395 411 fn from_dirstate_entry(entry: &DirstateEntry) -> (Flags, Self) {
396 412 let (wdir_tracked, p1_tracked, p2_info, mode_size_opt, mtime_opt) =
397 413 entry.v2_data();
398 414 // TODO: convert throug raw flag bits instead?
399 415 let mut flags = Flags::empty();
400 416 flags.set(Flags::WDIR_TRACKED, wdir_tracked);
401 417 flags.set(Flags::P1_TRACKED, p1_tracked);
402 418 flags.set(Flags::P2_INFO, p2_info);
403 let (mode, size, mtime);
419 let (size, mtime);
404 420 if let Some((m, s)) = mode_size_opt {
405 mode = m;
421 let exec_perm = m & libc::S_IXUSR != 0;
422 let is_symlink = m & libc::S_IFMT == libc::S_IFLNK;
423 flags.set(Flags::MODE_EXEC_PERM, exec_perm);
424 flags.set(Flags::MODE_IS_SYMLINK, is_symlink);
406 425 size = s;
407 426 flags.insert(Flags::HAS_MODE_AND_SIZE)
408 427 } else {
409 mode = 0;
410 428 size = 0;
411 429 }
412 430 if let Some(m) = mtime_opt {
413 431 mtime = m;
414 432 flags.insert(Flags::HAS_MTIME);
415 433 } else {
416 434 mtime = 0;
417 435 }
418 436 let raw_entry = Entry {
419 mode: mode.into(),
437 _padding: 0.into(),
420 438 size: size.into(),
421 439 mtime: mtime.into(),
422 440 };
423 441 (flags, raw_entry)
424 442 }
425 443
426 444 fn from_timestamp(timestamp: TruncatedTimestamp) -> Self {
427 445 let packed = PackedTimestamp {
428 446 _padding: 0.into(),
429 447 truncated_seconds: timestamp.truncated_seconds().into(),
430 448 nanoseconds: timestamp.nanoseconds().into(),
431 449 };
432 450 // Safety: both types implement the `ByteCast` trait, so we could
433 451 // safely use `as_bytes` and `from_bytes` to do this conversion. Using
434 452 // `transmute` instead makes the compiler check that the two types
435 453 // have the same size, which eliminates the error case of
436 454 // `from_bytes`.
437 455 unsafe { std::mem::transmute::<PackedTimestamp, Entry>(packed) }
438 456 }
439 457
440 458 fn as_timestamp(self) -> Result<TruncatedTimestamp, DirstateV2ParseError> {
441 459 // Safety: same as above in `from_timestamp`
442 460 let packed =
443 461 unsafe { std::mem::transmute::<Entry, PackedTimestamp>(self) };
444 462 TruncatedTimestamp::from_already_truncated(
445 463 packed.truncated_seconds.get(),
446 464 packed.nanoseconds.get(),
447 465 )
448 466 }
449 467 }
450 468
451 469 fn read_hg_path(
452 470 on_disk: &[u8],
453 471 slice: PathSlice,
454 472 ) -> Result<&HgPath, DirstateV2ParseError> {
455 473 read_slice(on_disk, slice.start, slice.len.get()).map(HgPath::new)
456 474 }
457 475
458 476 fn read_nodes(
459 477 on_disk: &[u8],
460 478 slice: ChildNodes,
461 479 ) -> Result<&[Node], DirstateV2ParseError> {
462 480 read_slice(on_disk, slice.start, slice.len.get())
463 481 }
464 482
465 483 fn read_slice<T, Len>(
466 484 on_disk: &[u8],
467 485 start: Offset,
468 486 len: Len,
469 487 ) -> Result<&[T], DirstateV2ParseError>
470 488 where
471 489 T: BytesCast,
472 490 Len: TryInto<usize>,
473 491 {
474 492 // Either `usize::MAX` would result in "out of bounds" error since a single
475 493 // `&[u8]` cannot occupy the entire addess space.
476 494 let start = start.get().try_into().unwrap_or(std::usize::MAX);
477 495 let len = len.try_into().unwrap_or(std::usize::MAX);
478 496 on_disk
479 497 .get(start..)
480 498 .and_then(|bytes| T::slice_from_bytes(bytes, len).ok())
481 499 .map(|(slice, _rest)| slice)
482 500 .ok_or_else(|| DirstateV2ParseError)
483 501 }
484 502
485 503 pub(crate) fn for_each_tracked_path<'on_disk>(
486 504 on_disk: &'on_disk [u8],
487 505 metadata: &[u8],
488 506 mut f: impl FnMut(&'on_disk HgPath),
489 507 ) -> Result<(), DirstateV2ParseError> {
490 508 let (meta, _) = TreeMetadata::from_bytes(metadata)
491 509 .map_err(|_| DirstateV2ParseError)?;
492 510 fn recur<'on_disk>(
493 511 on_disk: &'on_disk [u8],
494 512 nodes: ChildNodes,
495 513 f: &mut impl FnMut(&'on_disk HgPath),
496 514 ) -> Result<(), DirstateV2ParseError> {
497 515 for node in read_nodes(on_disk, nodes)? {
498 516 if let Some(entry) = node.entry()? {
499 517 if entry.state().is_tracked() {
500 518 f(node.full_path(on_disk)?)
501 519 }
502 520 }
503 521 recur(on_disk, node.children, f)?
504 522 }
505 523 Ok(())
506 524 }
507 525 recur(on_disk, meta.root_nodes, &mut f)
508 526 }
509 527
510 528 /// Returns new data and metadata, together with whether that data should be
511 529 /// appended to the existing data file whose content is at
512 530 /// `dirstate_map.on_disk` (true), instead of written to a new data file
513 531 /// (false).
514 532 pub(super) fn write(
515 533 dirstate_map: &mut DirstateMap,
516 534 can_append: bool,
517 535 ) -> Result<(Vec<u8>, Vec<u8>, bool), DirstateError> {
518 536 let append = can_append && dirstate_map.write_should_append();
519 537
520 538 // This ignores the space for paths, and for nodes without an entry.
521 539 // TODO: better estimate? Skip the `Vec` and write to a file directly?
522 540 let size_guess = std::mem::size_of::<Node>()
523 541 * dirstate_map.nodes_with_entry_count as usize;
524 542
525 543 let mut writer = Writer {
526 544 dirstate_map,
527 545 append,
528 546 out: Vec::with_capacity(size_guess),
529 547 };
530 548
531 549 let root_nodes = writer.write_nodes(dirstate_map.root.as_ref())?;
532 550
533 551 let meta = TreeMetadata {
534 552 root_nodes,
535 553 nodes_with_entry_count: dirstate_map.nodes_with_entry_count.into(),
536 554 nodes_with_copy_source_count: dirstate_map
537 555 .nodes_with_copy_source_count
538 556 .into(),
539 557 unreachable_bytes: dirstate_map.unreachable_bytes.into(),
540 558 unused: [0; 4],
541 559 ignore_patterns_hash: dirstate_map.ignore_patterns_hash,
542 560 };
543 561 Ok((writer.out, meta.as_bytes().to_vec(), append))
544 562 }
545 563
546 564 struct Writer<'dmap, 'on_disk> {
547 565 dirstate_map: &'dmap DirstateMap<'on_disk>,
548 566 append: bool,
549 567 out: Vec<u8>,
550 568 }
551 569
552 570 impl Writer<'_, '_> {
553 571 fn write_nodes(
554 572 &mut self,
555 573 nodes: dirstate_map::ChildNodesRef,
556 574 ) -> Result<ChildNodes, DirstateError> {
557 575 // Reuse already-written nodes if possible
558 576 if self.append {
559 577 if let dirstate_map::ChildNodesRef::OnDisk(nodes_slice) = nodes {
560 578 let start = self.on_disk_offset_of(nodes_slice).expect(
561 579 "dirstate-v2 OnDisk nodes not found within on_disk",
562 580 );
563 581 let len = child_nodes_len_from_usize(nodes_slice.len());
564 582 return Ok(ChildNodes { start, len });
565 583 }
566 584 }
567 585
568 586 // `dirstate_map::ChildNodes::InMemory` contains a `HashMap` which has
569 587 // undefined iteration order. Sort to enable binary search in the
570 588 // written file.
571 589 let nodes = nodes.sorted();
572 590 let nodes_len = nodes.len();
573 591
574 592 // First accumulate serialized nodes in a `Vec`
575 593 let mut on_disk_nodes = Vec::with_capacity(nodes_len);
576 594 for node in nodes {
577 595 let children =
578 596 self.write_nodes(node.children(self.dirstate_map.on_disk)?)?;
579 597 let full_path = node.full_path(self.dirstate_map.on_disk)?;
580 598 let full_path = self.write_path(full_path.as_bytes());
581 599 let copy_source = if let Some(source) =
582 600 node.copy_source(self.dirstate_map.on_disk)?
583 601 {
584 602 self.write_path(source.as_bytes())
585 603 } else {
586 604 PathSlice {
587 605 start: 0.into(),
588 606 len: 0.into(),
589 607 }
590 608 };
591 609 on_disk_nodes.push(match node {
592 610 NodeRef::InMemory(path, node) => {
593 611 let (flags, data) = match &node.data {
594 612 dirstate_map::NodeData::Entry(entry) => {
595 613 Entry::from_dirstate_entry(entry)
596 614 }
597 615 dirstate_map::NodeData::CachedDirectory { mtime } => {
598 616 (Flags::HAS_MTIME, Entry::from_timestamp(*mtime))
599 617 }
600 618 dirstate_map::NodeData::None => (
601 619 Flags::empty(),
602 620 Entry {
603 mode: 0.into(),
621 _padding: 0.into(),
604 622 size: 0.into(),
605 623 mtime: 0.into(),
606 624 },
607 625 ),
608 626 };
609 627 Node {
610 628 children,
611 629 copy_source,
612 630 full_path,
613 631 base_name_start: u16::try_from(path.base_name_start())
614 632 // Could only panic for paths over 64 KiB
615 633 .expect("dirstate-v2 path length overflow")
616 634 .into(),
617 635 descendants_with_entry_count: node
618 636 .descendants_with_entry_count
619 637 .into(),
620 638 tracked_descendants_count: node
621 639 .tracked_descendants_count
622 640 .into(),
623 641 flags,
624 642 data,
625 643 }
626 644 }
627 645 NodeRef::OnDisk(node) => Node {
628 646 children,
629 647 copy_source,
630 648 full_path,
631 649 ..*node
632 650 },
633 651 })
634 652 }
635 653 // … so we can write them contiguously, after writing everything else
636 654 // they refer to.
637 655 let start = self.current_offset();
638 656 let len = child_nodes_len_from_usize(nodes_len);
639 657 self.out.extend(on_disk_nodes.as_bytes());
640 658 Ok(ChildNodes { start, len })
641 659 }
642 660
643 661 /// If the given slice of items is within `on_disk`, returns its offset
644 662 /// from the start of `on_disk`.
645 663 fn on_disk_offset_of<T>(&self, slice: &[T]) -> Option<Offset>
646 664 where
647 665 T: BytesCast,
648 666 {
649 667 fn address_range(slice: &[u8]) -> std::ops::RangeInclusive<usize> {
650 668 let start = slice.as_ptr() as usize;
651 669 let end = start + slice.len();
652 670 start..=end
653 671 }
654 672 let slice_addresses = address_range(slice.as_bytes());
655 673 let on_disk_addresses = address_range(self.dirstate_map.on_disk);
656 674 if on_disk_addresses.contains(slice_addresses.start())
657 675 && on_disk_addresses.contains(slice_addresses.end())
658 676 {
659 677 let offset = slice_addresses.start() - on_disk_addresses.start();
660 678 Some(offset_from_usize(offset))
661 679 } else {
662 680 None
663 681 }
664 682 }
665 683
666 684 fn current_offset(&mut self) -> Offset {
667 685 let mut offset = self.out.len();
668 686 if self.append {
669 687 offset += self.dirstate_map.on_disk.len()
670 688 }
671 689 offset_from_usize(offset)
672 690 }
673 691
674 692 fn write_path(&mut self, slice: &[u8]) -> PathSlice {
675 693 let len = path_len_from_usize(slice.len());
676 694 // Reuse an already-written path if possible
677 695 if self.append {
678 696 if let Some(start) = self.on_disk_offset_of(slice) {
679 697 return PathSlice { start, len };
680 698 }
681 699 }
682 700 let start = self.current_offset();
683 701 self.out.extend(slice.as_bytes());
684 702 PathSlice { start, len }
685 703 }
686 704 }
687 705
688 706 fn offset_from_usize(x: usize) -> Offset {
689 707 u32::try_from(x)
690 708 // Could only panic for a dirstate file larger than 4 GiB
691 709 .expect("dirstate-v2 offset overflow")
692 710 .into()
693 711 }
694 712
695 713 fn child_nodes_len_from_usize(x: usize) -> Size {
696 714 u32::try_from(x)
697 715 // Could only panic with over 4 billion nodes
698 716 .expect("dirstate-v2 slice length overflow")
699 717 .into()
700 718 }
701 719
702 720 fn path_len_from_usize(x: usize) -> PathSize {
703 721 u16::try_from(x)
704 722 // Could only panic for paths over 64 KiB
705 723 .expect("dirstate-v2 path length overflow")
706 724 .into()
707 725 }
@@ -1,33 +1,33
1 1 [package]
2 2 name = "hg-cpython"
3 3 version = "0.1.0"
4 4 authors = ["Georges Racinet <gracinet@anybox.fr>"]
5 5 edition = "2018"
6 6
7 7 [lib]
8 8 name='rusthg'
9 9 crate-type = ["cdylib"]
10 10
11 11 [features]
12 12 default = ["python3"]
13 13
14 14 # Features to build an extension module:
15 15 python27 = ["cpython/python27-sys", "cpython/extension-module-2-7"]
16 16 python3 = ["cpython/python3-sys", "cpython/extension-module"]
17 17
18 18 # Enable one of these features to build a test executable linked to libpython:
19 19 # e.g. cargo test --no-default-features --features python27-bin
20 20 python27-bin = ["cpython/python27-sys"]
21 21 python3-bin = ["cpython/python3-sys"]
22 22
23 23 [dependencies]
24 24 crossbeam-channel = "0.4"
25 25 hg-core = { path = "../hg-core"}
26 libc = '*'
26 libc = "0.2"
27 27 log = "0.4.8"
28 28 env_logger = "0.7.1"
29 29 stable_deref_trait = "1.2.0"
30 30
31 31 [dependencies.cpython]
32 32 version = "0.6.0"
33 33 default-features = false
General Comments 0
You need to be logged in to leave comments. Login now