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