##// END OF EJS Templates
rust-dirstatemap: add Rust implementation of `reset_state`...
Raphaël Gomès -
r49992:dd043043 default
parent child Browse files
Show More
@@ -1,716 +1,722 b''
1 use crate::dirstate_tree::on_disk::DirstateV2ParseError;
1 use crate::dirstate_tree::on_disk::DirstateV2ParseError;
2 use crate::errors::HgError;
2 use crate::errors::HgError;
3 use bitflags::bitflags;
3 use bitflags::bitflags;
4 use std::convert::{TryFrom, TryInto};
4 use std::convert::{TryFrom, TryInto};
5 use std::fs;
5 use std::fs;
6 use std::io;
6 use std::io;
7 use std::time::{SystemTime, UNIX_EPOCH};
7 use std::time::{SystemTime, UNIX_EPOCH};
8
8
9 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
9 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
10 pub enum EntryState {
10 pub enum EntryState {
11 Normal,
11 Normal,
12 Added,
12 Added,
13 Removed,
13 Removed,
14 Merged,
14 Merged,
15 }
15 }
16
16
17 /// `size` and `mtime.seconds` are truncated to 31 bits.
17 /// `size` and `mtime.seconds` are truncated to 31 bits.
18 ///
18 ///
19 /// TODO: double-check status algorithm correctness for files
19 /// TODO: double-check status algorithm correctness for files
20 /// larger than 2 GiB or modified after 2038.
20 /// larger than 2 GiB or modified after 2038.
21 #[derive(Debug, Copy, Clone)]
21 #[derive(Debug, Copy, Clone)]
22 pub struct DirstateEntry {
22 pub struct DirstateEntry {
23 pub(crate) flags: Flags,
23 pub(crate) flags: Flags,
24 mode_size: Option<(u32, u32)>,
24 mode_size: Option<(u32, u32)>,
25 mtime: Option<TruncatedTimestamp>,
25 mtime: Option<TruncatedTimestamp>,
26 }
26 }
27
27
28 bitflags! {
28 bitflags! {
29 pub(crate) struct Flags: u8 {
29 pub(crate) struct Flags: u8 {
30 const WDIR_TRACKED = 1 << 0;
30 const WDIR_TRACKED = 1 << 0;
31 const P1_TRACKED = 1 << 1;
31 const P1_TRACKED = 1 << 1;
32 const P2_INFO = 1 << 2;
32 const P2_INFO = 1 << 2;
33 const HAS_FALLBACK_EXEC = 1 << 3;
33 const HAS_FALLBACK_EXEC = 1 << 3;
34 const FALLBACK_EXEC = 1 << 4;
34 const FALLBACK_EXEC = 1 << 4;
35 const HAS_FALLBACK_SYMLINK = 1 << 5;
35 const HAS_FALLBACK_SYMLINK = 1 << 5;
36 const FALLBACK_SYMLINK = 1 << 6;
36 const FALLBACK_SYMLINK = 1 << 6;
37 }
37 }
38 }
38 }
39
39
40 /// A Unix timestamp with nanoseconds precision
40 /// A Unix timestamp with nanoseconds precision
41 #[derive(Debug, Copy, Clone)]
41 #[derive(Debug, Copy, Clone)]
42 pub struct TruncatedTimestamp {
42 pub struct TruncatedTimestamp {
43 truncated_seconds: u32,
43 truncated_seconds: u32,
44 /// Always in the `0 .. 1_000_000_000` range.
44 /// Always in the `0 .. 1_000_000_000` range.
45 nanoseconds: u32,
45 nanoseconds: u32,
46 /// TODO this should be in DirstateEntry, but the current code needs
46 /// TODO this should be in DirstateEntry, but the current code needs
47 /// refactoring to use DirstateEntry instead of TruncatedTimestamp for
47 /// refactoring to use DirstateEntry instead of TruncatedTimestamp for
48 /// comparison.
48 /// comparison.
49 pub second_ambiguous: bool,
49 pub second_ambiguous: bool,
50 }
50 }
51
51
52 impl TruncatedTimestamp {
52 impl TruncatedTimestamp {
53 /// Constructs from a timestamp potentially outside of the supported range,
53 /// Constructs from a timestamp potentially outside of the supported range,
54 /// and truncate the seconds components to its lower 31 bits.
54 /// and truncate the seconds components to its lower 31 bits.
55 ///
55 ///
56 /// Panics if the nanoseconds components is not in the expected range.
56 /// Panics if the nanoseconds components is not in the expected range.
57 pub fn new_truncate(
57 pub fn new_truncate(
58 seconds: i64,
58 seconds: i64,
59 nanoseconds: u32,
59 nanoseconds: u32,
60 second_ambiguous: bool,
60 second_ambiguous: bool,
61 ) -> Self {
61 ) -> Self {
62 assert!(nanoseconds < NSEC_PER_SEC);
62 assert!(nanoseconds < NSEC_PER_SEC);
63 Self {
63 Self {
64 truncated_seconds: seconds as u32 & RANGE_MASK_31BIT,
64 truncated_seconds: seconds as u32 & RANGE_MASK_31BIT,
65 nanoseconds,
65 nanoseconds,
66 second_ambiguous,
66 second_ambiguous,
67 }
67 }
68 }
68 }
69
69
70 /// Construct from components. Returns an error if they are not in the
70 /// Construct from components. Returns an error if they are not in the
71 /// expcted range.
71 /// expcted range.
72 pub fn from_already_truncated(
72 pub fn from_already_truncated(
73 truncated_seconds: u32,
73 truncated_seconds: u32,
74 nanoseconds: u32,
74 nanoseconds: u32,
75 second_ambiguous: bool,
75 second_ambiguous: bool,
76 ) -> Result<Self, DirstateV2ParseError> {
76 ) -> Result<Self, DirstateV2ParseError> {
77 if truncated_seconds & !RANGE_MASK_31BIT == 0
77 if truncated_seconds & !RANGE_MASK_31BIT == 0
78 && nanoseconds < NSEC_PER_SEC
78 && nanoseconds < NSEC_PER_SEC
79 {
79 {
80 Ok(Self {
80 Ok(Self {
81 truncated_seconds,
81 truncated_seconds,
82 nanoseconds,
82 nanoseconds,
83 second_ambiguous,
83 second_ambiguous,
84 })
84 })
85 } else {
85 } else {
86 Err(DirstateV2ParseError)
86 Err(DirstateV2ParseError)
87 }
87 }
88 }
88 }
89
89
90 /// Returns a `TruncatedTimestamp` for the modification time of `metadata`.
90 /// Returns a `TruncatedTimestamp` for the modification time of `metadata`.
91 ///
91 ///
92 /// Propagates errors from `std` on platforms where modification time
92 /// Propagates errors from `std` on platforms where modification time
93 /// is not available at all.
93 /// is not available at all.
94 pub fn for_mtime_of(metadata: &fs::Metadata) -> io::Result<Self> {
94 pub fn for_mtime_of(metadata: &fs::Metadata) -> io::Result<Self> {
95 #[cfg(unix)]
95 #[cfg(unix)]
96 {
96 {
97 use std::os::unix::fs::MetadataExt;
97 use std::os::unix::fs::MetadataExt;
98 let seconds = metadata.mtime();
98 let seconds = metadata.mtime();
99 // i64 -> u32 with value always in the `0 .. NSEC_PER_SEC` range
99 // i64 -> u32 with value always in the `0 .. NSEC_PER_SEC` range
100 let nanoseconds = metadata.mtime_nsec().try_into().unwrap();
100 let nanoseconds = metadata.mtime_nsec().try_into().unwrap();
101 Ok(Self::new_truncate(seconds, nanoseconds, false))
101 Ok(Self::new_truncate(seconds, nanoseconds, false))
102 }
102 }
103 #[cfg(not(unix))]
103 #[cfg(not(unix))]
104 {
104 {
105 metadata.modified().map(Self::from)
105 metadata.modified().map(Self::from)
106 }
106 }
107 }
107 }
108
108
109 /// Like `for_mtime_of`, but may return `None` or a value with
109 /// Like `for_mtime_of`, but may return `None` or a value with
110 /// `second_ambiguous` set if the mtime is not "reliable".
110 /// `second_ambiguous` set if the mtime is not "reliable".
111 ///
111 ///
112 /// A modification time is reliable if it is older than `boundary` (or
112 /// A modification time is reliable if it is older than `boundary` (or
113 /// sufficiently in the future).
113 /// sufficiently in the future).
114 ///
114 ///
115 /// Otherwise a concurrent modification might happens with the same mtime.
115 /// Otherwise a concurrent modification might happens with the same mtime.
116 pub fn for_reliable_mtime_of(
116 pub fn for_reliable_mtime_of(
117 metadata: &fs::Metadata,
117 metadata: &fs::Metadata,
118 boundary: &Self,
118 boundary: &Self,
119 ) -> io::Result<Option<Self>> {
119 ) -> io::Result<Option<Self>> {
120 let mut mtime = Self::for_mtime_of(metadata)?;
120 let mut mtime = Self::for_mtime_of(metadata)?;
121 // If the mtime of the ambiguous file is younger (or equal) to the
121 // If the mtime of the ambiguous file is younger (or equal) to the
122 // starting point of the `status` walk, we cannot garantee that
122 // starting point of the `status` walk, we cannot garantee that
123 // another, racy, write will not happen right after with the same mtime
123 // another, racy, write will not happen right after with the same mtime
124 // and we cannot cache the information.
124 // and we cannot cache the information.
125 //
125 //
126 // However if the mtime is far away in the future, this is likely some
126 // However if the mtime is far away in the future, this is likely some
127 // mismatch between the current clock and previous file system
127 // mismatch between the current clock and previous file system
128 // operation. So mtime more than one days in the future are considered
128 // operation. So mtime more than one days in the future are considered
129 // fine.
129 // fine.
130 let reliable = if mtime.truncated_seconds == boundary.truncated_seconds
130 let reliable = if mtime.truncated_seconds == boundary.truncated_seconds
131 {
131 {
132 mtime.second_ambiguous = true;
132 mtime.second_ambiguous = true;
133 mtime.nanoseconds != 0
133 mtime.nanoseconds != 0
134 && boundary.nanoseconds != 0
134 && boundary.nanoseconds != 0
135 && mtime.nanoseconds < boundary.nanoseconds
135 && mtime.nanoseconds < boundary.nanoseconds
136 } else {
136 } else {
137 // `truncated_seconds` is less than 2**31,
137 // `truncated_seconds` is less than 2**31,
138 // so this does not overflow `u32`:
138 // so this does not overflow `u32`:
139 let one_day_later = boundary.truncated_seconds + 24 * 3600;
139 let one_day_later = boundary.truncated_seconds + 24 * 3600;
140 mtime.truncated_seconds < boundary.truncated_seconds
140 mtime.truncated_seconds < boundary.truncated_seconds
141 || mtime.truncated_seconds > one_day_later
141 || mtime.truncated_seconds > one_day_later
142 };
142 };
143 if reliable {
143 if reliable {
144 Ok(Some(mtime))
144 Ok(Some(mtime))
145 } else {
145 } else {
146 Ok(None)
146 Ok(None)
147 }
147 }
148 }
148 }
149
149
150 /// The lower 31 bits of the number of seconds since the epoch.
150 /// The lower 31 bits of the number of seconds since the epoch.
151 pub fn truncated_seconds(&self) -> u32 {
151 pub fn truncated_seconds(&self) -> u32 {
152 self.truncated_seconds
152 self.truncated_seconds
153 }
153 }
154
154
155 /// The sub-second component of this timestamp, in nanoseconds.
155 /// The sub-second component of this timestamp, in nanoseconds.
156 /// Always in the `0 .. 1_000_000_000` range.
156 /// Always in the `0 .. 1_000_000_000` range.
157 ///
157 ///
158 /// This timestamp is after `(seconds, 0)` by this many nanoseconds.
158 /// This timestamp is after `(seconds, 0)` by this many nanoseconds.
159 pub fn nanoseconds(&self) -> u32 {
159 pub fn nanoseconds(&self) -> u32 {
160 self.nanoseconds
160 self.nanoseconds
161 }
161 }
162
162
163 /// Returns whether two timestamps are equal modulo 2**31 seconds.
163 /// Returns whether two timestamps are equal modulo 2**31 seconds.
164 ///
164 ///
165 /// If this returns `true`, the original values converted from `SystemTime`
165 /// If this returns `true`, the original values converted from `SystemTime`
166 /// or given to `new_truncate` were very likely equal. A false positive is
166 /// or given to `new_truncate` were very likely equal. A false positive is
167 /// possible if they were exactly a multiple of 2**31 seconds apart (around
167 /// possible if they were exactly a multiple of 2**31 seconds apart (around
168 /// 68 years). This is deemed very unlikely to happen by chance, especially
168 /// 68 years). This is deemed very unlikely to happen by chance, especially
169 /// on filesystems that support sub-second precision.
169 /// on filesystems that support sub-second precision.
170 ///
170 ///
171 /// If someone is manipulating the modification times of some files to
171 /// If someone is manipulating the modification times of some files to
172 /// intentionally make `hg status` return incorrect results, not truncating
172 /// intentionally make `hg status` return incorrect results, not truncating
173 /// wouldn’t help much since they can set exactly the expected timestamp.
173 /// wouldn’t help much since they can set exactly the expected timestamp.
174 ///
174 ///
175 /// Sub-second precision is ignored if it is zero in either value.
175 /// Sub-second precision is ignored if it is zero in either value.
176 /// Some APIs simply return zero when more precision is not available.
176 /// Some APIs simply return zero when more precision is not available.
177 /// When comparing values from different sources, if only one is truncated
177 /// When comparing values from different sources, if only one is truncated
178 /// in that way, doing a simple comparison would cause many false
178 /// in that way, doing a simple comparison would cause many false
179 /// negatives.
179 /// negatives.
180 pub fn likely_equal(self, other: Self) -> bool {
180 pub fn likely_equal(self, other: Self) -> bool {
181 if self.truncated_seconds != other.truncated_seconds {
181 if self.truncated_seconds != other.truncated_seconds {
182 false
182 false
183 } else if self.nanoseconds == 0 || other.nanoseconds == 0 {
183 } else if self.nanoseconds == 0 || other.nanoseconds == 0 {
184 if self.second_ambiguous {
184 if self.second_ambiguous {
185 false
185 false
186 } else {
186 } else {
187 true
187 true
188 }
188 }
189 } else {
189 } else {
190 self.nanoseconds == other.nanoseconds
190 self.nanoseconds == other.nanoseconds
191 }
191 }
192 }
192 }
193
193
194 pub fn likely_equal_to_mtime_of(
194 pub fn likely_equal_to_mtime_of(
195 self,
195 self,
196 metadata: &fs::Metadata,
196 metadata: &fs::Metadata,
197 ) -> io::Result<bool> {
197 ) -> io::Result<bool> {
198 Ok(self.likely_equal(Self::for_mtime_of(metadata)?))
198 Ok(self.likely_equal(Self::for_mtime_of(metadata)?))
199 }
199 }
200 }
200 }
201
201
202 impl From<SystemTime> for TruncatedTimestamp {
202 impl From<SystemTime> for TruncatedTimestamp {
203 fn from(system_time: SystemTime) -> Self {
203 fn from(system_time: SystemTime) -> Self {
204 // On Unix, `SystemTime` is a wrapper for the `timespec` C struct:
204 // On Unix, `SystemTime` is a wrapper for the `timespec` C struct:
205 // https://www.gnu.org/software/libc/manual/html_node/Time-Types.html#index-struct-timespec
205 // https://www.gnu.org/software/libc/manual/html_node/Time-Types.html#index-struct-timespec
206 // We want to effectively access its fields, but the Rust standard
206 // We want to effectively access its fields, but the Rust standard
207 // library does not expose them. The best we can do is:
207 // library does not expose them. The best we can do is:
208 let seconds;
208 let seconds;
209 let nanoseconds;
209 let nanoseconds;
210 match system_time.duration_since(UNIX_EPOCH) {
210 match system_time.duration_since(UNIX_EPOCH) {
211 Ok(duration) => {
211 Ok(duration) => {
212 seconds = duration.as_secs() as i64;
212 seconds = duration.as_secs() as i64;
213 nanoseconds = duration.subsec_nanos();
213 nanoseconds = duration.subsec_nanos();
214 }
214 }
215 Err(error) => {
215 Err(error) => {
216 // `system_time` is before `UNIX_EPOCH`.
216 // `system_time` is before `UNIX_EPOCH`.
217 // We need to undo this algorithm:
217 // We need to undo this algorithm:
218 // https://github.com/rust-lang/rust/blob/6bed1f0bc3cc50c10aab26d5f94b16a00776b8a5/library/std/src/sys/unix/time.rs#L40-L41
218 // https://github.com/rust-lang/rust/blob/6bed1f0bc3cc50c10aab26d5f94b16a00776b8a5/library/std/src/sys/unix/time.rs#L40-L41
219 let negative = error.duration();
219 let negative = error.duration();
220 let negative_secs = negative.as_secs() as i64;
220 let negative_secs = negative.as_secs() as i64;
221 let negative_nanos = negative.subsec_nanos();
221 let negative_nanos = negative.subsec_nanos();
222 if negative_nanos == 0 {
222 if negative_nanos == 0 {
223 seconds = -negative_secs;
223 seconds = -negative_secs;
224 nanoseconds = 0;
224 nanoseconds = 0;
225 } else {
225 } else {
226 // For example if `system_time` was 4.3 seconds before
226 // For example if `system_time` was 4.3 seconds before
227 // the Unix epoch we get a Duration that represents
227 // the Unix epoch we get a Duration that represents
228 // `(-4, -0.3)` but we want `(-5, +0.7)`:
228 // `(-4, -0.3)` but we want `(-5, +0.7)`:
229 seconds = -1 - negative_secs;
229 seconds = -1 - negative_secs;
230 nanoseconds = NSEC_PER_SEC - negative_nanos;
230 nanoseconds = NSEC_PER_SEC - negative_nanos;
231 }
231 }
232 }
232 }
233 };
233 };
234 Self::new_truncate(seconds, nanoseconds, false)
234 Self::new_truncate(seconds, nanoseconds, false)
235 }
235 }
236 }
236 }
237
237
238 const NSEC_PER_SEC: u32 = 1_000_000_000;
238 const NSEC_PER_SEC: u32 = 1_000_000_000;
239 pub const RANGE_MASK_31BIT: u32 = 0x7FFF_FFFF;
239 pub const RANGE_MASK_31BIT: u32 = 0x7FFF_FFFF;
240
240
241 pub const MTIME_UNSET: i32 = -1;
241 pub const MTIME_UNSET: i32 = -1;
242
242
243 /// A `DirstateEntry` with a size of `-2` means that it was merged from the
243 /// A `DirstateEntry` with a size of `-2` means that it was merged from the
244 /// other parent. This allows revert to pick the right status back during a
244 /// other parent. This allows revert to pick the right status back during a
245 /// merge.
245 /// merge.
246 pub const SIZE_FROM_OTHER_PARENT: i32 = -2;
246 pub const SIZE_FROM_OTHER_PARENT: i32 = -2;
247 /// A special value used for internal representation of special case in
247 /// A special value used for internal representation of special case in
248 /// dirstate v1 format.
248 /// dirstate v1 format.
249 pub const SIZE_NON_NORMAL: i32 = -1;
249 pub const SIZE_NON_NORMAL: i32 = -1;
250
250
251 #[derive(Debug, Default, Copy, Clone)]
251 #[derive(Debug, Default, Copy, Clone)]
252 pub struct DirstateV2Data {
252 pub struct DirstateV2Data {
253 pub wc_tracked: bool,
253 pub wc_tracked: bool,
254 pub p1_tracked: bool,
254 pub p1_tracked: bool,
255 pub p2_info: bool,
255 pub p2_info: bool,
256 pub mode_size: Option<(u32, u32)>,
256 pub mode_size: Option<(u32, u32)>,
257 pub mtime: Option<TruncatedTimestamp>,
257 pub mtime: Option<TruncatedTimestamp>,
258 pub fallback_exec: Option<bool>,
258 pub fallback_exec: Option<bool>,
259 pub fallback_symlink: Option<bool>,
259 pub fallback_symlink: Option<bool>,
260 }
260 }
261
261
262 #[derive(Debug, Default, Copy, Clone)]
263 pub struct ParentFileData {
264 pub mode_size: Option<(u32, u32)>,
265 pub mtime: Option<TruncatedTimestamp>,
266 }
267
262 impl DirstateEntry {
268 impl DirstateEntry {
263 pub fn from_v2_data(v2_data: DirstateV2Data) -> Self {
269 pub fn from_v2_data(v2_data: DirstateV2Data) -> Self {
264 let DirstateV2Data {
270 let DirstateV2Data {
265 wc_tracked,
271 wc_tracked,
266 p1_tracked,
272 p1_tracked,
267 p2_info,
273 p2_info,
268 mode_size,
274 mode_size,
269 mtime,
275 mtime,
270 fallback_exec,
276 fallback_exec,
271 fallback_symlink,
277 fallback_symlink,
272 } = v2_data;
278 } = v2_data;
273 if let Some((mode, size)) = mode_size {
279 if let Some((mode, size)) = mode_size {
274 // TODO: return an error for out of range values?
280 // TODO: return an error for out of range values?
275 assert!(mode & !RANGE_MASK_31BIT == 0);
281 assert!(mode & !RANGE_MASK_31BIT == 0);
276 assert!(size & !RANGE_MASK_31BIT == 0);
282 assert!(size & !RANGE_MASK_31BIT == 0);
277 }
283 }
278 let mut flags = Flags::empty();
284 let mut flags = Flags::empty();
279 flags.set(Flags::WDIR_TRACKED, wc_tracked);
285 flags.set(Flags::WDIR_TRACKED, wc_tracked);
280 flags.set(Flags::P1_TRACKED, p1_tracked);
286 flags.set(Flags::P1_TRACKED, p1_tracked);
281 flags.set(Flags::P2_INFO, p2_info);
287 flags.set(Flags::P2_INFO, p2_info);
282 if let Some(exec) = fallback_exec {
288 if let Some(exec) = fallback_exec {
283 flags.insert(Flags::HAS_FALLBACK_EXEC);
289 flags.insert(Flags::HAS_FALLBACK_EXEC);
284 if exec {
290 if exec {
285 flags.insert(Flags::FALLBACK_EXEC);
291 flags.insert(Flags::FALLBACK_EXEC);
286 }
292 }
287 }
293 }
288 if let Some(exec) = fallback_symlink {
294 if let Some(exec) = fallback_symlink {
289 flags.insert(Flags::HAS_FALLBACK_SYMLINK);
295 flags.insert(Flags::HAS_FALLBACK_SYMLINK);
290 if exec {
296 if exec {
291 flags.insert(Flags::FALLBACK_SYMLINK);
297 flags.insert(Flags::FALLBACK_SYMLINK);
292 }
298 }
293 }
299 }
294 Self {
300 Self {
295 flags,
301 flags,
296 mode_size,
302 mode_size,
297 mtime,
303 mtime,
298 }
304 }
299 }
305 }
300
306
301 pub fn from_v1_data(
307 pub fn from_v1_data(
302 state: EntryState,
308 state: EntryState,
303 mode: i32,
309 mode: i32,
304 size: i32,
310 size: i32,
305 mtime: i32,
311 mtime: i32,
306 ) -> Self {
312 ) -> Self {
307 match state {
313 match state {
308 EntryState::Normal => {
314 EntryState::Normal => {
309 if size == SIZE_FROM_OTHER_PARENT {
315 if size == SIZE_FROM_OTHER_PARENT {
310 Self {
316 Self {
311 // might be missing P1_TRACKED
317 // might be missing P1_TRACKED
312 flags: Flags::WDIR_TRACKED | Flags::P2_INFO,
318 flags: Flags::WDIR_TRACKED | Flags::P2_INFO,
313 mode_size: None,
319 mode_size: None,
314 mtime: None,
320 mtime: None,
315 }
321 }
316 } else if size == SIZE_NON_NORMAL {
322 } else if size == SIZE_NON_NORMAL {
317 Self {
323 Self {
318 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
324 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
319 mode_size: None,
325 mode_size: None,
320 mtime: None,
326 mtime: None,
321 }
327 }
322 } else if mtime == MTIME_UNSET {
328 } else if mtime == MTIME_UNSET {
323 // TODO: return an error for negative values?
329 // TODO: return an error for negative values?
324 let mode = u32::try_from(mode).unwrap();
330 let mode = u32::try_from(mode).unwrap();
325 let size = u32::try_from(size).unwrap();
331 let size = u32::try_from(size).unwrap();
326 Self {
332 Self {
327 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
333 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
328 mode_size: Some((mode, size)),
334 mode_size: Some((mode, size)),
329 mtime: None,
335 mtime: None,
330 }
336 }
331 } else {
337 } else {
332 // TODO: return an error for negative values?
338 // TODO: return an error for negative values?
333 let mode = u32::try_from(mode).unwrap();
339 let mode = u32::try_from(mode).unwrap();
334 let size = u32::try_from(size).unwrap();
340 let size = u32::try_from(size).unwrap();
335 let mtime = u32::try_from(mtime).unwrap();
341 let mtime = u32::try_from(mtime).unwrap();
336 let mtime = TruncatedTimestamp::from_already_truncated(
342 let mtime = TruncatedTimestamp::from_already_truncated(
337 mtime, 0, false,
343 mtime, 0, false,
338 )
344 )
339 .unwrap();
345 .unwrap();
340 Self {
346 Self {
341 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
347 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
342 mode_size: Some((mode, size)),
348 mode_size: Some((mode, size)),
343 mtime: Some(mtime),
349 mtime: Some(mtime),
344 }
350 }
345 }
351 }
346 }
352 }
347 EntryState::Added => Self {
353 EntryState::Added => Self {
348 flags: Flags::WDIR_TRACKED,
354 flags: Flags::WDIR_TRACKED,
349 mode_size: None,
355 mode_size: None,
350 mtime: None,
356 mtime: None,
351 },
357 },
352 EntryState::Removed => Self {
358 EntryState::Removed => Self {
353 flags: if size == SIZE_NON_NORMAL {
359 flags: if size == SIZE_NON_NORMAL {
354 Flags::P1_TRACKED | Flags::P2_INFO
360 Flags::P1_TRACKED | Flags::P2_INFO
355 } else if size == SIZE_FROM_OTHER_PARENT {
361 } else if size == SIZE_FROM_OTHER_PARENT {
356 // We don’t know if P1_TRACKED should be set (file history)
362 // We don’t know if P1_TRACKED should be set (file history)
357 Flags::P2_INFO
363 Flags::P2_INFO
358 } else {
364 } else {
359 Flags::P1_TRACKED
365 Flags::P1_TRACKED
360 },
366 },
361 mode_size: None,
367 mode_size: None,
362 mtime: None,
368 mtime: None,
363 },
369 },
364 EntryState::Merged => Self {
370 EntryState::Merged => Self {
365 flags: Flags::WDIR_TRACKED
371 flags: Flags::WDIR_TRACKED
366 | Flags::P1_TRACKED // might not be true because of rename ?
372 | Flags::P1_TRACKED // might not be true because of rename ?
367 | Flags::P2_INFO, // might not be true because of rename ?
373 | Flags::P2_INFO, // might not be true because of rename ?
368 mode_size: None,
374 mode_size: None,
369 mtime: None,
375 mtime: None,
370 },
376 },
371 }
377 }
372 }
378 }
373
379
374 /// Creates a new entry in "removed" state.
380 /// Creates a new entry in "removed" state.
375 ///
381 ///
376 /// `size` is expected to be zero, `SIZE_NON_NORMAL`, or
382 /// `size` is expected to be zero, `SIZE_NON_NORMAL`, or
377 /// `SIZE_FROM_OTHER_PARENT`
383 /// `SIZE_FROM_OTHER_PARENT`
378 pub fn new_removed(size: i32) -> Self {
384 pub fn new_removed(size: i32) -> Self {
379 Self::from_v1_data(EntryState::Removed, 0, size, 0)
385 Self::from_v1_data(EntryState::Removed, 0, size, 0)
380 }
386 }
381
387
382 pub fn new_tracked() -> Self {
388 pub fn new_tracked() -> Self {
383 let data = DirstateV2Data {
389 let data = DirstateV2Data {
384 wc_tracked: true,
390 wc_tracked: true,
385 ..Default::default()
391 ..Default::default()
386 };
392 };
387 Self::from_v2_data(data)
393 Self::from_v2_data(data)
388 }
394 }
389
395
390 pub fn tracked(&self) -> bool {
396 pub fn tracked(&self) -> bool {
391 self.flags.contains(Flags::WDIR_TRACKED)
397 self.flags.contains(Flags::WDIR_TRACKED)
392 }
398 }
393
399
394 pub fn p1_tracked(&self) -> bool {
400 pub fn p1_tracked(&self) -> bool {
395 self.flags.contains(Flags::P1_TRACKED)
401 self.flags.contains(Flags::P1_TRACKED)
396 }
402 }
397
403
398 fn in_either_parent(&self) -> bool {
404 fn in_either_parent(&self) -> bool {
399 self.flags.intersects(Flags::P1_TRACKED | Flags::P2_INFO)
405 self.flags.intersects(Flags::P1_TRACKED | Flags::P2_INFO)
400 }
406 }
401
407
402 pub fn removed(&self) -> bool {
408 pub fn removed(&self) -> bool {
403 self.in_either_parent() && !self.flags.contains(Flags::WDIR_TRACKED)
409 self.in_either_parent() && !self.flags.contains(Flags::WDIR_TRACKED)
404 }
410 }
405
411
406 pub fn p2_info(&self) -> bool {
412 pub fn p2_info(&self) -> bool {
407 self.flags.contains(Flags::WDIR_TRACKED | Flags::P2_INFO)
413 self.flags.contains(Flags::WDIR_TRACKED | Flags::P2_INFO)
408 }
414 }
409
415
410 pub fn added(&self) -> bool {
416 pub fn added(&self) -> bool {
411 self.flags.contains(Flags::WDIR_TRACKED) && !self.in_either_parent()
417 self.flags.contains(Flags::WDIR_TRACKED) && !self.in_either_parent()
412 }
418 }
413
419
414 pub fn maybe_clean(&self) -> bool {
420 pub fn maybe_clean(&self) -> bool {
415 if !self.flags.contains(Flags::WDIR_TRACKED) {
421 if !self.flags.contains(Flags::WDIR_TRACKED) {
416 false
422 false
417 } else if !self.flags.contains(Flags::P1_TRACKED) {
423 } else if !self.flags.contains(Flags::P1_TRACKED) {
418 false
424 false
419 } else if self.flags.contains(Flags::P2_INFO) {
425 } else if self.flags.contains(Flags::P2_INFO) {
420 false
426 false
421 } else {
427 } else {
422 true
428 true
423 }
429 }
424 }
430 }
425
431
426 pub fn any_tracked(&self) -> bool {
432 pub fn any_tracked(&self) -> bool {
427 self.flags.intersects(
433 self.flags.intersects(
428 Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_INFO,
434 Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_INFO,
429 )
435 )
430 }
436 }
431
437
432 pub(crate) fn v2_data(&self) -> DirstateV2Data {
438 pub(crate) fn v2_data(&self) -> DirstateV2Data {
433 if !self.any_tracked() {
439 if !self.any_tracked() {
434 // TODO: return an Option instead?
440 // TODO: return an Option instead?
435 panic!("Accessing v2_data of an untracked DirstateEntry")
441 panic!("Accessing v2_data of an untracked DirstateEntry")
436 }
442 }
437 let wc_tracked = self.flags.contains(Flags::WDIR_TRACKED);
443 let wc_tracked = self.flags.contains(Flags::WDIR_TRACKED);
438 let p1_tracked = self.flags.contains(Flags::P1_TRACKED);
444 let p1_tracked = self.flags.contains(Flags::P1_TRACKED);
439 let p2_info = self.flags.contains(Flags::P2_INFO);
445 let p2_info = self.flags.contains(Flags::P2_INFO);
440 let mode_size = self.mode_size;
446 let mode_size = self.mode_size;
441 let mtime = self.mtime;
447 let mtime = self.mtime;
442 DirstateV2Data {
448 DirstateV2Data {
443 wc_tracked,
449 wc_tracked,
444 p1_tracked,
450 p1_tracked,
445 p2_info,
451 p2_info,
446 mode_size,
452 mode_size,
447 mtime,
453 mtime,
448 fallback_exec: self.get_fallback_exec(),
454 fallback_exec: self.get_fallback_exec(),
449 fallback_symlink: self.get_fallback_symlink(),
455 fallback_symlink: self.get_fallback_symlink(),
450 }
456 }
451 }
457 }
452
458
453 fn v1_state(&self) -> EntryState {
459 fn v1_state(&self) -> EntryState {
454 if !self.any_tracked() {
460 if !self.any_tracked() {
455 // TODO: return an Option instead?
461 // TODO: return an Option instead?
456 panic!("Accessing v1_state of an untracked DirstateEntry")
462 panic!("Accessing v1_state of an untracked DirstateEntry")
457 }
463 }
458 if self.removed() {
464 if self.removed() {
459 EntryState::Removed
465 EntryState::Removed
460 } else if self
466 } else if self
461 .flags
467 .flags
462 .contains(Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_INFO)
468 .contains(Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_INFO)
463 {
469 {
464 EntryState::Merged
470 EntryState::Merged
465 } else if self.added() {
471 } else if self.added() {
466 EntryState::Added
472 EntryState::Added
467 } else {
473 } else {
468 EntryState::Normal
474 EntryState::Normal
469 }
475 }
470 }
476 }
471
477
472 fn v1_mode(&self) -> i32 {
478 fn v1_mode(&self) -> i32 {
473 if let Some((mode, _size)) = self.mode_size {
479 if let Some((mode, _size)) = self.mode_size {
474 i32::try_from(mode).unwrap()
480 i32::try_from(mode).unwrap()
475 } else {
481 } else {
476 0
482 0
477 }
483 }
478 }
484 }
479
485
480 fn v1_size(&self) -> i32 {
486 fn v1_size(&self) -> i32 {
481 if !self.any_tracked() {
487 if !self.any_tracked() {
482 // TODO: return an Option instead?
488 // TODO: return an Option instead?
483 panic!("Accessing v1_size of an untracked DirstateEntry")
489 panic!("Accessing v1_size of an untracked DirstateEntry")
484 }
490 }
485 if self.removed()
491 if self.removed()
486 && self.flags.contains(Flags::P1_TRACKED | Flags::P2_INFO)
492 && self.flags.contains(Flags::P1_TRACKED | Flags::P2_INFO)
487 {
493 {
488 SIZE_NON_NORMAL
494 SIZE_NON_NORMAL
489 } else if self.flags.contains(Flags::P2_INFO) {
495 } else if self.flags.contains(Flags::P2_INFO) {
490 SIZE_FROM_OTHER_PARENT
496 SIZE_FROM_OTHER_PARENT
491 } else if self.removed() {
497 } else if self.removed() {
492 0
498 0
493 } else if self.added() {
499 } else if self.added() {
494 SIZE_NON_NORMAL
500 SIZE_NON_NORMAL
495 } else if let Some((_mode, size)) = self.mode_size {
501 } else if let Some((_mode, size)) = self.mode_size {
496 i32::try_from(size).unwrap()
502 i32::try_from(size).unwrap()
497 } else {
503 } else {
498 SIZE_NON_NORMAL
504 SIZE_NON_NORMAL
499 }
505 }
500 }
506 }
501
507
502 fn v1_mtime(&self) -> i32 {
508 fn v1_mtime(&self) -> i32 {
503 if !self.any_tracked() {
509 if !self.any_tracked() {
504 // TODO: return an Option instead?
510 // TODO: return an Option instead?
505 panic!("Accessing v1_mtime of an untracked DirstateEntry")
511 panic!("Accessing v1_mtime of an untracked DirstateEntry")
506 }
512 }
507 if self.removed() {
513 if self.removed() {
508 0
514 0
509 } else if self.flags.contains(Flags::P2_INFO) {
515 } else if self.flags.contains(Flags::P2_INFO) {
510 MTIME_UNSET
516 MTIME_UNSET
511 } else if !self.flags.contains(Flags::P1_TRACKED) {
517 } else if !self.flags.contains(Flags::P1_TRACKED) {
512 MTIME_UNSET
518 MTIME_UNSET
513 } else if let Some(mtime) = self.mtime {
519 } else if let Some(mtime) = self.mtime {
514 if mtime.second_ambiguous {
520 if mtime.second_ambiguous {
515 MTIME_UNSET
521 MTIME_UNSET
516 } else {
522 } else {
517 i32::try_from(mtime.truncated_seconds()).unwrap()
523 i32::try_from(mtime.truncated_seconds()).unwrap()
518 }
524 }
519 } else {
525 } else {
520 MTIME_UNSET
526 MTIME_UNSET
521 }
527 }
522 }
528 }
523
529
524 // TODO: return `Option<EntryState>`? None when `!self.any_tracked`
530 // TODO: return `Option<EntryState>`? None when `!self.any_tracked`
525 pub fn state(&self) -> EntryState {
531 pub fn state(&self) -> EntryState {
526 self.v1_state()
532 self.v1_state()
527 }
533 }
528
534
529 // TODO: return Option?
535 // TODO: return Option?
530 pub fn mode(&self) -> i32 {
536 pub fn mode(&self) -> i32 {
531 self.v1_mode()
537 self.v1_mode()
532 }
538 }
533
539
534 // TODO: return Option?
540 // TODO: return Option?
535 pub fn size(&self) -> i32 {
541 pub fn size(&self) -> i32 {
536 self.v1_size()
542 self.v1_size()
537 }
543 }
538
544
539 // TODO: return Option?
545 // TODO: return Option?
540 pub fn mtime(&self) -> i32 {
546 pub fn mtime(&self) -> i32 {
541 self.v1_mtime()
547 self.v1_mtime()
542 }
548 }
543
549
544 pub fn get_fallback_exec(&self) -> Option<bool> {
550 pub fn get_fallback_exec(&self) -> Option<bool> {
545 if self.flags.contains(Flags::HAS_FALLBACK_EXEC) {
551 if self.flags.contains(Flags::HAS_FALLBACK_EXEC) {
546 Some(self.flags.contains(Flags::FALLBACK_EXEC))
552 Some(self.flags.contains(Flags::FALLBACK_EXEC))
547 } else {
553 } else {
548 None
554 None
549 }
555 }
550 }
556 }
551
557
552 pub fn set_fallback_exec(&mut self, value: Option<bool>) {
558 pub fn set_fallback_exec(&mut self, value: Option<bool>) {
553 match value {
559 match value {
554 None => {
560 None => {
555 self.flags.remove(Flags::HAS_FALLBACK_EXEC);
561 self.flags.remove(Flags::HAS_FALLBACK_EXEC);
556 self.flags.remove(Flags::FALLBACK_EXEC);
562 self.flags.remove(Flags::FALLBACK_EXEC);
557 }
563 }
558 Some(exec) => {
564 Some(exec) => {
559 self.flags.insert(Flags::HAS_FALLBACK_EXEC);
565 self.flags.insert(Flags::HAS_FALLBACK_EXEC);
560 if exec {
566 if exec {
561 self.flags.insert(Flags::FALLBACK_EXEC);
567 self.flags.insert(Flags::FALLBACK_EXEC);
562 }
568 }
563 }
569 }
564 }
570 }
565 }
571 }
566
572
567 pub fn get_fallback_symlink(&self) -> Option<bool> {
573 pub fn get_fallback_symlink(&self) -> Option<bool> {
568 if self.flags.contains(Flags::HAS_FALLBACK_SYMLINK) {
574 if self.flags.contains(Flags::HAS_FALLBACK_SYMLINK) {
569 Some(self.flags.contains(Flags::FALLBACK_SYMLINK))
575 Some(self.flags.contains(Flags::FALLBACK_SYMLINK))
570 } else {
576 } else {
571 None
577 None
572 }
578 }
573 }
579 }
574
580
575 pub fn set_fallback_symlink(&mut self, value: Option<bool>) {
581 pub fn set_fallback_symlink(&mut self, value: Option<bool>) {
576 match value {
582 match value {
577 None => {
583 None => {
578 self.flags.remove(Flags::HAS_FALLBACK_SYMLINK);
584 self.flags.remove(Flags::HAS_FALLBACK_SYMLINK);
579 self.flags.remove(Flags::FALLBACK_SYMLINK);
585 self.flags.remove(Flags::FALLBACK_SYMLINK);
580 }
586 }
581 Some(symlink) => {
587 Some(symlink) => {
582 self.flags.insert(Flags::HAS_FALLBACK_SYMLINK);
588 self.flags.insert(Flags::HAS_FALLBACK_SYMLINK);
583 if symlink {
589 if symlink {
584 self.flags.insert(Flags::FALLBACK_SYMLINK);
590 self.flags.insert(Flags::FALLBACK_SYMLINK);
585 }
591 }
586 }
592 }
587 }
593 }
588 }
594 }
589
595
590 pub fn truncated_mtime(&self) -> Option<TruncatedTimestamp> {
596 pub fn truncated_mtime(&self) -> Option<TruncatedTimestamp> {
591 self.mtime
597 self.mtime
592 }
598 }
593
599
594 pub fn drop_merge_data(&mut self) {
600 pub fn drop_merge_data(&mut self) {
595 if self.flags.contains(Flags::P2_INFO) {
601 if self.flags.contains(Flags::P2_INFO) {
596 self.flags.remove(Flags::P2_INFO);
602 self.flags.remove(Flags::P2_INFO);
597 self.mode_size = None;
603 self.mode_size = None;
598 self.mtime = None;
604 self.mtime = None;
599 }
605 }
600 }
606 }
601
607
602 pub fn set_possibly_dirty(&mut self) {
608 pub fn set_possibly_dirty(&mut self) {
603 self.mtime = None
609 self.mtime = None
604 }
610 }
605
611
606 pub fn set_clean(
612 pub fn set_clean(
607 &mut self,
613 &mut self,
608 mode: u32,
614 mode: u32,
609 size: u32,
615 size: u32,
610 mtime: TruncatedTimestamp,
616 mtime: TruncatedTimestamp,
611 ) {
617 ) {
612 let size = size & RANGE_MASK_31BIT;
618 let size = size & RANGE_MASK_31BIT;
613 self.flags.insert(Flags::WDIR_TRACKED | Flags::P1_TRACKED);
619 self.flags.insert(Flags::WDIR_TRACKED | Flags::P1_TRACKED);
614 self.mode_size = Some((mode, size));
620 self.mode_size = Some((mode, size));
615 self.mtime = Some(mtime);
621 self.mtime = Some(mtime);
616 }
622 }
617
623
618 pub fn set_tracked(&mut self) {
624 pub fn set_tracked(&mut self) {
619 self.flags.insert(Flags::WDIR_TRACKED);
625 self.flags.insert(Flags::WDIR_TRACKED);
620 // `set_tracked` is replacing various `normallookup` call. So we mark
626 // `set_tracked` is replacing various `normallookup` call. So we mark
621 // the files as needing lookup
627 // the files as needing lookup
622 //
628 //
623 // Consider dropping this in the future in favor of something less
629 // Consider dropping this in the future in favor of something less
624 // broad.
630 // broad.
625 self.mtime = None;
631 self.mtime = None;
626 }
632 }
627
633
628 pub fn set_untracked(&mut self) {
634 pub fn set_untracked(&mut self) {
629 self.flags.remove(Flags::WDIR_TRACKED);
635 self.flags.remove(Flags::WDIR_TRACKED);
630 self.mode_size = None;
636 self.mode_size = None;
631 self.mtime = None;
637 self.mtime = None;
632 }
638 }
633
639
634 /// Returns `(state, mode, size, mtime)` for the puprose of serialization
640 /// Returns `(state, mode, size, mtime)` for the puprose of serialization
635 /// in the dirstate-v1 format.
641 /// in the dirstate-v1 format.
636 ///
642 ///
637 /// This includes marker values such as `mtime == -1`. In the future we may
643 /// This includes marker values such as `mtime == -1`. In the future we may
638 /// want to not represent these cases that way in memory, but serialization
644 /// want to not represent these cases that way in memory, but serialization
639 /// will need to keep the same format.
645 /// will need to keep the same format.
640 pub fn v1_data(&self) -> (u8, i32, i32, i32) {
646 pub fn v1_data(&self) -> (u8, i32, i32, i32) {
641 (
647 (
642 self.v1_state().into(),
648 self.v1_state().into(),
643 self.v1_mode(),
649 self.v1_mode(),
644 self.v1_size(),
650 self.v1_size(),
645 self.v1_mtime(),
651 self.v1_mtime(),
646 )
652 )
647 }
653 }
648
654
649 pub(crate) fn is_from_other_parent(&self) -> bool {
655 pub(crate) fn is_from_other_parent(&self) -> bool {
650 self.state() == EntryState::Normal
656 self.state() == EntryState::Normal
651 && self.size() == SIZE_FROM_OTHER_PARENT
657 && self.size() == SIZE_FROM_OTHER_PARENT
652 }
658 }
653
659
654 // TODO: other platforms
660 // TODO: other platforms
655 #[cfg(unix)]
661 #[cfg(unix)]
656 pub fn mode_changed(
662 pub fn mode_changed(
657 &self,
663 &self,
658 filesystem_metadata: &std::fs::Metadata,
664 filesystem_metadata: &std::fs::Metadata,
659 ) -> bool {
665 ) -> bool {
660 let dirstate_exec_bit = (self.mode() as u32 & EXEC_BIT_MASK) != 0;
666 let dirstate_exec_bit = (self.mode() as u32 & EXEC_BIT_MASK) != 0;
661 let fs_exec_bit = has_exec_bit(filesystem_metadata);
667 let fs_exec_bit = has_exec_bit(filesystem_metadata);
662 dirstate_exec_bit != fs_exec_bit
668 dirstate_exec_bit != fs_exec_bit
663 }
669 }
664
670
665 /// Returns a `(state, mode, size, mtime)` tuple as for
671 /// Returns a `(state, mode, size, mtime)` tuple as for
666 /// `DirstateMapMethods::debug_iter`.
672 /// `DirstateMapMethods::debug_iter`.
667 pub fn debug_tuple(&self) -> (u8, i32, i32, i32) {
673 pub fn debug_tuple(&self) -> (u8, i32, i32, i32) {
668 (self.state().into(), self.mode(), self.size(), self.mtime())
674 (self.state().into(), self.mode(), self.size(), self.mtime())
669 }
675 }
670 }
676 }
671
677
672 impl EntryState {
678 impl EntryState {
673 pub fn is_tracked(self) -> bool {
679 pub fn is_tracked(self) -> bool {
674 use EntryState::*;
680 use EntryState::*;
675 match self {
681 match self {
676 Normal | Added | Merged => true,
682 Normal | Added | Merged => true,
677 Removed => false,
683 Removed => false,
678 }
684 }
679 }
685 }
680 }
686 }
681
687
682 impl TryFrom<u8> for EntryState {
688 impl TryFrom<u8> for EntryState {
683 type Error = HgError;
689 type Error = HgError;
684
690
685 fn try_from(value: u8) -> Result<Self, Self::Error> {
691 fn try_from(value: u8) -> Result<Self, Self::Error> {
686 match value {
692 match value {
687 b'n' => Ok(EntryState::Normal),
693 b'n' => Ok(EntryState::Normal),
688 b'a' => Ok(EntryState::Added),
694 b'a' => Ok(EntryState::Added),
689 b'r' => Ok(EntryState::Removed),
695 b'r' => Ok(EntryState::Removed),
690 b'm' => Ok(EntryState::Merged),
696 b'm' => Ok(EntryState::Merged),
691 _ => Err(HgError::CorruptedRepository(format!(
697 _ => Err(HgError::CorruptedRepository(format!(
692 "Incorrect dirstate entry state {}",
698 "Incorrect dirstate entry state {}",
693 value
699 value
694 ))),
700 ))),
695 }
701 }
696 }
702 }
697 }
703 }
698
704
699 impl Into<u8> for EntryState {
705 impl Into<u8> for EntryState {
700 fn into(self) -> u8 {
706 fn into(self) -> u8 {
701 match self {
707 match self {
702 EntryState::Normal => b'n',
708 EntryState::Normal => b'n',
703 EntryState::Added => b'a',
709 EntryState::Added => b'a',
704 EntryState::Removed => b'r',
710 EntryState::Removed => b'r',
705 EntryState::Merged => b'm',
711 EntryState::Merged => b'm',
706 }
712 }
707 }
713 }
708 }
714 }
709
715
710 const EXEC_BIT_MASK: u32 = 0o100;
716 const EXEC_BIT_MASK: u32 = 0o100;
711
717
712 pub fn has_exec_bit(metadata: &std::fs::Metadata) -> bool {
718 pub fn has_exec_bit(metadata: &std::fs::Metadata) -> bool {
713 // TODO: How to handle executable permissions on Windows?
719 // TODO: How to handle executable permissions on Windows?
714 use std::os::unix::fs::MetadataExt;
720 use std::os::unix::fs::MetadataExt;
715 (metadata.mode() & EXEC_BIT_MASK) != 0
721 (metadata.mode() & EXEC_BIT_MASK) != 0
716 }
722 }
@@ -1,1255 +1,1352 b''
1 use bytes_cast::BytesCast;
1 use bytes_cast::BytesCast;
2 use micro_timer::timed;
2 use micro_timer::timed;
3 use std::borrow::Cow;
3 use std::borrow::Cow;
4 use std::path::PathBuf;
4 use std::path::PathBuf;
5
5
6 use super::on_disk;
6 use super::on_disk;
7 use super::on_disk::DirstateV2ParseError;
7 use super::on_disk::DirstateV2ParseError;
8 use super::owning::OwningDirstateMap;
8 use super::owning::OwningDirstateMap;
9 use super::path_with_basename::WithBasename;
9 use super::path_with_basename::WithBasename;
10 use crate::dirstate::parsers::pack_entry;
10 use crate::dirstate::parsers::pack_entry;
11 use crate::dirstate::parsers::packed_entry_size;
11 use crate::dirstate::parsers::packed_entry_size;
12 use crate::dirstate::parsers::parse_dirstate_entries;
12 use crate::dirstate::parsers::parse_dirstate_entries;
13 use crate::dirstate::CopyMapIter;
13 use crate::dirstate::CopyMapIter;
14 use crate::dirstate::DirstateV2Data;
15 use crate::dirstate::ParentFileData;
14 use crate::dirstate::StateMapIter;
16 use crate::dirstate::StateMapIter;
15 use crate::dirstate::TruncatedTimestamp;
17 use crate::dirstate::TruncatedTimestamp;
16 use crate::dirstate::SIZE_FROM_OTHER_PARENT;
18 use crate::dirstate::SIZE_FROM_OTHER_PARENT;
17 use crate::dirstate::SIZE_NON_NORMAL;
19 use crate::dirstate::SIZE_NON_NORMAL;
18 use crate::matchers::Matcher;
20 use crate::matchers::Matcher;
19 use crate::utils::hg_path::{HgPath, HgPathBuf};
21 use crate::utils::hg_path::{HgPath, HgPathBuf};
20 use crate::DirstateEntry;
22 use crate::DirstateEntry;
21 use crate::DirstateError;
23 use crate::DirstateError;
22 use crate::DirstateParents;
24 use crate::DirstateParents;
23 use crate::DirstateStatus;
25 use crate::DirstateStatus;
24 use crate::EntryState;
26 use crate::EntryState;
25 use crate::FastHashbrownMap as FastHashMap;
27 use crate::FastHashbrownMap as FastHashMap;
26 use crate::PatternFileWarning;
28 use crate::PatternFileWarning;
27 use crate::StatusError;
29 use crate::StatusError;
28 use crate::StatusOptions;
30 use crate::StatusOptions;
29
31
30 /// Append to an existing data file if the amount of unreachable data (not used
32 /// Append to an existing data file if the amount of unreachable data (not used
31 /// anymore) is less than this fraction of the total amount of existing data.
33 /// anymore) is less than this fraction of the total amount of existing data.
32 const ACCEPTABLE_UNREACHABLE_BYTES_RATIO: f32 = 0.5;
34 const ACCEPTABLE_UNREACHABLE_BYTES_RATIO: f32 = 0.5;
33
35
34 pub struct DirstateMap<'on_disk> {
36 pub struct DirstateMap<'on_disk> {
35 /// Contents of the `.hg/dirstate` file
37 /// Contents of the `.hg/dirstate` file
36 pub(super) on_disk: &'on_disk [u8],
38 pub(super) on_disk: &'on_disk [u8],
37
39
38 pub(super) root: ChildNodes<'on_disk>,
40 pub(super) root: ChildNodes<'on_disk>,
39
41
40 /// Number of nodes anywhere in the tree that have `.entry.is_some()`.
42 /// Number of nodes anywhere in the tree that have `.entry.is_some()`.
41 pub(super) nodes_with_entry_count: u32,
43 pub(super) nodes_with_entry_count: u32,
42
44
43 /// Number of nodes anywhere in the tree that have
45 /// Number of nodes anywhere in the tree that have
44 /// `.copy_source.is_some()`.
46 /// `.copy_source.is_some()`.
45 pub(super) nodes_with_copy_source_count: u32,
47 pub(super) nodes_with_copy_source_count: u32,
46
48
47 /// See on_disk::Header
49 /// See on_disk::Header
48 pub(super) ignore_patterns_hash: on_disk::IgnorePatternsHash,
50 pub(super) ignore_patterns_hash: on_disk::IgnorePatternsHash,
49
51
50 /// How many bytes of `on_disk` are not used anymore
52 /// How many bytes of `on_disk` are not used anymore
51 pub(super) unreachable_bytes: u32,
53 pub(super) unreachable_bytes: u32,
52 }
54 }
53
55
54 /// Using a plain `HgPathBuf` of the full path from the repository root as a
56 /// Using a plain `HgPathBuf` of the full path from the repository root as a
55 /// map key would also work: all paths in a given map have the same parent
57 /// map key would also work: all paths in a given map have the same parent
56 /// path, so comparing full paths gives the same result as comparing base
58 /// path, so comparing full paths gives the same result as comparing base
57 /// names. However `HashMap` would waste time always re-hashing the same
59 /// names. However `HashMap` would waste time always re-hashing the same
58 /// string prefix.
60 /// string prefix.
59 pub(super) type NodeKey<'on_disk> = WithBasename<Cow<'on_disk, HgPath>>;
61 pub(super) type NodeKey<'on_disk> = WithBasename<Cow<'on_disk, HgPath>>;
60
62
61 /// Similar to `&'tree Cow<'on_disk, HgPath>`, but can also be returned
63 /// Similar to `&'tree Cow<'on_disk, HgPath>`, but can also be returned
62 /// for on-disk nodes that don’t actually have a `Cow` to borrow.
64 /// for on-disk nodes that don’t actually have a `Cow` to borrow.
63 pub(super) enum BorrowedPath<'tree, 'on_disk> {
65 pub(super) enum BorrowedPath<'tree, 'on_disk> {
64 InMemory(&'tree HgPathBuf),
66 InMemory(&'tree HgPathBuf),
65 OnDisk(&'on_disk HgPath),
67 OnDisk(&'on_disk HgPath),
66 }
68 }
67
69
68 pub(super) enum ChildNodes<'on_disk> {
70 pub(super) enum ChildNodes<'on_disk> {
69 InMemory(FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
71 InMemory(FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
70 OnDisk(&'on_disk [on_disk::Node]),
72 OnDisk(&'on_disk [on_disk::Node]),
71 }
73 }
72
74
73 pub(super) enum ChildNodesRef<'tree, 'on_disk> {
75 pub(super) enum ChildNodesRef<'tree, 'on_disk> {
74 InMemory(&'tree FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
76 InMemory(&'tree FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>),
75 OnDisk(&'on_disk [on_disk::Node]),
77 OnDisk(&'on_disk [on_disk::Node]),
76 }
78 }
77
79
78 pub(super) enum NodeRef<'tree, 'on_disk> {
80 pub(super) enum NodeRef<'tree, 'on_disk> {
79 InMemory(&'tree NodeKey<'on_disk>, &'tree Node<'on_disk>),
81 InMemory(&'tree NodeKey<'on_disk>, &'tree Node<'on_disk>),
80 OnDisk(&'on_disk on_disk::Node),
82 OnDisk(&'on_disk on_disk::Node),
81 }
83 }
82
84
83 impl<'tree, 'on_disk> BorrowedPath<'tree, 'on_disk> {
85 impl<'tree, 'on_disk> BorrowedPath<'tree, 'on_disk> {
84 pub fn detach_from_tree(&self) -> Cow<'on_disk, HgPath> {
86 pub fn detach_from_tree(&self) -> Cow<'on_disk, HgPath> {
85 match *self {
87 match *self {
86 BorrowedPath::InMemory(in_memory) => Cow::Owned(in_memory.clone()),
88 BorrowedPath::InMemory(in_memory) => Cow::Owned(in_memory.clone()),
87 BorrowedPath::OnDisk(on_disk) => Cow::Borrowed(on_disk),
89 BorrowedPath::OnDisk(on_disk) => Cow::Borrowed(on_disk),
88 }
90 }
89 }
91 }
90 }
92 }
91
93
92 impl<'tree, 'on_disk> std::ops::Deref for BorrowedPath<'tree, 'on_disk> {
94 impl<'tree, 'on_disk> std::ops::Deref for BorrowedPath<'tree, 'on_disk> {
93 type Target = HgPath;
95 type Target = HgPath;
94
96
95 fn deref(&self) -> &HgPath {
97 fn deref(&self) -> &HgPath {
96 match *self {
98 match *self {
97 BorrowedPath::InMemory(in_memory) => in_memory,
99 BorrowedPath::InMemory(in_memory) => in_memory,
98 BorrowedPath::OnDisk(on_disk) => on_disk,
100 BorrowedPath::OnDisk(on_disk) => on_disk,
99 }
101 }
100 }
102 }
101 }
103 }
102
104
103 impl Default for ChildNodes<'_> {
105 impl Default for ChildNodes<'_> {
104 fn default() -> Self {
106 fn default() -> Self {
105 ChildNodes::InMemory(Default::default())
107 ChildNodes::InMemory(Default::default())
106 }
108 }
107 }
109 }
108
110
109 impl<'on_disk> ChildNodes<'on_disk> {
111 impl<'on_disk> ChildNodes<'on_disk> {
110 pub(super) fn as_ref<'tree>(
112 pub(super) fn as_ref<'tree>(
111 &'tree self,
113 &'tree self,
112 ) -> ChildNodesRef<'tree, 'on_disk> {
114 ) -> ChildNodesRef<'tree, 'on_disk> {
113 match self {
115 match self {
114 ChildNodes::InMemory(nodes) => ChildNodesRef::InMemory(nodes),
116 ChildNodes::InMemory(nodes) => ChildNodesRef::InMemory(nodes),
115 ChildNodes::OnDisk(nodes) => ChildNodesRef::OnDisk(nodes),
117 ChildNodes::OnDisk(nodes) => ChildNodesRef::OnDisk(nodes),
116 }
118 }
117 }
119 }
118
120
119 pub(super) fn is_empty(&self) -> bool {
121 pub(super) fn is_empty(&self) -> bool {
120 match self {
122 match self {
121 ChildNodes::InMemory(nodes) => nodes.is_empty(),
123 ChildNodes::InMemory(nodes) => nodes.is_empty(),
122 ChildNodes::OnDisk(nodes) => nodes.is_empty(),
124 ChildNodes::OnDisk(nodes) => nodes.is_empty(),
123 }
125 }
124 }
126 }
125
127
126 fn make_mut(
128 fn make_mut(
127 &mut self,
129 &mut self,
128 on_disk: &'on_disk [u8],
130 on_disk: &'on_disk [u8],
129 unreachable_bytes: &mut u32,
131 unreachable_bytes: &mut u32,
130 ) -> Result<
132 ) -> Result<
131 &mut FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>,
133 &mut FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>,
132 DirstateV2ParseError,
134 DirstateV2ParseError,
133 > {
135 > {
134 match self {
136 match self {
135 ChildNodes::InMemory(nodes) => Ok(nodes),
137 ChildNodes::InMemory(nodes) => Ok(nodes),
136 ChildNodes::OnDisk(nodes) => {
138 ChildNodes::OnDisk(nodes) => {
137 *unreachable_bytes +=
139 *unreachable_bytes +=
138 std::mem::size_of_val::<[on_disk::Node]>(nodes) as u32;
140 std::mem::size_of_val::<[on_disk::Node]>(nodes) as u32;
139 let nodes = nodes
141 let nodes = nodes
140 .iter()
142 .iter()
141 .map(|node| {
143 .map(|node| {
142 Ok((
144 Ok((
143 node.path(on_disk)?,
145 node.path(on_disk)?,
144 node.to_in_memory_node(on_disk)?,
146 node.to_in_memory_node(on_disk)?,
145 ))
147 ))
146 })
148 })
147 .collect::<Result<_, _>>()?;
149 .collect::<Result<_, _>>()?;
148 *self = ChildNodes::InMemory(nodes);
150 *self = ChildNodes::InMemory(nodes);
149 match self {
151 match self {
150 ChildNodes::InMemory(nodes) => Ok(nodes),
152 ChildNodes::InMemory(nodes) => Ok(nodes),
151 ChildNodes::OnDisk(_) => unreachable!(),
153 ChildNodes::OnDisk(_) => unreachable!(),
152 }
154 }
153 }
155 }
154 }
156 }
155 }
157 }
156 }
158 }
157
159
158 impl<'tree, 'on_disk> ChildNodesRef<'tree, 'on_disk> {
160 impl<'tree, 'on_disk> ChildNodesRef<'tree, 'on_disk> {
159 pub(super) fn get(
161 pub(super) fn get(
160 &self,
162 &self,
161 base_name: &HgPath,
163 base_name: &HgPath,
162 on_disk: &'on_disk [u8],
164 on_disk: &'on_disk [u8],
163 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
165 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
164 match self {
166 match self {
165 ChildNodesRef::InMemory(nodes) => Ok(nodes
167 ChildNodesRef::InMemory(nodes) => Ok(nodes
166 .get_key_value(base_name)
168 .get_key_value(base_name)
167 .map(|(k, v)| NodeRef::InMemory(k, v))),
169 .map(|(k, v)| NodeRef::InMemory(k, v))),
168 ChildNodesRef::OnDisk(nodes) => {
170 ChildNodesRef::OnDisk(nodes) => {
169 let mut parse_result = Ok(());
171 let mut parse_result = Ok(());
170 let search_result = nodes.binary_search_by(|node| {
172 let search_result = nodes.binary_search_by(|node| {
171 match node.base_name(on_disk) {
173 match node.base_name(on_disk) {
172 Ok(node_base_name) => node_base_name.cmp(base_name),
174 Ok(node_base_name) => node_base_name.cmp(base_name),
173 Err(e) => {
175 Err(e) => {
174 parse_result = Err(e);
176 parse_result = Err(e);
175 // Dummy comparison result, `search_result` won’t
177 // Dummy comparison result, `search_result` won’t
176 // be used since `parse_result` is an error
178 // be used since `parse_result` is an error
177 std::cmp::Ordering::Equal
179 std::cmp::Ordering::Equal
178 }
180 }
179 }
181 }
180 });
182 });
181 parse_result.map(|()| {
183 parse_result.map(|()| {
182 search_result.ok().map(|i| NodeRef::OnDisk(&nodes[i]))
184 search_result.ok().map(|i| NodeRef::OnDisk(&nodes[i]))
183 })
185 })
184 }
186 }
185 }
187 }
186 }
188 }
187
189
188 /// Iterate in undefined order
190 /// Iterate in undefined order
189 pub(super) fn iter(
191 pub(super) fn iter(
190 &self,
192 &self,
191 ) -> impl Iterator<Item = NodeRef<'tree, 'on_disk>> {
193 ) -> impl Iterator<Item = NodeRef<'tree, 'on_disk>> {
192 match self {
194 match self {
193 ChildNodesRef::InMemory(nodes) => itertools::Either::Left(
195 ChildNodesRef::InMemory(nodes) => itertools::Either::Left(
194 nodes.iter().map(|(k, v)| NodeRef::InMemory(k, v)),
196 nodes.iter().map(|(k, v)| NodeRef::InMemory(k, v)),
195 ),
197 ),
196 ChildNodesRef::OnDisk(nodes) => {
198 ChildNodesRef::OnDisk(nodes) => {
197 itertools::Either::Right(nodes.iter().map(NodeRef::OnDisk))
199 itertools::Either::Right(nodes.iter().map(NodeRef::OnDisk))
198 }
200 }
199 }
201 }
200 }
202 }
201
203
202 /// Iterate in parallel in undefined order
204 /// Iterate in parallel in undefined order
203 pub(super) fn par_iter(
205 pub(super) fn par_iter(
204 &self,
206 &self,
205 ) -> impl rayon::iter::ParallelIterator<Item = NodeRef<'tree, 'on_disk>>
207 ) -> impl rayon::iter::ParallelIterator<Item = NodeRef<'tree, 'on_disk>>
206 {
208 {
207 use rayon::prelude::*;
209 use rayon::prelude::*;
208 match self {
210 match self {
209 ChildNodesRef::InMemory(nodes) => rayon::iter::Either::Left(
211 ChildNodesRef::InMemory(nodes) => rayon::iter::Either::Left(
210 nodes.par_iter().map(|(k, v)| NodeRef::InMemory(k, v)),
212 nodes.par_iter().map(|(k, v)| NodeRef::InMemory(k, v)),
211 ),
213 ),
212 ChildNodesRef::OnDisk(nodes) => rayon::iter::Either::Right(
214 ChildNodesRef::OnDisk(nodes) => rayon::iter::Either::Right(
213 nodes.par_iter().map(NodeRef::OnDisk),
215 nodes.par_iter().map(NodeRef::OnDisk),
214 ),
216 ),
215 }
217 }
216 }
218 }
217
219
218 pub(super) fn sorted(&self) -> Vec<NodeRef<'tree, 'on_disk>> {
220 pub(super) fn sorted(&self) -> Vec<NodeRef<'tree, 'on_disk>> {
219 match self {
221 match self {
220 ChildNodesRef::InMemory(nodes) => {
222 ChildNodesRef::InMemory(nodes) => {
221 let mut vec: Vec<_> = nodes
223 let mut vec: Vec<_> = nodes
222 .iter()
224 .iter()
223 .map(|(k, v)| NodeRef::InMemory(k, v))
225 .map(|(k, v)| NodeRef::InMemory(k, v))
224 .collect();
226 .collect();
225 fn sort_key<'a>(node: &'a NodeRef) -> &'a HgPath {
227 fn sort_key<'a>(node: &'a NodeRef) -> &'a HgPath {
226 match node {
228 match node {
227 NodeRef::InMemory(path, _node) => path.base_name(),
229 NodeRef::InMemory(path, _node) => path.base_name(),
228 NodeRef::OnDisk(_) => unreachable!(),
230 NodeRef::OnDisk(_) => unreachable!(),
229 }
231 }
230 }
232 }
231 // `sort_unstable_by_key` doesn’t allow keys borrowing from the
233 // `sort_unstable_by_key` doesn’t allow keys borrowing from the
232 // value: https://github.com/rust-lang/rust/issues/34162
234 // value: https://github.com/rust-lang/rust/issues/34162
233 vec.sort_unstable_by(|a, b| sort_key(a).cmp(sort_key(b)));
235 vec.sort_unstable_by(|a, b| sort_key(a).cmp(sort_key(b)));
234 vec
236 vec
235 }
237 }
236 ChildNodesRef::OnDisk(nodes) => {
238 ChildNodesRef::OnDisk(nodes) => {
237 // Nodes on disk are already sorted
239 // Nodes on disk are already sorted
238 nodes.iter().map(NodeRef::OnDisk).collect()
240 nodes.iter().map(NodeRef::OnDisk).collect()
239 }
241 }
240 }
242 }
241 }
243 }
242 }
244 }
243
245
244 impl<'tree, 'on_disk> NodeRef<'tree, 'on_disk> {
246 impl<'tree, 'on_disk> NodeRef<'tree, 'on_disk> {
245 pub(super) fn full_path(
247 pub(super) fn full_path(
246 &self,
248 &self,
247 on_disk: &'on_disk [u8],
249 on_disk: &'on_disk [u8],
248 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
250 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
249 match self {
251 match self {
250 NodeRef::InMemory(path, _node) => Ok(path.full_path()),
252 NodeRef::InMemory(path, _node) => Ok(path.full_path()),
251 NodeRef::OnDisk(node) => node.full_path(on_disk),
253 NodeRef::OnDisk(node) => node.full_path(on_disk),
252 }
254 }
253 }
255 }
254
256
255 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
257 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
256 /// HgPath>` detached from `'tree`
258 /// HgPath>` detached from `'tree`
257 pub(super) fn full_path_borrowed(
259 pub(super) fn full_path_borrowed(
258 &self,
260 &self,
259 on_disk: &'on_disk [u8],
261 on_disk: &'on_disk [u8],
260 ) -> Result<BorrowedPath<'tree, 'on_disk>, DirstateV2ParseError> {
262 ) -> Result<BorrowedPath<'tree, 'on_disk>, DirstateV2ParseError> {
261 match self {
263 match self {
262 NodeRef::InMemory(path, _node) => match path.full_path() {
264 NodeRef::InMemory(path, _node) => match path.full_path() {
263 Cow::Borrowed(on_disk) => Ok(BorrowedPath::OnDisk(on_disk)),
265 Cow::Borrowed(on_disk) => Ok(BorrowedPath::OnDisk(on_disk)),
264 Cow::Owned(in_memory) => Ok(BorrowedPath::InMemory(in_memory)),
266 Cow::Owned(in_memory) => Ok(BorrowedPath::InMemory(in_memory)),
265 },
267 },
266 NodeRef::OnDisk(node) => {
268 NodeRef::OnDisk(node) => {
267 Ok(BorrowedPath::OnDisk(node.full_path(on_disk)?))
269 Ok(BorrowedPath::OnDisk(node.full_path(on_disk)?))
268 }
270 }
269 }
271 }
270 }
272 }
271
273
272 pub(super) fn base_name(
274 pub(super) fn base_name(
273 &self,
275 &self,
274 on_disk: &'on_disk [u8],
276 on_disk: &'on_disk [u8],
275 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
277 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
276 match self {
278 match self {
277 NodeRef::InMemory(path, _node) => Ok(path.base_name()),
279 NodeRef::InMemory(path, _node) => Ok(path.base_name()),
278 NodeRef::OnDisk(node) => node.base_name(on_disk),
280 NodeRef::OnDisk(node) => node.base_name(on_disk),
279 }
281 }
280 }
282 }
281
283
282 pub(super) fn children(
284 pub(super) fn children(
283 &self,
285 &self,
284 on_disk: &'on_disk [u8],
286 on_disk: &'on_disk [u8],
285 ) -> Result<ChildNodesRef<'tree, 'on_disk>, DirstateV2ParseError> {
287 ) -> Result<ChildNodesRef<'tree, 'on_disk>, DirstateV2ParseError> {
286 match self {
288 match self {
287 NodeRef::InMemory(_path, node) => Ok(node.children.as_ref()),
289 NodeRef::InMemory(_path, node) => Ok(node.children.as_ref()),
288 NodeRef::OnDisk(node) => {
290 NodeRef::OnDisk(node) => {
289 Ok(ChildNodesRef::OnDisk(node.children(on_disk)?))
291 Ok(ChildNodesRef::OnDisk(node.children(on_disk)?))
290 }
292 }
291 }
293 }
292 }
294 }
293
295
294 pub(super) fn has_copy_source(&self) -> bool {
296 pub(super) fn has_copy_source(&self) -> bool {
295 match self {
297 match self {
296 NodeRef::InMemory(_path, node) => node.copy_source.is_some(),
298 NodeRef::InMemory(_path, node) => node.copy_source.is_some(),
297 NodeRef::OnDisk(node) => node.has_copy_source(),
299 NodeRef::OnDisk(node) => node.has_copy_source(),
298 }
300 }
299 }
301 }
300
302
301 pub(super) fn copy_source(
303 pub(super) fn copy_source(
302 &self,
304 &self,
303 on_disk: &'on_disk [u8],
305 on_disk: &'on_disk [u8],
304 ) -> Result<Option<&'tree HgPath>, DirstateV2ParseError> {
306 ) -> Result<Option<&'tree HgPath>, DirstateV2ParseError> {
305 match self {
307 match self {
306 NodeRef::InMemory(_path, node) => {
308 NodeRef::InMemory(_path, node) => {
307 Ok(node.copy_source.as_ref().map(|s| &**s))
309 Ok(node.copy_source.as_ref().map(|s| &**s))
308 }
310 }
309 NodeRef::OnDisk(node) => node.copy_source(on_disk),
311 NodeRef::OnDisk(node) => node.copy_source(on_disk),
310 }
312 }
311 }
313 }
312 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
314 /// Returns a `BorrowedPath`, which can be turned into a `Cow<'on_disk,
313 /// HgPath>` detached from `'tree`
315 /// HgPath>` detached from `'tree`
314 pub(super) fn copy_source_borrowed(
316 pub(super) fn copy_source_borrowed(
315 &self,
317 &self,
316 on_disk: &'on_disk [u8],
318 on_disk: &'on_disk [u8],
317 ) -> Result<Option<BorrowedPath<'tree, 'on_disk>>, DirstateV2ParseError>
319 ) -> Result<Option<BorrowedPath<'tree, 'on_disk>>, DirstateV2ParseError>
318 {
320 {
319 Ok(match self {
321 Ok(match self {
320 NodeRef::InMemory(_path, node) => {
322 NodeRef::InMemory(_path, node) => {
321 node.copy_source.as_ref().map(|source| match source {
323 node.copy_source.as_ref().map(|source| match source {
322 Cow::Borrowed(on_disk) => BorrowedPath::OnDisk(on_disk),
324 Cow::Borrowed(on_disk) => BorrowedPath::OnDisk(on_disk),
323 Cow::Owned(in_memory) => BorrowedPath::InMemory(in_memory),
325 Cow::Owned(in_memory) => BorrowedPath::InMemory(in_memory),
324 })
326 })
325 }
327 }
326 NodeRef::OnDisk(node) => node
328 NodeRef::OnDisk(node) => node
327 .copy_source(on_disk)?
329 .copy_source(on_disk)?
328 .map(|source| BorrowedPath::OnDisk(source)),
330 .map(|source| BorrowedPath::OnDisk(source)),
329 })
331 })
330 }
332 }
331
333
332 pub(super) fn entry(
334 pub(super) fn entry(
333 &self,
335 &self,
334 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
336 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
335 match self {
337 match self {
336 NodeRef::InMemory(_path, node) => {
338 NodeRef::InMemory(_path, node) => {
337 Ok(node.data.as_entry().copied())
339 Ok(node.data.as_entry().copied())
338 }
340 }
339 NodeRef::OnDisk(node) => node.entry(),
341 NodeRef::OnDisk(node) => node.entry(),
340 }
342 }
341 }
343 }
342
344
343 pub(super) fn state(
345 pub(super) fn state(
344 &self,
346 &self,
345 ) -> Result<Option<EntryState>, DirstateV2ParseError> {
347 ) -> Result<Option<EntryState>, DirstateV2ParseError> {
346 Ok(self.entry()?.and_then(|e| {
348 Ok(self.entry()?.and_then(|e| {
347 if e.any_tracked() {
349 if e.any_tracked() {
348 Some(e.state())
350 Some(e.state())
349 } else {
351 } else {
350 None
352 None
351 }
353 }
352 }))
354 }))
353 }
355 }
354
356
355 pub(super) fn cached_directory_mtime(
357 pub(super) fn cached_directory_mtime(
356 &self,
358 &self,
357 ) -> Result<Option<TruncatedTimestamp>, DirstateV2ParseError> {
359 ) -> Result<Option<TruncatedTimestamp>, DirstateV2ParseError> {
358 match self {
360 match self {
359 NodeRef::InMemory(_path, node) => Ok(match node.data {
361 NodeRef::InMemory(_path, node) => Ok(match node.data {
360 NodeData::CachedDirectory { mtime } => Some(mtime),
362 NodeData::CachedDirectory { mtime } => Some(mtime),
361 _ => None,
363 _ => None,
362 }),
364 }),
363 NodeRef::OnDisk(node) => node.cached_directory_mtime(),
365 NodeRef::OnDisk(node) => node.cached_directory_mtime(),
364 }
366 }
365 }
367 }
366
368
367 pub(super) fn descendants_with_entry_count(&self) -> u32 {
369 pub(super) fn descendants_with_entry_count(&self) -> u32 {
368 match self {
370 match self {
369 NodeRef::InMemory(_path, node) => {
371 NodeRef::InMemory(_path, node) => {
370 node.descendants_with_entry_count
372 node.descendants_with_entry_count
371 }
373 }
372 NodeRef::OnDisk(node) => node.descendants_with_entry_count.get(),
374 NodeRef::OnDisk(node) => node.descendants_with_entry_count.get(),
373 }
375 }
374 }
376 }
375
377
376 pub(super) fn tracked_descendants_count(&self) -> u32 {
378 pub(super) fn tracked_descendants_count(&self) -> u32 {
377 match self {
379 match self {
378 NodeRef::InMemory(_path, node) => node.tracked_descendants_count,
380 NodeRef::InMemory(_path, node) => node.tracked_descendants_count,
379 NodeRef::OnDisk(node) => node.tracked_descendants_count.get(),
381 NodeRef::OnDisk(node) => node.tracked_descendants_count.get(),
380 }
382 }
381 }
383 }
382 }
384 }
383
385
384 /// Represents a file or a directory
386 /// Represents a file or a directory
385 #[derive(Default)]
387 #[derive(Default)]
386 pub(super) struct Node<'on_disk> {
388 pub(super) struct Node<'on_disk> {
387 pub(super) data: NodeData,
389 pub(super) data: NodeData,
388
390
389 pub(super) copy_source: Option<Cow<'on_disk, HgPath>>,
391 pub(super) copy_source: Option<Cow<'on_disk, HgPath>>,
390
392
391 pub(super) children: ChildNodes<'on_disk>,
393 pub(super) children: ChildNodes<'on_disk>,
392
394
393 /// How many (non-inclusive) descendants of this node have an entry.
395 /// How many (non-inclusive) descendants of this node have an entry.
394 pub(super) descendants_with_entry_count: u32,
396 pub(super) descendants_with_entry_count: u32,
395
397
396 /// How many (non-inclusive) descendants of this node have an entry whose
398 /// How many (non-inclusive) descendants of this node have an entry whose
397 /// state is "tracked".
399 /// state is "tracked".
398 pub(super) tracked_descendants_count: u32,
400 pub(super) tracked_descendants_count: u32,
399 }
401 }
400
402
401 pub(super) enum NodeData {
403 pub(super) enum NodeData {
402 Entry(DirstateEntry),
404 Entry(DirstateEntry),
403 CachedDirectory { mtime: TruncatedTimestamp },
405 CachedDirectory { mtime: TruncatedTimestamp },
404 None,
406 None,
405 }
407 }
406
408
407 impl Default for NodeData {
409 impl Default for NodeData {
408 fn default() -> Self {
410 fn default() -> Self {
409 NodeData::None
411 NodeData::None
410 }
412 }
411 }
413 }
412
414
413 impl NodeData {
415 impl NodeData {
414 fn has_entry(&self) -> bool {
416 fn has_entry(&self) -> bool {
415 match self {
417 match self {
416 NodeData::Entry(_) => true,
418 NodeData::Entry(_) => true,
417 _ => false,
419 _ => false,
418 }
420 }
419 }
421 }
420
422
421 fn as_entry(&self) -> Option<&DirstateEntry> {
423 fn as_entry(&self) -> Option<&DirstateEntry> {
422 match self {
424 match self {
423 NodeData::Entry(entry) => Some(entry),
425 NodeData::Entry(entry) => Some(entry),
424 _ => None,
426 _ => None,
425 }
427 }
426 }
428 }
427 }
429 }
428
430
429 impl<'on_disk> DirstateMap<'on_disk> {
431 impl<'on_disk> DirstateMap<'on_disk> {
430 pub(super) fn empty(on_disk: &'on_disk [u8]) -> Self {
432 pub(super) fn empty(on_disk: &'on_disk [u8]) -> Self {
431 Self {
433 Self {
432 on_disk,
434 on_disk,
433 root: ChildNodes::default(),
435 root: ChildNodes::default(),
434 nodes_with_entry_count: 0,
436 nodes_with_entry_count: 0,
435 nodes_with_copy_source_count: 0,
437 nodes_with_copy_source_count: 0,
436 ignore_patterns_hash: [0; on_disk::IGNORE_PATTERNS_HASH_LEN],
438 ignore_patterns_hash: [0; on_disk::IGNORE_PATTERNS_HASH_LEN],
437 unreachable_bytes: 0,
439 unreachable_bytes: 0,
438 }
440 }
439 }
441 }
440
442
441 #[timed]
443 #[timed]
442 pub fn new_v2(
444 pub fn new_v2(
443 on_disk: &'on_disk [u8],
445 on_disk: &'on_disk [u8],
444 data_size: usize,
446 data_size: usize,
445 metadata: &[u8],
447 metadata: &[u8],
446 ) -> Result<Self, DirstateError> {
448 ) -> Result<Self, DirstateError> {
447 if let Some(data) = on_disk.get(..data_size) {
449 if let Some(data) = on_disk.get(..data_size) {
448 Ok(on_disk::read(data, metadata)?)
450 Ok(on_disk::read(data, metadata)?)
449 } else {
451 } else {
450 Err(DirstateV2ParseError.into())
452 Err(DirstateV2ParseError.into())
451 }
453 }
452 }
454 }
453
455
454 #[timed]
456 #[timed]
455 pub fn new_v1(
457 pub fn new_v1(
456 on_disk: &'on_disk [u8],
458 on_disk: &'on_disk [u8],
457 ) -> Result<(Self, Option<DirstateParents>), DirstateError> {
459 ) -> Result<(Self, Option<DirstateParents>), DirstateError> {
458 let mut map = Self::empty(on_disk);
460 let mut map = Self::empty(on_disk);
459 if map.on_disk.is_empty() {
461 if map.on_disk.is_empty() {
460 return Ok((map, None));
462 return Ok((map, None));
461 }
463 }
462
464
463 let parents = parse_dirstate_entries(
465 let parents = parse_dirstate_entries(
464 map.on_disk,
466 map.on_disk,
465 |path, entry, copy_source| {
467 |path, entry, copy_source| {
466 let tracked = entry.state().is_tracked();
468 let tracked = entry.state().is_tracked();
467 let node = Self::get_or_insert_node(
469 let node = Self::get_or_insert_node(
468 map.on_disk,
470 map.on_disk,
469 &mut map.unreachable_bytes,
471 &mut map.unreachable_bytes,
470 &mut map.root,
472 &mut map.root,
471 path,
473 path,
472 WithBasename::to_cow_borrowed,
474 WithBasename::to_cow_borrowed,
473 |ancestor| {
475 |ancestor| {
474 if tracked {
476 if tracked {
475 ancestor.tracked_descendants_count += 1
477 ancestor.tracked_descendants_count += 1
476 }
478 }
477 ancestor.descendants_with_entry_count += 1
479 ancestor.descendants_with_entry_count += 1
478 },
480 },
479 )?;
481 )?;
480 assert!(
482 assert!(
481 !node.data.has_entry(),
483 !node.data.has_entry(),
482 "duplicate dirstate entry in read"
484 "duplicate dirstate entry in read"
483 );
485 );
484 assert!(
486 assert!(
485 node.copy_source.is_none(),
487 node.copy_source.is_none(),
486 "duplicate dirstate entry in read"
488 "duplicate dirstate entry in read"
487 );
489 );
488 node.data = NodeData::Entry(*entry);
490 node.data = NodeData::Entry(*entry);
489 node.copy_source = copy_source.map(Cow::Borrowed);
491 node.copy_source = copy_source.map(Cow::Borrowed);
490 map.nodes_with_entry_count += 1;
492 map.nodes_with_entry_count += 1;
491 if copy_source.is_some() {
493 if copy_source.is_some() {
492 map.nodes_with_copy_source_count += 1
494 map.nodes_with_copy_source_count += 1
493 }
495 }
494 Ok(())
496 Ok(())
495 },
497 },
496 )?;
498 )?;
497 let parents = Some(parents.clone());
499 let parents = Some(parents.clone());
498
500
499 Ok((map, parents))
501 Ok((map, parents))
500 }
502 }
501
503
502 /// Assuming dirstate-v2 format, returns whether the next write should
504 /// Assuming dirstate-v2 format, returns whether the next write should
503 /// append to the existing data file that contains `self.on_disk` (true),
505 /// append to the existing data file that contains `self.on_disk` (true),
504 /// or create a new data file from scratch (false).
506 /// or create a new data file from scratch (false).
505 pub(super) fn write_should_append(&self) -> bool {
507 pub(super) fn write_should_append(&self) -> bool {
506 let ratio = self.unreachable_bytes as f32 / self.on_disk.len() as f32;
508 let ratio = self.unreachable_bytes as f32 / self.on_disk.len() as f32;
507 ratio < ACCEPTABLE_UNREACHABLE_BYTES_RATIO
509 ratio < ACCEPTABLE_UNREACHABLE_BYTES_RATIO
508 }
510 }
509
511
510 fn get_node<'tree>(
512 fn get_node<'tree>(
511 &'tree self,
513 &'tree self,
512 path: &HgPath,
514 path: &HgPath,
513 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
515 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
514 let mut children = self.root.as_ref();
516 let mut children = self.root.as_ref();
515 let mut components = path.components();
517 let mut components = path.components();
516 let mut component =
518 let mut component =
517 components.next().expect("expected at least one components");
519 components.next().expect("expected at least one components");
518 loop {
520 loop {
519 if let Some(child) = children.get(component, self.on_disk)? {
521 if let Some(child) = children.get(component, self.on_disk)? {
520 if let Some(next_component) = components.next() {
522 if let Some(next_component) = components.next() {
521 component = next_component;
523 component = next_component;
522 children = child.children(self.on_disk)?;
524 children = child.children(self.on_disk)?;
523 } else {
525 } else {
524 return Ok(Some(child));
526 return Ok(Some(child));
525 }
527 }
526 } else {
528 } else {
527 return Ok(None);
529 return Ok(None);
528 }
530 }
529 }
531 }
530 }
532 }
531
533
532 /// Returns a mutable reference to the node at `path` if it exists
534 /// Returns a mutable reference to the node at `path` if it exists
533 ///
535 ///
534 /// This takes `root` instead of `&mut self` so that callers can mutate
536 /// This takes `root` instead of `&mut self` so that callers can mutate
535 /// other fields while the returned borrow is still valid
537 /// other fields while the returned borrow is still valid
536 fn get_node_mut<'tree>(
538 fn get_node_mut<'tree>(
537 on_disk: &'on_disk [u8],
539 on_disk: &'on_disk [u8],
538 unreachable_bytes: &mut u32,
540 unreachable_bytes: &mut u32,
539 root: &'tree mut ChildNodes<'on_disk>,
541 root: &'tree mut ChildNodes<'on_disk>,
540 path: &HgPath,
542 path: &HgPath,
541 ) -> Result<Option<&'tree mut Node<'on_disk>>, DirstateV2ParseError> {
543 ) -> Result<Option<&'tree mut Node<'on_disk>>, DirstateV2ParseError> {
542 let mut children = root;
544 let mut children = root;
543 let mut components = path.components();
545 let mut components = path.components();
544 let mut component =
546 let mut component =
545 components.next().expect("expected at least one components");
547 components.next().expect("expected at least one components");
546 loop {
548 loop {
547 if let Some(child) = children
549 if let Some(child) = children
548 .make_mut(on_disk, unreachable_bytes)?
550 .make_mut(on_disk, unreachable_bytes)?
549 .get_mut(component)
551 .get_mut(component)
550 {
552 {
551 if let Some(next_component) = components.next() {
553 if let Some(next_component) = components.next() {
552 component = next_component;
554 component = next_component;
553 children = &mut child.children;
555 children = &mut child.children;
554 } else {
556 } else {
555 return Ok(Some(child));
557 return Ok(Some(child));
556 }
558 }
557 } else {
559 } else {
558 return Ok(None);
560 return Ok(None);
559 }
561 }
560 }
562 }
561 }
563 }
562
564
563 pub(super) fn get_or_insert<'tree, 'path>(
565 pub(super) fn get_or_insert<'tree, 'path>(
564 &'tree mut self,
566 &'tree mut self,
565 path: &HgPath,
567 path: &HgPath,
566 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
568 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
567 Self::get_or_insert_node(
569 Self::get_or_insert_node(
568 self.on_disk,
570 self.on_disk,
569 &mut self.unreachable_bytes,
571 &mut self.unreachable_bytes,
570 &mut self.root,
572 &mut self.root,
571 path,
573 path,
572 WithBasename::to_cow_owned,
574 WithBasename::to_cow_owned,
573 |_| {},
575 |_| {},
574 )
576 )
575 }
577 }
576
578
577 fn get_or_insert_node<'tree, 'path>(
579 fn get_or_insert_node<'tree, 'path>(
578 on_disk: &'on_disk [u8],
580 on_disk: &'on_disk [u8],
579 unreachable_bytes: &mut u32,
581 unreachable_bytes: &mut u32,
580 root: &'tree mut ChildNodes<'on_disk>,
582 root: &'tree mut ChildNodes<'on_disk>,
581 path: &'path HgPath,
583 path: &'path HgPath,
582 to_cow: impl Fn(
584 to_cow: impl Fn(
583 WithBasename<&'path HgPath>,
585 WithBasename<&'path HgPath>,
584 ) -> WithBasename<Cow<'on_disk, HgPath>>,
586 ) -> WithBasename<Cow<'on_disk, HgPath>>,
585 mut each_ancestor: impl FnMut(&mut Node),
587 mut each_ancestor: impl FnMut(&mut Node),
586 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
588 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
587 let mut child_nodes = root;
589 let mut child_nodes = root;
588 let mut inclusive_ancestor_paths =
590 let mut inclusive_ancestor_paths =
589 WithBasename::inclusive_ancestors_of(path);
591 WithBasename::inclusive_ancestors_of(path);
590 let mut ancestor_path = inclusive_ancestor_paths
592 let mut ancestor_path = inclusive_ancestor_paths
591 .next()
593 .next()
592 .expect("expected at least one inclusive ancestor");
594 .expect("expected at least one inclusive ancestor");
593 loop {
595 loop {
594 let (_, child_node) = child_nodes
596 let (_, child_node) = child_nodes
595 .make_mut(on_disk, unreachable_bytes)?
597 .make_mut(on_disk, unreachable_bytes)?
596 .raw_entry_mut()
598 .raw_entry_mut()
597 .from_key(ancestor_path.base_name())
599 .from_key(ancestor_path.base_name())
598 .or_insert_with(|| (to_cow(ancestor_path), Node::default()));
600 .or_insert_with(|| (to_cow(ancestor_path), Node::default()));
599 if let Some(next) = inclusive_ancestor_paths.next() {
601 if let Some(next) = inclusive_ancestor_paths.next() {
600 each_ancestor(child_node);
602 each_ancestor(child_node);
601 ancestor_path = next;
603 ancestor_path = next;
602 child_nodes = &mut child_node.children;
604 child_nodes = &mut child_node.children;
603 } else {
605 } else {
604 return Ok(child_node);
606 return Ok(child_node);
605 }
607 }
606 }
608 }
607 }
609 }
608
610
611 fn reset_state(
612 &mut self,
613 filename: &HgPath,
614 old_entry_opt: Option<DirstateEntry>,
615 wc_tracked: bool,
616 p1_tracked: bool,
617 p2_info: bool,
618 has_meaningful_mtime: bool,
619 parent_file_data_opt: Option<ParentFileData>,
620 ) -> Result<(), DirstateError> {
621 let (had_entry, was_tracked) = match old_entry_opt {
622 Some(old_entry) => (true, old_entry.tracked()),
623 None => (false, false),
624 };
625 let node = Self::get_or_insert_node(
626 self.on_disk,
627 &mut self.unreachable_bytes,
628 &mut self.root,
629 filename,
630 WithBasename::to_cow_owned,
631 |ancestor| {
632 if !had_entry {
633 ancestor.descendants_with_entry_count += 1;
634 }
635 if was_tracked {
636 if !wc_tracked {
637 ancestor.tracked_descendants_count = ancestor
638 .tracked_descendants_count
639 .checked_sub(1)
640 .expect("tracked count to be >= 0");
641 }
642 } else {
643 if wc_tracked {
644 ancestor.tracked_descendants_count += 1;
645 }
646 }
647 },
648 )?;
649
650 let v2_data = if let Some(parent_file_data) = parent_file_data_opt {
651 DirstateV2Data {
652 wc_tracked,
653 p1_tracked,
654 p2_info,
655 mode_size: parent_file_data.mode_size,
656 mtime: if has_meaningful_mtime {
657 parent_file_data.mtime
658 } else {
659 None
660 },
661 ..Default::default()
662 }
663 } else {
664 DirstateV2Data {
665 wc_tracked,
666 p1_tracked,
667 p2_info,
668 ..Default::default()
669 }
670 };
671 if !had_entry {
672 self.nodes_with_entry_count += 1;
673 }
674 node.data = NodeData::Entry(DirstateEntry::from_v2_data(v2_data));
675 Ok(())
676 }
677
609 fn set_tracked(
678 fn set_tracked(
610 &mut self,
679 &mut self,
611 filename: &HgPath,
680 filename: &HgPath,
612 old_entry_opt: Option<DirstateEntry>,
681 old_entry_opt: Option<DirstateEntry>,
613 ) -> Result<bool, DirstateV2ParseError> {
682 ) -> Result<bool, DirstateV2ParseError> {
614 let was_tracked = old_entry_opt.map_or(false, |e| e.tracked());
683 let was_tracked = old_entry_opt.map_or(false, |e| e.tracked());
615 let had_entry = old_entry_opt.is_some();
684 let had_entry = old_entry_opt.is_some();
616 let tracked_count_increment = if was_tracked { 0 } else { 1 };
685 let tracked_count_increment = if was_tracked { 0 } else { 1 };
617 let mut new = false;
686 let mut new = false;
618
687
619 let node = Self::get_or_insert_node(
688 let node = Self::get_or_insert_node(
620 self.on_disk,
689 self.on_disk,
621 &mut self.unreachable_bytes,
690 &mut self.unreachable_bytes,
622 &mut self.root,
691 &mut self.root,
623 filename,
692 filename,
624 WithBasename::to_cow_owned,
693 WithBasename::to_cow_owned,
625 |ancestor| {
694 |ancestor| {
626 if !had_entry {
695 if !had_entry {
627 ancestor.descendants_with_entry_count += 1;
696 ancestor.descendants_with_entry_count += 1;
628 }
697 }
629
698
630 ancestor.tracked_descendants_count += tracked_count_increment;
699 ancestor.tracked_descendants_count += tracked_count_increment;
631 },
700 },
632 )?;
701 )?;
633 let new_entry = if let Some(old_entry) = old_entry_opt {
702 let new_entry = if let Some(old_entry) = old_entry_opt {
634 let mut e = old_entry.clone();
703 let mut e = old_entry.clone();
635 if e.tracked() {
704 if e.tracked() {
636 // XXX
705 // XXX
637 // This is probably overkill for more case, but we need this to
706 // This is probably overkill for more case, but we need this to
638 // fully replace the `normallookup` call with `set_tracked`
707 // fully replace the `normallookup` call with `set_tracked`
639 // one. Consider smoothing this in the future.
708 // one. Consider smoothing this in the future.
640 e.set_possibly_dirty();
709 e.set_possibly_dirty();
641 } else {
710 } else {
642 new = true;
711 new = true;
643 e.set_tracked();
712 e.set_tracked();
644 }
713 }
645 e
714 e
646 } else {
715 } else {
647 self.nodes_with_entry_count += 1;
716 self.nodes_with_entry_count += 1;
648 new = true;
717 new = true;
649 DirstateEntry::new_tracked()
718 DirstateEntry::new_tracked()
650 };
719 };
651 node.data = NodeData::Entry(new_entry);
720 node.data = NodeData::Entry(new_entry);
652 Ok(new)
721 Ok(new)
653 }
722 }
654
723
655 fn add_or_remove_file(
724 fn add_or_remove_file(
656 &mut self,
725 &mut self,
657 path: &HgPath,
726 path: &HgPath,
658 old_state: Option<EntryState>,
727 old_state: Option<EntryState>,
659 new_entry: DirstateEntry,
728 new_entry: DirstateEntry,
660 ) -> Result<(), DirstateV2ParseError> {
729 ) -> Result<(), DirstateV2ParseError> {
661 let had_entry = old_state.is_some();
730 let had_entry = old_state.is_some();
662 let was_tracked = old_state.map_or(false, |s| s.is_tracked());
731 let was_tracked = old_state.map_or(false, |s| s.is_tracked());
663 let tracked_count_increment =
732 let tracked_count_increment =
664 match (was_tracked, new_entry.state().is_tracked()) {
733 match (was_tracked, new_entry.state().is_tracked()) {
665 (false, true) => 1,
734 (false, true) => 1,
666 (true, false) => -1,
735 (true, false) => -1,
667 _ => 0,
736 _ => 0,
668 };
737 };
669
738
670 let node = Self::get_or_insert_node(
739 let node = Self::get_or_insert_node(
671 self.on_disk,
740 self.on_disk,
672 &mut self.unreachable_bytes,
741 &mut self.unreachable_bytes,
673 &mut self.root,
742 &mut self.root,
674 path,
743 path,
675 WithBasename::to_cow_owned,
744 WithBasename::to_cow_owned,
676 |ancestor| {
745 |ancestor| {
677 if !had_entry {
746 if !had_entry {
678 ancestor.descendants_with_entry_count += 1;
747 ancestor.descendants_with_entry_count += 1;
679 }
748 }
680
749
681 // We can’t use `+= increment` because the counter is unsigned,
750 // We can’t use `+= increment` because the counter is unsigned,
682 // and we want debug builds to detect accidental underflow
751 // and we want debug builds to detect accidental underflow
683 // through zero
752 // through zero
684 match tracked_count_increment {
753 match tracked_count_increment {
685 1 => ancestor.tracked_descendants_count += 1,
754 1 => ancestor.tracked_descendants_count += 1,
686 -1 => ancestor.tracked_descendants_count -= 1,
755 -1 => ancestor.tracked_descendants_count -= 1,
687 _ => {}
756 _ => {}
688 }
757 }
689 },
758 },
690 )?;
759 )?;
691 if !had_entry {
760 if !had_entry {
692 self.nodes_with_entry_count += 1
761 self.nodes_with_entry_count += 1
693 }
762 }
694 node.data = NodeData::Entry(new_entry);
763 node.data = NodeData::Entry(new_entry);
695 Ok(())
764 Ok(())
696 }
765 }
697
766
698 fn iter_nodes<'tree>(
767 fn iter_nodes<'tree>(
699 &'tree self,
768 &'tree self,
700 ) -> impl Iterator<
769 ) -> impl Iterator<
701 Item = Result<NodeRef<'tree, 'on_disk>, DirstateV2ParseError>,
770 Item = Result<NodeRef<'tree, 'on_disk>, DirstateV2ParseError>,
702 > + 'tree {
771 > + 'tree {
703 // Depth first tree traversal.
772 // Depth first tree traversal.
704 //
773 //
705 // If we could afford internal iteration and recursion,
774 // If we could afford internal iteration and recursion,
706 // this would look like:
775 // this would look like:
707 //
776 //
708 // ```
777 // ```
709 // fn traverse_children(
778 // fn traverse_children(
710 // children: &ChildNodes,
779 // children: &ChildNodes,
711 // each: &mut impl FnMut(&Node),
780 // each: &mut impl FnMut(&Node),
712 // ) {
781 // ) {
713 // for child in children.values() {
782 // for child in children.values() {
714 // traverse_children(&child.children, each);
783 // traverse_children(&child.children, each);
715 // each(child);
784 // each(child);
716 // }
785 // }
717 // }
786 // }
718 // ```
787 // ```
719 //
788 //
720 // However we want an external iterator and therefore can’t use the
789 // However we want an external iterator and therefore can’t use the
721 // call stack. Use an explicit stack instead:
790 // call stack. Use an explicit stack instead:
722 let mut stack = Vec::new();
791 let mut stack = Vec::new();
723 let mut iter = self.root.as_ref().iter();
792 let mut iter = self.root.as_ref().iter();
724 std::iter::from_fn(move || {
793 std::iter::from_fn(move || {
725 while let Some(child_node) = iter.next() {
794 while let Some(child_node) = iter.next() {
726 let children = match child_node.children(self.on_disk) {
795 let children = match child_node.children(self.on_disk) {
727 Ok(children) => children,
796 Ok(children) => children,
728 Err(error) => return Some(Err(error)),
797 Err(error) => return Some(Err(error)),
729 };
798 };
730 // Pseudo-recursion
799 // Pseudo-recursion
731 let new_iter = children.iter();
800 let new_iter = children.iter();
732 let old_iter = std::mem::replace(&mut iter, new_iter);
801 let old_iter = std::mem::replace(&mut iter, new_iter);
733 stack.push((child_node, old_iter));
802 stack.push((child_node, old_iter));
734 }
803 }
735 // Found the end of a `children.iter()` iterator.
804 // Found the end of a `children.iter()` iterator.
736 if let Some((child_node, next_iter)) = stack.pop() {
805 if let Some((child_node, next_iter)) = stack.pop() {
737 // "Return" from pseudo-recursion by restoring state from the
806 // "Return" from pseudo-recursion by restoring state from the
738 // explicit stack
807 // explicit stack
739 iter = next_iter;
808 iter = next_iter;
740
809
741 Some(Ok(child_node))
810 Some(Ok(child_node))
742 } else {
811 } else {
743 // Reached the bottom of the stack, we’re done
812 // Reached the bottom of the stack, we’re done
744 None
813 None
745 }
814 }
746 })
815 })
747 }
816 }
748
817
749 fn count_dropped_path(unreachable_bytes: &mut u32, path: &Cow<HgPath>) {
818 fn count_dropped_path(unreachable_bytes: &mut u32, path: &Cow<HgPath>) {
750 if let Cow::Borrowed(path) = path {
819 if let Cow::Borrowed(path) = path {
751 *unreachable_bytes += path.len() as u32
820 *unreachable_bytes += path.len() as u32
752 }
821 }
753 }
822 }
754 }
823 }
755
824
756 /// Like `Iterator::filter_map`, but over a fallible iterator of `Result`s.
825 /// Like `Iterator::filter_map`, but over a fallible iterator of `Result`s.
757 ///
826 ///
758 /// The callback is only called for incoming `Ok` values. Errors are passed
827 /// The callback is only called for incoming `Ok` values. Errors are passed
759 /// through as-is. In order to let it use the `?` operator the callback is
828 /// through as-is. In order to let it use the `?` operator the callback is
760 /// expected to return a `Result` of `Option`, instead of an `Option` of
829 /// expected to return a `Result` of `Option`, instead of an `Option` of
761 /// `Result`.
830 /// `Result`.
762 fn filter_map_results<'a, I, F, A, B, E>(
831 fn filter_map_results<'a, I, F, A, B, E>(
763 iter: I,
832 iter: I,
764 f: F,
833 f: F,
765 ) -> impl Iterator<Item = Result<B, E>> + 'a
834 ) -> impl Iterator<Item = Result<B, E>> + 'a
766 where
835 where
767 I: Iterator<Item = Result<A, E>> + 'a,
836 I: Iterator<Item = Result<A, E>> + 'a,
768 F: Fn(A) -> Result<Option<B>, E> + 'a,
837 F: Fn(A) -> Result<Option<B>, E> + 'a,
769 {
838 {
770 iter.filter_map(move |result| match result {
839 iter.filter_map(move |result| match result {
771 Ok(node) => f(node).transpose(),
840 Ok(node) => f(node).transpose(),
772 Err(e) => Some(Err(e)),
841 Err(e) => Some(Err(e)),
773 })
842 })
774 }
843 }
775
844
776 impl OwningDirstateMap {
845 impl OwningDirstateMap {
777 pub fn clear(&mut self) {
846 pub fn clear(&mut self) {
778 self.with_dmap_mut(|map| {
847 self.with_dmap_mut(|map| {
779 map.root = Default::default();
848 map.root = Default::default();
780 map.nodes_with_entry_count = 0;
849 map.nodes_with_entry_count = 0;
781 map.nodes_with_copy_source_count = 0;
850 map.nodes_with_copy_source_count = 0;
782 });
851 });
783 }
852 }
784
853
785 pub fn set_entry(
854 pub fn set_entry(
786 &mut self,
855 &mut self,
787 filename: &HgPath,
856 filename: &HgPath,
788 entry: DirstateEntry,
857 entry: DirstateEntry,
789 ) -> Result<(), DirstateV2ParseError> {
858 ) -> Result<(), DirstateV2ParseError> {
790 self.with_dmap_mut(|map| {
859 self.with_dmap_mut(|map| {
791 map.get_or_insert(&filename)?.data = NodeData::Entry(entry);
860 map.get_or_insert(&filename)?.data = NodeData::Entry(entry);
792 Ok(())
861 Ok(())
793 })
862 })
794 }
863 }
795
864
796 pub fn add_file(
865 pub fn add_file(
797 &mut self,
866 &mut self,
798 filename: &HgPath,
867 filename: &HgPath,
799 entry: DirstateEntry,
868 entry: DirstateEntry,
800 ) -> Result<(), DirstateError> {
869 ) -> Result<(), DirstateError> {
801 let old_state = self.get(filename)?.map(|e| e.state());
870 let old_state = self.get(filename)?.map(|e| e.state());
802 self.with_dmap_mut(|map| {
871 self.with_dmap_mut(|map| {
803 Ok(map.add_or_remove_file(filename, old_state, entry)?)
872 Ok(map.add_or_remove_file(filename, old_state, entry)?)
804 })
873 })
805 }
874 }
806
875
807 pub fn set_tracked(
876 pub fn set_tracked(
808 &mut self,
877 &mut self,
809 filename: &HgPath,
878 filename: &HgPath,
810 ) -> Result<bool, DirstateV2ParseError> {
879 ) -> Result<bool, DirstateV2ParseError> {
811 let old_entry_opt = self.get(filename)?;
880 let old_entry_opt = self.get(filename)?;
812 self.with_dmap_mut(|map| map.set_tracked(filename, old_entry_opt))
881 self.with_dmap_mut(|map| map.set_tracked(filename, old_entry_opt))
813 }
882 }
814
883
884 pub fn reset_state(
885 &mut self,
886 filename: &HgPath,
887 wc_tracked: bool,
888 p1_tracked: bool,
889 p2_info: bool,
890 has_meaningful_mtime: bool,
891 parent_file_data_opt: Option<ParentFileData>,
892 ) -> Result<(), DirstateError> {
893 if !(p1_tracked || p2_info || wc_tracked) {
894 self.drop_entry_and_copy_source(filename)?;
895 return Ok(());
896 }
897 self.copy_map_remove(filename)?;
898 let old_entry_opt = self.get(filename)?;
899 self.with_dmap_mut(|map| {
900 map.reset_state(
901 filename,
902 old_entry_opt,
903 wc_tracked,
904 p1_tracked,
905 p2_info,
906 has_meaningful_mtime,
907 parent_file_data_opt,
908 )
909 })
910 }
911
815 pub fn remove_file(
912 pub fn remove_file(
816 &mut self,
913 &mut self,
817 filename: &HgPath,
914 filename: &HgPath,
818 in_merge: bool,
915 in_merge: bool,
819 ) -> Result<(), DirstateError> {
916 ) -> Result<(), DirstateError> {
820 let old_entry_opt = self.get(filename)?;
917 let old_entry_opt = self.get(filename)?;
821 let old_state = old_entry_opt.map(|e| e.state());
918 let old_state = old_entry_opt.map(|e| e.state());
822 let mut size = 0;
919 let mut size = 0;
823 if in_merge {
920 if in_merge {
824 // XXX we should not be able to have 'm' state and 'FROM_P2' if not
921 // XXX we should not be able to have 'm' state and 'FROM_P2' if not
825 // during a merge. So I (marmoute) am not sure we need the
922 // during a merge. So I (marmoute) am not sure we need the
826 // conditionnal at all. Adding double checking this with assert
923 // conditionnal at all. Adding double checking this with assert
827 // would be nice.
924 // would be nice.
828 if let Some(old_entry) = old_entry_opt {
925 if let Some(old_entry) = old_entry_opt {
829 // backup the previous state
926 // backup the previous state
830 if old_entry.state() == EntryState::Merged {
927 if old_entry.state() == EntryState::Merged {
831 size = SIZE_NON_NORMAL;
928 size = SIZE_NON_NORMAL;
832 } else if old_entry.state() == EntryState::Normal
929 } else if old_entry.state() == EntryState::Normal
833 && old_entry.size() == SIZE_FROM_OTHER_PARENT
930 && old_entry.size() == SIZE_FROM_OTHER_PARENT
834 {
931 {
835 // other parent
932 // other parent
836 size = SIZE_FROM_OTHER_PARENT;
933 size = SIZE_FROM_OTHER_PARENT;
837 }
934 }
838 }
935 }
839 }
936 }
840 if size == 0 {
937 if size == 0 {
841 self.copy_map_remove(filename)?;
938 self.copy_map_remove(filename)?;
842 }
939 }
843 self.with_dmap_mut(|map| {
940 self.with_dmap_mut(|map| {
844 let entry = DirstateEntry::new_removed(size);
941 let entry = DirstateEntry::new_removed(size);
845 Ok(map.add_or_remove_file(filename, old_state, entry)?)
942 Ok(map.add_or_remove_file(filename, old_state, entry)?)
846 })
943 })
847 }
944 }
848
945
849 pub fn drop_entry_and_copy_source(
946 pub fn drop_entry_and_copy_source(
850 &mut self,
947 &mut self,
851 filename: &HgPath,
948 filename: &HgPath,
852 ) -> Result<(), DirstateError> {
949 ) -> Result<(), DirstateError> {
853 let was_tracked = self
950 let was_tracked = self
854 .get(filename)?
951 .get(filename)?
855 .map_or(false, |e| e.state().is_tracked());
952 .map_or(false, |e| e.state().is_tracked());
856 struct Dropped {
953 struct Dropped {
857 was_tracked: bool,
954 was_tracked: bool,
858 had_entry: bool,
955 had_entry: bool,
859 had_copy_source: bool,
956 had_copy_source: bool,
860 }
957 }
861
958
862 /// If this returns `Ok(Some((dropped, removed)))`, then
959 /// If this returns `Ok(Some((dropped, removed)))`, then
863 ///
960 ///
864 /// * `dropped` is about the leaf node that was at `filename`
961 /// * `dropped` is about the leaf node that was at `filename`
865 /// * `removed` is whether this particular level of recursion just
962 /// * `removed` is whether this particular level of recursion just
866 /// removed a node in `nodes`.
963 /// removed a node in `nodes`.
867 fn recur<'on_disk>(
964 fn recur<'on_disk>(
868 on_disk: &'on_disk [u8],
965 on_disk: &'on_disk [u8],
869 unreachable_bytes: &mut u32,
966 unreachable_bytes: &mut u32,
870 nodes: &mut ChildNodes<'on_disk>,
967 nodes: &mut ChildNodes<'on_disk>,
871 path: &HgPath,
968 path: &HgPath,
872 ) -> Result<Option<(Dropped, bool)>, DirstateV2ParseError> {
969 ) -> Result<Option<(Dropped, bool)>, DirstateV2ParseError> {
873 let (first_path_component, rest_of_path) =
970 let (first_path_component, rest_of_path) =
874 path.split_first_component();
971 path.split_first_component();
875 let nodes = nodes.make_mut(on_disk, unreachable_bytes)?;
972 let nodes = nodes.make_mut(on_disk, unreachable_bytes)?;
876 let node = if let Some(node) = nodes.get_mut(first_path_component)
973 let node = if let Some(node) = nodes.get_mut(first_path_component)
877 {
974 {
878 node
975 node
879 } else {
976 } else {
880 return Ok(None);
977 return Ok(None);
881 };
978 };
882 let dropped;
979 let dropped;
883 if let Some(rest) = rest_of_path {
980 if let Some(rest) = rest_of_path {
884 if let Some((d, removed)) = recur(
981 if let Some((d, removed)) = recur(
885 on_disk,
982 on_disk,
886 unreachable_bytes,
983 unreachable_bytes,
887 &mut node.children,
984 &mut node.children,
888 rest,
985 rest,
889 )? {
986 )? {
890 dropped = d;
987 dropped = d;
891 if dropped.had_entry {
988 if dropped.had_entry {
892 node.descendants_with_entry_count = node
989 node.descendants_with_entry_count = node
893 .descendants_with_entry_count
990 .descendants_with_entry_count
894 .checked_sub(1)
991 .checked_sub(1)
895 .expect(
992 .expect(
896 "descendants_with_entry_count should be >= 0",
993 "descendants_with_entry_count should be >= 0",
897 );
994 );
898 }
995 }
899 if dropped.was_tracked {
996 if dropped.was_tracked {
900 node.tracked_descendants_count = node
997 node.tracked_descendants_count = node
901 .tracked_descendants_count
998 .tracked_descendants_count
902 .checked_sub(1)
999 .checked_sub(1)
903 .expect(
1000 .expect(
904 "tracked_descendants_count should be >= 0",
1001 "tracked_descendants_count should be >= 0",
905 );
1002 );
906 }
1003 }
907
1004
908 // Directory caches must be invalidated when removing a
1005 // Directory caches must be invalidated when removing a
909 // child node
1006 // child node
910 if removed {
1007 if removed {
911 if let NodeData::CachedDirectory { .. } = &node.data {
1008 if let NodeData::CachedDirectory { .. } = &node.data {
912 node.data = NodeData::None
1009 node.data = NodeData::None
913 }
1010 }
914 }
1011 }
915 } else {
1012 } else {
916 return Ok(None);
1013 return Ok(None);
917 }
1014 }
918 } else {
1015 } else {
919 let entry = node.data.as_entry();
1016 let entry = node.data.as_entry();
920 let was_tracked = entry.map_or(false, |entry| entry.tracked());
1017 let was_tracked = entry.map_or(false, |entry| entry.tracked());
921 let had_entry = entry.is_some();
1018 let had_entry = entry.is_some();
922 if had_entry {
1019 if had_entry {
923 node.data = NodeData::None
1020 node.data = NodeData::None
924 }
1021 }
925 let mut had_copy_source = false;
1022 let mut had_copy_source = false;
926 if let Some(source) = &node.copy_source {
1023 if let Some(source) = &node.copy_source {
927 DirstateMap::count_dropped_path(unreachable_bytes, source);
1024 DirstateMap::count_dropped_path(unreachable_bytes, source);
928 had_copy_source = true;
1025 had_copy_source = true;
929 node.copy_source = None
1026 node.copy_source = None
930 }
1027 }
931 dropped = Dropped {
1028 dropped = Dropped {
932 was_tracked,
1029 was_tracked,
933 had_entry,
1030 had_entry,
934 had_copy_source,
1031 had_copy_source,
935 };
1032 };
936 }
1033 }
937 // After recursion, for both leaf (rest_of_path is None) nodes and
1034 // After recursion, for both leaf (rest_of_path is None) nodes and
938 // parent nodes, remove a node if it just became empty.
1035 // parent nodes, remove a node if it just became empty.
939 let remove = !node.data.has_entry()
1036 let remove = !node.data.has_entry()
940 && node.copy_source.is_none()
1037 && node.copy_source.is_none()
941 && node.children.is_empty();
1038 && node.children.is_empty();
942 if remove {
1039 if remove {
943 let (key, _) =
1040 let (key, _) =
944 nodes.remove_entry(first_path_component).unwrap();
1041 nodes.remove_entry(first_path_component).unwrap();
945 DirstateMap::count_dropped_path(
1042 DirstateMap::count_dropped_path(
946 unreachable_bytes,
1043 unreachable_bytes,
947 key.full_path(),
1044 key.full_path(),
948 )
1045 )
949 }
1046 }
950 Ok(Some((dropped, remove)))
1047 Ok(Some((dropped, remove)))
951 }
1048 }
952
1049
953 self.with_dmap_mut(|map| {
1050 self.with_dmap_mut(|map| {
954 if let Some((dropped, _removed)) = recur(
1051 if let Some((dropped, _removed)) = recur(
955 map.on_disk,
1052 map.on_disk,
956 &mut map.unreachable_bytes,
1053 &mut map.unreachable_bytes,
957 &mut map.root,
1054 &mut map.root,
958 filename,
1055 filename,
959 )? {
1056 )? {
960 if dropped.had_entry {
1057 if dropped.had_entry {
961 map.nodes_with_entry_count = map
1058 map.nodes_with_entry_count = map
962 .nodes_with_entry_count
1059 .nodes_with_entry_count
963 .checked_sub(1)
1060 .checked_sub(1)
964 .expect("nodes_with_entry_count should be >= 0");
1061 .expect("nodes_with_entry_count should be >= 0");
965 }
1062 }
966 if dropped.had_copy_source {
1063 if dropped.had_copy_source {
967 map.nodes_with_copy_source_count = map
1064 map.nodes_with_copy_source_count = map
968 .nodes_with_copy_source_count
1065 .nodes_with_copy_source_count
969 .checked_sub(1)
1066 .checked_sub(1)
970 .expect("nodes_with_copy_source_count should be >= 0");
1067 .expect("nodes_with_copy_source_count should be >= 0");
971 }
1068 }
972 } else {
1069 } else {
973 debug_assert!(!was_tracked);
1070 debug_assert!(!was_tracked);
974 }
1071 }
975 Ok(())
1072 Ok(())
976 })
1073 })
977 }
1074 }
978
1075
979 pub fn has_tracked_dir(
1076 pub fn has_tracked_dir(
980 &mut self,
1077 &mut self,
981 directory: &HgPath,
1078 directory: &HgPath,
982 ) -> Result<bool, DirstateError> {
1079 ) -> Result<bool, DirstateError> {
983 self.with_dmap_mut(|map| {
1080 self.with_dmap_mut(|map| {
984 if let Some(node) = map.get_node(directory)? {
1081 if let Some(node) = map.get_node(directory)? {
985 // A node without a `DirstateEntry` was created to hold child
1082 // A node without a `DirstateEntry` was created to hold child
986 // nodes, and is therefore a directory.
1083 // nodes, and is therefore a directory.
987 let state = node.state()?;
1084 let state = node.state()?;
988 Ok(state.is_none() && node.tracked_descendants_count() > 0)
1085 Ok(state.is_none() && node.tracked_descendants_count() > 0)
989 } else {
1086 } else {
990 Ok(false)
1087 Ok(false)
991 }
1088 }
992 })
1089 })
993 }
1090 }
994
1091
995 pub fn has_dir(
1092 pub fn has_dir(
996 &mut self,
1093 &mut self,
997 directory: &HgPath,
1094 directory: &HgPath,
998 ) -> Result<bool, DirstateError> {
1095 ) -> Result<bool, DirstateError> {
999 self.with_dmap_mut(|map| {
1096 self.with_dmap_mut(|map| {
1000 if let Some(node) = map.get_node(directory)? {
1097 if let Some(node) = map.get_node(directory)? {
1001 // A node without a `DirstateEntry` was created to hold child
1098 // A node without a `DirstateEntry` was created to hold child
1002 // nodes, and is therefore a directory.
1099 // nodes, and is therefore a directory.
1003 let state = node.state()?;
1100 let state = node.state()?;
1004 Ok(state.is_none() && node.descendants_with_entry_count() > 0)
1101 Ok(state.is_none() && node.descendants_with_entry_count() > 0)
1005 } else {
1102 } else {
1006 Ok(false)
1103 Ok(false)
1007 }
1104 }
1008 })
1105 })
1009 }
1106 }
1010
1107
1011 #[timed]
1108 #[timed]
1012 pub fn pack_v1(
1109 pub fn pack_v1(
1013 &self,
1110 &self,
1014 parents: DirstateParents,
1111 parents: DirstateParents,
1015 ) -> Result<Vec<u8>, DirstateError> {
1112 ) -> Result<Vec<u8>, DirstateError> {
1016 let map = self.get_map();
1113 let map = self.get_map();
1017 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
1114 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
1018 // reallocations
1115 // reallocations
1019 let mut size = parents.as_bytes().len();
1116 let mut size = parents.as_bytes().len();
1020 for node in map.iter_nodes() {
1117 for node in map.iter_nodes() {
1021 let node = node?;
1118 let node = node?;
1022 if node.entry()?.is_some() {
1119 if node.entry()?.is_some() {
1023 size += packed_entry_size(
1120 size += packed_entry_size(
1024 node.full_path(map.on_disk)?,
1121 node.full_path(map.on_disk)?,
1025 node.copy_source(map.on_disk)?,
1122 node.copy_source(map.on_disk)?,
1026 );
1123 );
1027 }
1124 }
1028 }
1125 }
1029
1126
1030 let mut packed = Vec::with_capacity(size);
1127 let mut packed = Vec::with_capacity(size);
1031 packed.extend(parents.as_bytes());
1128 packed.extend(parents.as_bytes());
1032
1129
1033 for node in map.iter_nodes() {
1130 for node in map.iter_nodes() {
1034 let node = node?;
1131 let node = node?;
1035 if let Some(entry) = node.entry()? {
1132 if let Some(entry) = node.entry()? {
1036 pack_entry(
1133 pack_entry(
1037 node.full_path(map.on_disk)?,
1134 node.full_path(map.on_disk)?,
1038 &entry,
1135 &entry,
1039 node.copy_source(map.on_disk)?,
1136 node.copy_source(map.on_disk)?,
1040 &mut packed,
1137 &mut packed,
1041 );
1138 );
1042 }
1139 }
1043 }
1140 }
1044 Ok(packed)
1141 Ok(packed)
1045 }
1142 }
1046
1143
1047 /// Returns new data and metadata together with whether that data should be
1144 /// Returns new data and metadata together with whether that data should be
1048 /// appended to the existing data file whose content is at
1145 /// appended to the existing data file whose content is at
1049 /// `map.on_disk` (true), instead of written to a new data file
1146 /// `map.on_disk` (true), instead of written to a new data file
1050 /// (false).
1147 /// (false).
1051 #[timed]
1148 #[timed]
1052 pub fn pack_v2(
1149 pub fn pack_v2(
1053 &self,
1150 &self,
1054 can_append: bool,
1151 can_append: bool,
1055 ) -> Result<(Vec<u8>, on_disk::TreeMetadata, bool), DirstateError> {
1152 ) -> Result<(Vec<u8>, on_disk::TreeMetadata, bool), DirstateError> {
1056 let map = self.get_map();
1153 let map = self.get_map();
1057 on_disk::write(map, can_append)
1154 on_disk::write(map, can_append)
1058 }
1155 }
1059
1156
1060 /// `callback` allows the caller to process and do something with the
1157 /// `callback` allows the caller to process and do something with the
1061 /// results of the status. This is needed to do so efficiently (i.e.
1158 /// results of the status. This is needed to do so efficiently (i.e.
1062 /// without cloning the `DirstateStatus` object with its paths) because
1159 /// without cloning the `DirstateStatus` object with its paths) because
1063 /// we need to borrow from `Self`.
1160 /// we need to borrow from `Self`.
1064 pub fn with_status<R>(
1161 pub fn with_status<R>(
1065 &mut self,
1162 &mut self,
1066 matcher: &(dyn Matcher + Sync),
1163 matcher: &(dyn Matcher + Sync),
1067 root_dir: PathBuf,
1164 root_dir: PathBuf,
1068 ignore_files: Vec<PathBuf>,
1165 ignore_files: Vec<PathBuf>,
1069 options: StatusOptions,
1166 options: StatusOptions,
1070 callback: impl for<'r> FnOnce(
1167 callback: impl for<'r> FnOnce(
1071 Result<(DirstateStatus<'r>, Vec<PatternFileWarning>), StatusError>,
1168 Result<(DirstateStatus<'r>, Vec<PatternFileWarning>), StatusError>,
1072 ) -> R,
1169 ) -> R,
1073 ) -> R {
1170 ) -> R {
1074 self.with_dmap_mut(|map| {
1171 self.with_dmap_mut(|map| {
1075 callback(super::status::status(
1172 callback(super::status::status(
1076 map,
1173 map,
1077 matcher,
1174 matcher,
1078 root_dir,
1175 root_dir,
1079 ignore_files,
1176 ignore_files,
1080 options,
1177 options,
1081 ))
1178 ))
1082 })
1179 })
1083 }
1180 }
1084
1181
1085 pub fn copy_map_len(&self) -> usize {
1182 pub fn copy_map_len(&self) -> usize {
1086 let map = self.get_map();
1183 let map = self.get_map();
1087 map.nodes_with_copy_source_count as usize
1184 map.nodes_with_copy_source_count as usize
1088 }
1185 }
1089
1186
1090 pub fn copy_map_iter(&self) -> CopyMapIter<'_> {
1187 pub fn copy_map_iter(&self) -> CopyMapIter<'_> {
1091 let map = self.get_map();
1188 let map = self.get_map();
1092 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1189 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1093 Ok(if let Some(source) = node.copy_source(map.on_disk)? {
1190 Ok(if let Some(source) = node.copy_source(map.on_disk)? {
1094 Some((node.full_path(map.on_disk)?, source))
1191 Some((node.full_path(map.on_disk)?, source))
1095 } else {
1192 } else {
1096 None
1193 None
1097 })
1194 })
1098 }))
1195 }))
1099 }
1196 }
1100
1197
1101 pub fn copy_map_contains_key(
1198 pub fn copy_map_contains_key(
1102 &self,
1199 &self,
1103 key: &HgPath,
1200 key: &HgPath,
1104 ) -> Result<bool, DirstateV2ParseError> {
1201 ) -> Result<bool, DirstateV2ParseError> {
1105 let map = self.get_map();
1202 let map = self.get_map();
1106 Ok(if let Some(node) = map.get_node(key)? {
1203 Ok(if let Some(node) = map.get_node(key)? {
1107 node.has_copy_source()
1204 node.has_copy_source()
1108 } else {
1205 } else {
1109 false
1206 false
1110 })
1207 })
1111 }
1208 }
1112
1209
1113 pub fn copy_map_get(
1210 pub fn copy_map_get(
1114 &self,
1211 &self,
1115 key: &HgPath,
1212 key: &HgPath,
1116 ) -> Result<Option<&HgPath>, DirstateV2ParseError> {
1213 ) -> Result<Option<&HgPath>, DirstateV2ParseError> {
1117 let map = self.get_map();
1214 let map = self.get_map();
1118 if let Some(node) = map.get_node(key)? {
1215 if let Some(node) = map.get_node(key)? {
1119 if let Some(source) = node.copy_source(map.on_disk)? {
1216 if let Some(source) = node.copy_source(map.on_disk)? {
1120 return Ok(Some(source));
1217 return Ok(Some(source));
1121 }
1218 }
1122 }
1219 }
1123 Ok(None)
1220 Ok(None)
1124 }
1221 }
1125
1222
1126 pub fn copy_map_remove(
1223 pub fn copy_map_remove(
1127 &mut self,
1224 &mut self,
1128 key: &HgPath,
1225 key: &HgPath,
1129 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1226 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1130 self.with_dmap_mut(|map| {
1227 self.with_dmap_mut(|map| {
1131 let count = &mut map.nodes_with_copy_source_count;
1228 let count = &mut map.nodes_with_copy_source_count;
1132 let unreachable_bytes = &mut map.unreachable_bytes;
1229 let unreachable_bytes = &mut map.unreachable_bytes;
1133 Ok(DirstateMap::get_node_mut(
1230 Ok(DirstateMap::get_node_mut(
1134 map.on_disk,
1231 map.on_disk,
1135 unreachable_bytes,
1232 unreachable_bytes,
1136 &mut map.root,
1233 &mut map.root,
1137 key,
1234 key,
1138 )?
1235 )?
1139 .and_then(|node| {
1236 .and_then(|node| {
1140 if let Some(source) = &node.copy_source {
1237 if let Some(source) = &node.copy_source {
1141 *count -= 1;
1238 *count -= 1;
1142 DirstateMap::count_dropped_path(unreachable_bytes, source);
1239 DirstateMap::count_dropped_path(unreachable_bytes, source);
1143 }
1240 }
1144 node.copy_source.take().map(Cow::into_owned)
1241 node.copy_source.take().map(Cow::into_owned)
1145 }))
1242 }))
1146 })
1243 })
1147 }
1244 }
1148
1245
1149 pub fn copy_map_insert(
1246 pub fn copy_map_insert(
1150 &mut self,
1247 &mut self,
1151 key: HgPathBuf,
1248 key: HgPathBuf,
1152 value: HgPathBuf,
1249 value: HgPathBuf,
1153 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1250 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1154 self.with_dmap_mut(|map| {
1251 self.with_dmap_mut(|map| {
1155 let node = DirstateMap::get_or_insert_node(
1252 let node = DirstateMap::get_or_insert_node(
1156 map.on_disk,
1253 map.on_disk,
1157 &mut map.unreachable_bytes,
1254 &mut map.unreachable_bytes,
1158 &mut map.root,
1255 &mut map.root,
1159 &key,
1256 &key,
1160 WithBasename::to_cow_owned,
1257 WithBasename::to_cow_owned,
1161 |_ancestor| {},
1258 |_ancestor| {},
1162 )?;
1259 )?;
1163 if node.copy_source.is_none() {
1260 if node.copy_source.is_none() {
1164 map.nodes_with_copy_source_count += 1
1261 map.nodes_with_copy_source_count += 1
1165 }
1262 }
1166 Ok(node.copy_source.replace(value.into()).map(Cow::into_owned))
1263 Ok(node.copy_source.replace(value.into()).map(Cow::into_owned))
1167 })
1264 })
1168 }
1265 }
1169
1266
1170 pub fn len(&self) -> usize {
1267 pub fn len(&self) -> usize {
1171 let map = self.get_map();
1268 let map = self.get_map();
1172 map.nodes_with_entry_count as usize
1269 map.nodes_with_entry_count as usize
1173 }
1270 }
1174
1271
1175 pub fn contains_key(
1272 pub fn contains_key(
1176 &self,
1273 &self,
1177 key: &HgPath,
1274 key: &HgPath,
1178 ) -> Result<bool, DirstateV2ParseError> {
1275 ) -> Result<bool, DirstateV2ParseError> {
1179 Ok(self.get(key)?.is_some())
1276 Ok(self.get(key)?.is_some())
1180 }
1277 }
1181
1278
1182 pub fn get(
1279 pub fn get(
1183 &self,
1280 &self,
1184 key: &HgPath,
1281 key: &HgPath,
1185 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
1282 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
1186 let map = self.get_map();
1283 let map = self.get_map();
1187 Ok(if let Some(node) = map.get_node(key)? {
1284 Ok(if let Some(node) = map.get_node(key)? {
1188 node.entry()?
1285 node.entry()?
1189 } else {
1286 } else {
1190 None
1287 None
1191 })
1288 })
1192 }
1289 }
1193
1290
1194 pub fn iter(&self) -> StateMapIter<'_> {
1291 pub fn iter(&self) -> StateMapIter<'_> {
1195 let map = self.get_map();
1292 let map = self.get_map();
1196 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1293 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1197 Ok(if let Some(entry) = node.entry()? {
1294 Ok(if let Some(entry) = node.entry()? {
1198 Some((node.full_path(map.on_disk)?, entry))
1295 Some((node.full_path(map.on_disk)?, entry))
1199 } else {
1296 } else {
1200 None
1297 None
1201 })
1298 })
1202 }))
1299 }))
1203 }
1300 }
1204
1301
1205 pub fn iter_tracked_dirs(
1302 pub fn iter_tracked_dirs(
1206 &mut self,
1303 &mut self,
1207 ) -> Result<
1304 ) -> Result<
1208 Box<
1305 Box<
1209 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>>
1306 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>>
1210 + Send
1307 + Send
1211 + '_,
1308 + '_,
1212 >,
1309 >,
1213 DirstateError,
1310 DirstateError,
1214 > {
1311 > {
1215 let map = self.get_map();
1312 let map = self.get_map();
1216 let on_disk = map.on_disk;
1313 let on_disk = map.on_disk;
1217 Ok(Box::new(filter_map_results(
1314 Ok(Box::new(filter_map_results(
1218 map.iter_nodes(),
1315 map.iter_nodes(),
1219 move |node| {
1316 move |node| {
1220 Ok(if node.tracked_descendants_count() > 0 {
1317 Ok(if node.tracked_descendants_count() > 0 {
1221 Some(node.full_path(on_disk)?)
1318 Some(node.full_path(on_disk)?)
1222 } else {
1319 } else {
1223 None
1320 None
1224 })
1321 })
1225 },
1322 },
1226 )))
1323 )))
1227 }
1324 }
1228
1325
1229 pub fn debug_iter(
1326 pub fn debug_iter(
1230 &self,
1327 &self,
1231 all: bool,
1328 all: bool,
1232 ) -> Box<
1329 ) -> Box<
1233 dyn Iterator<
1330 dyn Iterator<
1234 Item = Result<
1331 Item = Result<
1235 (&HgPath, (u8, i32, i32, i32)),
1332 (&HgPath, (u8, i32, i32, i32)),
1236 DirstateV2ParseError,
1333 DirstateV2ParseError,
1237 >,
1334 >,
1238 > + Send
1335 > + Send
1239 + '_,
1336 + '_,
1240 > {
1337 > {
1241 let map = self.get_map();
1338 let map = self.get_map();
1242 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1339 Box::new(filter_map_results(map.iter_nodes(), move |node| {
1243 let debug_tuple = if let Some(entry) = node.entry()? {
1340 let debug_tuple = if let Some(entry) = node.entry()? {
1244 entry.debug_tuple()
1341 entry.debug_tuple()
1245 } else if !all {
1342 } else if !all {
1246 return Ok(None);
1343 return Ok(None);
1247 } else if let Some(mtime) = node.cached_directory_mtime()? {
1344 } else if let Some(mtime) = node.cached_directory_mtime()? {
1248 (b' ', 0, -1, mtime.truncated_seconds() as i32)
1345 (b' ', 0, -1, mtime.truncated_seconds() as i32)
1249 } else {
1346 } else {
1250 (b' ', 0, -1, -1)
1347 (b' ', 0, -1, -1)
1251 };
1348 };
1252 Ok(Some((node.full_path(map.on_disk)?, debug_tuple)))
1349 Ok(Some((node.full_path(map.on_disk)?, debug_tuple)))
1253 }))
1350 }))
1254 }
1351 }
1255 }
1352 }
@@ -1,500 +1,550 b''
1 // dirstate_map.rs
1 // dirstate_map.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //! Bindings for the `hg::dirstate::dirstate_map` file provided by the
8 //! Bindings for the `hg::dirstate::dirstate_map` file provided by the
9 //! `hg-core` package.
9 //! `hg-core` package.
10
10
11 use std::cell::{RefCell, RefMut};
11 use std::cell::{RefCell, RefMut};
12 use std::convert::TryInto;
12 use std::convert::TryInto;
13
13
14 use cpython::{
14 use cpython::{
15 exc, PyBool, PyBytes, PyClone, PyDict, PyErr, PyList, PyNone, PyObject,
15 exc, PyBool, PyBytes, PyClone, PyDict, PyErr, PyList, PyNone, PyObject,
16 PyResult, Python, PythonObject, ToPyObject, UnsafePyLeaked,
16 PyResult, Python, PythonObject, ToPyObject, UnsafePyLeaked,
17 };
17 };
18 use hg::dirstate::{ParentFileData, TruncatedTimestamp};
18
19
19 use crate::{
20 use crate::{
20 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
21 dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
21 dirstate::item::DirstateItem,
22 dirstate::item::DirstateItem,
22 pybytes_deref::PyBytesDeref,
23 pybytes_deref::PyBytesDeref,
23 };
24 };
24 use hg::{
25 use hg::{
25 dirstate::StateMapIter,
26 dirstate::StateMapIter,
26 dirstate_tree::on_disk::DirstateV2ParseError,
27 dirstate_tree::on_disk::DirstateV2ParseError,
27 dirstate_tree::owning::OwningDirstateMap,
28 dirstate_tree::owning::OwningDirstateMap,
28 revlog::Node,
29 revlog::Node,
29 utils::files::normalize_case,
30 utils::files::normalize_case,
30 utils::hg_path::{HgPath, HgPathBuf},
31 utils::hg_path::{HgPath, HgPathBuf},
31 DirstateEntry, DirstateError, DirstateParents, EntryState,
32 DirstateEntry, DirstateError, DirstateParents, EntryState,
32 };
33 };
33
34
34 // TODO
35 // TODO
35 // This object needs to share references to multiple members of its Rust
36 // This object needs to share references to multiple members of its Rust
36 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
37 // inner struct, namely `copy_map`, `dirs` and `all_dirs`.
37 // Right now `CopyMap` is done, but it needs to have an explicit reference
38 // Right now `CopyMap` is done, but it needs to have an explicit reference
38 // to `RustDirstateMap` which itself needs to have an encapsulation for
39 // to `RustDirstateMap` which itself needs to have an encapsulation for
39 // every method in `CopyMap` (copymapcopy, etc.).
40 // every method in `CopyMap` (copymapcopy, etc.).
40 // This is ugly and hard to maintain.
41 // This is ugly and hard to maintain.
41 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
42 // The same logic applies to `dirs` and `all_dirs`, however the `Dirs`
42 // `py_class!` is already implemented and does not mention
43 // `py_class!` is already implemented and does not mention
43 // `RustDirstateMap`, rightfully so.
44 // `RustDirstateMap`, rightfully so.
44 // All attributes also have to have a separate refcount data attribute for
45 // All attributes also have to have a separate refcount data attribute for
45 // leaks, with all methods that go along for reference sharing.
46 // leaks, with all methods that go along for reference sharing.
46 py_class!(pub class DirstateMap |py| {
47 py_class!(pub class DirstateMap |py| {
47 @shared data inner: OwningDirstateMap;
48 @shared data inner: OwningDirstateMap;
48
49
49 /// Returns a `(dirstate_map, parents)` tuple
50 /// Returns a `(dirstate_map, parents)` tuple
50 @staticmethod
51 @staticmethod
51 def new_v1(
52 def new_v1(
52 on_disk: PyBytes,
53 on_disk: PyBytes,
53 ) -> PyResult<PyObject> {
54 ) -> PyResult<PyObject> {
54 let on_disk = PyBytesDeref::new(py, on_disk);
55 let on_disk = PyBytesDeref::new(py, on_disk);
55 let (map, parents) = OwningDirstateMap::new_v1(on_disk)
56 let (map, parents) = OwningDirstateMap::new_v1(on_disk)
56 .map_err(|e| dirstate_error(py, e))?;
57 .map_err(|e| dirstate_error(py, e))?;
57 let map = Self::create_instance(py, map)?;
58 let map = Self::create_instance(py, map)?;
58 let p1 = PyBytes::new(py, parents.p1.as_bytes());
59 let p1 = PyBytes::new(py, parents.p1.as_bytes());
59 let p2 = PyBytes::new(py, parents.p2.as_bytes());
60 let p2 = PyBytes::new(py, parents.p2.as_bytes());
60 let parents = (p1, p2);
61 let parents = (p1, p2);
61 Ok((map, parents).to_py_object(py).into_object())
62 Ok((map, parents).to_py_object(py).into_object())
62 }
63 }
63
64
64 /// Returns a DirstateMap
65 /// Returns a DirstateMap
65 @staticmethod
66 @staticmethod
66 def new_v2(
67 def new_v2(
67 on_disk: PyBytes,
68 on_disk: PyBytes,
68 data_size: usize,
69 data_size: usize,
69 tree_metadata: PyBytes,
70 tree_metadata: PyBytes,
70 ) -> PyResult<PyObject> {
71 ) -> PyResult<PyObject> {
71 let dirstate_error = |e: DirstateError| {
72 let dirstate_error = |e: DirstateError| {
72 PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e))
73 PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e))
73 };
74 };
74 let on_disk = PyBytesDeref::new(py, on_disk);
75 let on_disk = PyBytesDeref::new(py, on_disk);
75 let map = OwningDirstateMap::new_v2(
76 let map = OwningDirstateMap::new_v2(
76 on_disk, data_size, tree_metadata.data(py),
77 on_disk, data_size, tree_metadata.data(py),
77 ).map_err(dirstate_error)?;
78 ).map_err(dirstate_error)?;
78 let map = Self::create_instance(py, map)?;
79 let map = Self::create_instance(py, map)?;
79 Ok(map.into_object())
80 Ok(map.into_object())
80 }
81 }
81
82
82 def clear(&self) -> PyResult<PyObject> {
83 def clear(&self) -> PyResult<PyObject> {
83 self.inner(py).borrow_mut().clear();
84 self.inner(py).borrow_mut().clear();
84 Ok(py.None())
85 Ok(py.None())
85 }
86 }
86
87
87 def get(
88 def get(
88 &self,
89 &self,
89 key: PyObject,
90 key: PyObject,
90 default: Option<PyObject> = None
91 default: Option<PyObject> = None
91 ) -> PyResult<Option<PyObject>> {
92 ) -> PyResult<Option<PyObject>> {
92 let key = key.extract::<PyBytes>(py)?;
93 let key = key.extract::<PyBytes>(py)?;
93 match self
94 match self
94 .inner(py)
95 .inner(py)
95 .borrow()
96 .borrow()
96 .get(HgPath::new(key.data(py)))
97 .get(HgPath::new(key.data(py)))
97 .map_err(|e| v2_error(py, e))?
98 .map_err(|e| v2_error(py, e))?
98 {
99 {
99 Some(entry) => {
100 Some(entry) => {
100 Ok(Some(DirstateItem::new_as_pyobject(py, entry)?))
101 Ok(Some(DirstateItem::new_as_pyobject(py, entry)?))
101 },
102 },
102 None => Ok(default)
103 None => Ok(default)
103 }
104 }
104 }
105 }
105
106
106 def set_dirstate_item(
107 def set_dirstate_item(
107 &self,
108 &self,
108 path: PyObject,
109 path: PyObject,
109 item: DirstateItem
110 item: DirstateItem
110 ) -> PyResult<PyObject> {
111 ) -> PyResult<PyObject> {
111 let f = path.extract::<PyBytes>(py)?;
112 let f = path.extract::<PyBytes>(py)?;
112 let filename = HgPath::new(f.data(py));
113 let filename = HgPath::new(f.data(py));
113 self.inner(py)
114 self.inner(py)
114 .borrow_mut()
115 .borrow_mut()
115 .set_entry(filename, item.get_entry(py))
116 .set_entry(filename, item.get_entry(py))
116 .map_err(|e| v2_error(py, e))?;
117 .map_err(|e| v2_error(py, e))?;
117 Ok(py.None())
118 Ok(py.None())
118 }
119 }
119
120
120 def addfile(
121 def addfile(
121 &self,
122 &self,
122 f: PyBytes,
123 f: PyBytes,
123 item: DirstateItem,
124 item: DirstateItem,
124 ) -> PyResult<PyNone> {
125 ) -> PyResult<PyNone> {
125 let filename = HgPath::new(f.data(py));
126 let filename = HgPath::new(f.data(py));
126 let entry = item.get_entry(py);
127 let entry = item.get_entry(py);
127 self.inner(py)
128 self.inner(py)
128 .borrow_mut()
129 .borrow_mut()
129 .add_file(filename, entry)
130 .add_file(filename, entry)
130 .map_err(|e |dirstate_error(py, e))?;
131 .map_err(|e |dirstate_error(py, e))?;
131 Ok(PyNone)
132 Ok(PyNone)
132 }
133 }
133
134
134 def set_tracked(&self, f: PyObject) -> PyResult<PyBool> {
135 def set_tracked(&self, f: PyObject) -> PyResult<PyBool> {
135 let bytes = f.extract::<PyBytes>(py)?;
136 let bytes = f.extract::<PyBytes>(py)?;
136 let path = HgPath::new(bytes.data(py));
137 let path = HgPath::new(bytes.data(py));
137 let res = self.inner(py).borrow_mut().set_tracked(path);
138 let res = self.inner(py).borrow_mut().set_tracked(path);
138 let was_tracked = res.or_else(|_| {
139 let was_tracked = res.or_else(|_| {
139 Err(PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))
140 Err(PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))
140 })?;
141 })?;
141 Ok(was_tracked.to_py_object(py))
142 Ok(was_tracked.to_py_object(py))
142 }
143 }
143
144
145 def reset_state(
146 &self,
147 f: PyObject,
148 wc_tracked: bool,
149 p1_tracked: bool,
150 p2_info: bool,
151 has_meaningful_mtime: bool,
152 parentfiledata: Option<(u32, u32, Option<(i64, u32, bool)>)>,
153 ) -> PyResult<PyNone> {
154 let mut has_meaningful_mtime = has_meaningful_mtime;
155 let parent_file_data = match parentfiledata {
156 None => {
157 has_meaningful_mtime = false;
158 None
159 },
160 Some(data) => {
161 let (mode, size, mtime_info) = data;
162 let mtime = if let Some(mtime_info) = mtime_info {
163 let (mtime_s, mtime_ns, second_ambiguous) = mtime_info;
164 let timestamp = TruncatedTimestamp::new_truncate(
165 mtime_s, mtime_ns, second_ambiguous
166 );
167 Some(timestamp)
168 } else {
169 has_meaningful_mtime = false;
170 None
171 };
172 Some(ParentFileData {
173 mode_size: Some((mode, size)),
174 mtime,
175 })
176 }
177 };
178 let bytes = f.extract::<PyBytes>(py)?;
179 let path = HgPath::new(bytes.data(py));
180 let res = self.inner(py).borrow_mut().reset_state(
181 path,
182 wc_tracked,
183 p1_tracked,
184 p2_info,
185 has_meaningful_mtime,
186 parent_file_data,
187 );
188 res.or_else(|_| {
189 Err(PyErr::new::<exc::OSError, _>(py, "Dirstate error".to_string()))
190 })?;
191 Ok(PyNone)
192 }
193
144 def removefile(
194 def removefile(
145 &self,
195 &self,
146 f: PyObject,
196 f: PyObject,
147 in_merge: PyObject
197 in_merge: PyObject
148 ) -> PyResult<PyObject> {
198 ) -> PyResult<PyObject> {
149 self.inner(py).borrow_mut()
199 self.inner(py).borrow_mut()
150 .remove_file(
200 .remove_file(
151 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
201 HgPath::new(f.extract::<PyBytes>(py)?.data(py)),
152 in_merge.extract::<PyBool>(py)?.is_true(),
202 in_merge.extract::<PyBool>(py)?.is_true(),
153 )
203 )
154 .or_else(|_| {
204 .or_else(|_| {
155 Err(PyErr::new::<exc::OSError, _>(
205 Err(PyErr::new::<exc::OSError, _>(
156 py,
206 py,
157 "Dirstate error".to_string(),
207 "Dirstate error".to_string(),
158 ))
208 ))
159 })?;
209 })?;
160 Ok(py.None())
210 Ok(py.None())
161 }
211 }
162
212
163 def drop_item_and_copy_source(
213 def drop_item_and_copy_source(
164 &self,
214 &self,
165 f: PyBytes,
215 f: PyBytes,
166 ) -> PyResult<PyNone> {
216 ) -> PyResult<PyNone> {
167 self.inner(py)
217 self.inner(py)
168 .borrow_mut()
218 .borrow_mut()
169 .drop_entry_and_copy_source(HgPath::new(f.data(py)))
219 .drop_entry_and_copy_source(HgPath::new(f.data(py)))
170 .map_err(|e |dirstate_error(py, e))?;
220 .map_err(|e |dirstate_error(py, e))?;
171 Ok(PyNone)
221 Ok(PyNone)
172 }
222 }
173
223
174 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
224 def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
175 let d = d.extract::<PyBytes>(py)?;
225 let d = d.extract::<PyBytes>(py)?;
176 Ok(self.inner(py).borrow_mut()
226 Ok(self.inner(py).borrow_mut()
177 .has_tracked_dir(HgPath::new(d.data(py)))
227 .has_tracked_dir(HgPath::new(d.data(py)))
178 .map_err(|e| {
228 .map_err(|e| {
179 PyErr::new::<exc::ValueError, _>(py, e.to_string())
229 PyErr::new::<exc::ValueError, _>(py, e.to_string())
180 })?
230 })?
181 .to_py_object(py))
231 .to_py_object(py))
182 }
232 }
183
233
184 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
234 def hasdir(&self, d: PyObject) -> PyResult<PyBool> {
185 let d = d.extract::<PyBytes>(py)?;
235 let d = d.extract::<PyBytes>(py)?;
186 Ok(self.inner(py).borrow_mut()
236 Ok(self.inner(py).borrow_mut()
187 .has_dir(HgPath::new(d.data(py)))
237 .has_dir(HgPath::new(d.data(py)))
188 .map_err(|e| {
238 .map_err(|e| {
189 PyErr::new::<exc::ValueError, _>(py, e.to_string())
239 PyErr::new::<exc::ValueError, _>(py, e.to_string())
190 })?
240 })?
191 .to_py_object(py))
241 .to_py_object(py))
192 }
242 }
193
243
194 def write_v1(
244 def write_v1(
195 &self,
245 &self,
196 p1: PyObject,
246 p1: PyObject,
197 p2: PyObject,
247 p2: PyObject,
198 ) -> PyResult<PyBytes> {
248 ) -> PyResult<PyBytes> {
199 let inner = self.inner(py).borrow();
249 let inner = self.inner(py).borrow();
200 let parents = DirstateParents {
250 let parents = DirstateParents {
201 p1: extract_node_id(py, &p1)?,
251 p1: extract_node_id(py, &p1)?,
202 p2: extract_node_id(py, &p2)?,
252 p2: extract_node_id(py, &p2)?,
203 };
253 };
204 let result = inner.pack_v1(parents);
254 let result = inner.pack_v1(parents);
205 match result {
255 match result {
206 Ok(packed) => Ok(PyBytes::new(py, &packed)),
256 Ok(packed) => Ok(PyBytes::new(py, &packed)),
207 Err(_) => Err(PyErr::new::<exc::OSError, _>(
257 Err(_) => Err(PyErr::new::<exc::OSError, _>(
208 py,
258 py,
209 "Dirstate error".to_string(),
259 "Dirstate error".to_string(),
210 )),
260 )),
211 }
261 }
212 }
262 }
213
263
214 /// Returns new data together with whether that data should be appended to
264 /// Returns new data together with whether that data should be appended to
215 /// the existing data file whose content is at `self.on_disk` (True),
265 /// the existing data file whose content is at `self.on_disk` (True),
216 /// instead of written to a new data file (False).
266 /// instead of written to a new data file (False).
217 def write_v2(
267 def write_v2(
218 &self,
268 &self,
219 can_append: bool,
269 can_append: bool,
220 ) -> PyResult<PyObject> {
270 ) -> PyResult<PyObject> {
221 let inner = self.inner(py).borrow();
271 let inner = self.inner(py).borrow();
222 let result = inner.pack_v2(can_append);
272 let result = inner.pack_v2(can_append);
223 match result {
273 match result {
224 Ok((packed, tree_metadata, append)) => {
274 Ok((packed, tree_metadata, append)) => {
225 let packed = PyBytes::new(py, &packed);
275 let packed = PyBytes::new(py, &packed);
226 let tree_metadata = PyBytes::new(py, tree_metadata.as_bytes());
276 let tree_metadata = PyBytes::new(py, tree_metadata.as_bytes());
227 let tuple = (packed, tree_metadata, append);
277 let tuple = (packed, tree_metadata, append);
228 Ok(tuple.to_py_object(py).into_object())
278 Ok(tuple.to_py_object(py).into_object())
229 },
279 },
230 Err(_) => Err(PyErr::new::<exc::OSError, _>(
280 Err(_) => Err(PyErr::new::<exc::OSError, _>(
231 py,
281 py,
232 "Dirstate error".to_string(),
282 "Dirstate error".to_string(),
233 )),
283 )),
234 }
284 }
235 }
285 }
236
286
237 def filefoldmapasdict(&self) -> PyResult<PyDict> {
287 def filefoldmapasdict(&self) -> PyResult<PyDict> {
238 let dict = PyDict::new(py);
288 let dict = PyDict::new(py);
239 for item in self.inner(py).borrow_mut().iter() {
289 for item in self.inner(py).borrow_mut().iter() {
240 let (path, entry) = item.map_err(|e| v2_error(py, e))?;
290 let (path, entry) = item.map_err(|e| v2_error(py, e))?;
241 if entry.state() != EntryState::Removed {
291 if entry.state() != EntryState::Removed {
242 let key = normalize_case(path);
292 let key = normalize_case(path);
243 let value = path;
293 let value = path;
244 dict.set_item(
294 dict.set_item(
245 py,
295 py,
246 PyBytes::new(py, key.as_bytes()).into_object(),
296 PyBytes::new(py, key.as_bytes()).into_object(),
247 PyBytes::new(py, value.as_bytes()).into_object(),
297 PyBytes::new(py, value.as_bytes()).into_object(),
248 )?;
298 )?;
249 }
299 }
250 }
300 }
251 Ok(dict)
301 Ok(dict)
252 }
302 }
253
303
254 def __len__(&self) -> PyResult<usize> {
304 def __len__(&self) -> PyResult<usize> {
255 Ok(self.inner(py).borrow().len())
305 Ok(self.inner(py).borrow().len())
256 }
306 }
257
307
258 def __contains__(&self, key: PyObject) -> PyResult<bool> {
308 def __contains__(&self, key: PyObject) -> PyResult<bool> {
259 let key = key.extract::<PyBytes>(py)?;
309 let key = key.extract::<PyBytes>(py)?;
260 self.inner(py)
310 self.inner(py)
261 .borrow()
311 .borrow()
262 .contains_key(HgPath::new(key.data(py)))
312 .contains_key(HgPath::new(key.data(py)))
263 .map_err(|e| v2_error(py, e))
313 .map_err(|e| v2_error(py, e))
264 }
314 }
265
315
266 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
316 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
267 let key = key.extract::<PyBytes>(py)?;
317 let key = key.extract::<PyBytes>(py)?;
268 let key = HgPath::new(key.data(py));
318 let key = HgPath::new(key.data(py));
269 match self
319 match self
270 .inner(py)
320 .inner(py)
271 .borrow()
321 .borrow()
272 .get(key)
322 .get(key)
273 .map_err(|e| v2_error(py, e))?
323 .map_err(|e| v2_error(py, e))?
274 {
324 {
275 Some(entry) => {
325 Some(entry) => {
276 Ok(DirstateItem::new_as_pyobject(py, entry)?)
326 Ok(DirstateItem::new_as_pyobject(py, entry)?)
277 },
327 },
278 None => Err(PyErr::new::<exc::KeyError, _>(
328 None => Err(PyErr::new::<exc::KeyError, _>(
279 py,
329 py,
280 String::from_utf8_lossy(key.as_bytes()),
330 String::from_utf8_lossy(key.as_bytes()),
281 )),
331 )),
282 }
332 }
283 }
333 }
284
334
285 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
335 def keys(&self) -> PyResult<DirstateMapKeysIterator> {
286 let leaked_ref = self.inner(py).leak_immutable();
336 let leaked_ref = self.inner(py).leak_immutable();
287 DirstateMapKeysIterator::from_inner(
337 DirstateMapKeysIterator::from_inner(
288 py,
338 py,
289 unsafe { leaked_ref.map(py, |o| o.iter()) },
339 unsafe { leaked_ref.map(py, |o| o.iter()) },
290 )
340 )
291 }
341 }
292
342
293 def items(&self) -> PyResult<DirstateMapItemsIterator> {
343 def items(&self) -> PyResult<DirstateMapItemsIterator> {
294 let leaked_ref = self.inner(py).leak_immutable();
344 let leaked_ref = self.inner(py).leak_immutable();
295 DirstateMapItemsIterator::from_inner(
345 DirstateMapItemsIterator::from_inner(
296 py,
346 py,
297 unsafe { leaked_ref.map(py, |o| o.iter()) },
347 unsafe { leaked_ref.map(py, |o| o.iter()) },
298 )
348 )
299 }
349 }
300
350
301 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
351 def __iter__(&self) -> PyResult<DirstateMapKeysIterator> {
302 let leaked_ref = self.inner(py).leak_immutable();
352 let leaked_ref = self.inner(py).leak_immutable();
303 DirstateMapKeysIterator::from_inner(
353 DirstateMapKeysIterator::from_inner(
304 py,
354 py,
305 unsafe { leaked_ref.map(py, |o| o.iter()) },
355 unsafe { leaked_ref.map(py, |o| o.iter()) },
306 )
356 )
307 }
357 }
308
358
309 // TODO all copymap* methods, see docstring above
359 // TODO all copymap* methods, see docstring above
310 def copymapcopy(&self) -> PyResult<PyDict> {
360 def copymapcopy(&self) -> PyResult<PyDict> {
311 let dict = PyDict::new(py);
361 let dict = PyDict::new(py);
312 for item in self.inner(py).borrow().copy_map_iter() {
362 for item in self.inner(py).borrow().copy_map_iter() {
313 let (key, value) = item.map_err(|e| v2_error(py, e))?;
363 let (key, value) = item.map_err(|e| v2_error(py, e))?;
314 dict.set_item(
364 dict.set_item(
315 py,
365 py,
316 PyBytes::new(py, key.as_bytes()),
366 PyBytes::new(py, key.as_bytes()),
317 PyBytes::new(py, value.as_bytes()),
367 PyBytes::new(py, value.as_bytes()),
318 )?;
368 )?;
319 }
369 }
320 Ok(dict)
370 Ok(dict)
321 }
371 }
322
372
323 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
373 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
324 let key = key.extract::<PyBytes>(py)?;
374 let key = key.extract::<PyBytes>(py)?;
325 match self
375 match self
326 .inner(py)
376 .inner(py)
327 .borrow()
377 .borrow()
328 .copy_map_get(HgPath::new(key.data(py)))
378 .copy_map_get(HgPath::new(key.data(py)))
329 .map_err(|e| v2_error(py, e))?
379 .map_err(|e| v2_error(py, e))?
330 {
380 {
331 Some(copy) => Ok(PyBytes::new(py, copy.as_bytes())),
381 Some(copy) => Ok(PyBytes::new(py, copy.as_bytes())),
332 None => Err(PyErr::new::<exc::KeyError, _>(
382 None => Err(PyErr::new::<exc::KeyError, _>(
333 py,
383 py,
334 String::from_utf8_lossy(key.data(py)),
384 String::from_utf8_lossy(key.data(py)),
335 )),
385 )),
336 }
386 }
337 }
387 }
338 def copymap(&self) -> PyResult<CopyMap> {
388 def copymap(&self) -> PyResult<CopyMap> {
339 CopyMap::from_inner(py, self.clone_ref(py))
389 CopyMap::from_inner(py, self.clone_ref(py))
340 }
390 }
341
391
342 def copymaplen(&self) -> PyResult<usize> {
392 def copymaplen(&self) -> PyResult<usize> {
343 Ok(self.inner(py).borrow().copy_map_len())
393 Ok(self.inner(py).borrow().copy_map_len())
344 }
394 }
345 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
395 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
346 let key = key.extract::<PyBytes>(py)?;
396 let key = key.extract::<PyBytes>(py)?;
347 self.inner(py)
397 self.inner(py)
348 .borrow()
398 .borrow()
349 .copy_map_contains_key(HgPath::new(key.data(py)))
399 .copy_map_contains_key(HgPath::new(key.data(py)))
350 .map_err(|e| v2_error(py, e))
400 .map_err(|e| v2_error(py, e))
351 }
401 }
352 def copymapget(
402 def copymapget(
353 &self,
403 &self,
354 key: PyObject,
404 key: PyObject,
355 default: Option<PyObject>
405 default: Option<PyObject>
356 ) -> PyResult<Option<PyObject>> {
406 ) -> PyResult<Option<PyObject>> {
357 let key = key.extract::<PyBytes>(py)?;
407 let key = key.extract::<PyBytes>(py)?;
358 match self
408 match self
359 .inner(py)
409 .inner(py)
360 .borrow()
410 .borrow()
361 .copy_map_get(HgPath::new(key.data(py)))
411 .copy_map_get(HgPath::new(key.data(py)))
362 .map_err(|e| v2_error(py, e))?
412 .map_err(|e| v2_error(py, e))?
363 {
413 {
364 Some(copy) => Ok(Some(
414 Some(copy) => Ok(Some(
365 PyBytes::new(py, copy.as_bytes()).into_object(),
415 PyBytes::new(py, copy.as_bytes()).into_object(),
366 )),
416 )),
367 None => Ok(default),
417 None => Ok(default),
368 }
418 }
369 }
419 }
370 def copymapsetitem(
420 def copymapsetitem(
371 &self,
421 &self,
372 key: PyObject,
422 key: PyObject,
373 value: PyObject
423 value: PyObject
374 ) -> PyResult<PyObject> {
424 ) -> PyResult<PyObject> {
375 let key = key.extract::<PyBytes>(py)?;
425 let key = key.extract::<PyBytes>(py)?;
376 let value = value.extract::<PyBytes>(py)?;
426 let value = value.extract::<PyBytes>(py)?;
377 self.inner(py)
427 self.inner(py)
378 .borrow_mut()
428 .borrow_mut()
379 .copy_map_insert(
429 .copy_map_insert(
380 HgPathBuf::from_bytes(key.data(py)),
430 HgPathBuf::from_bytes(key.data(py)),
381 HgPathBuf::from_bytes(value.data(py)),
431 HgPathBuf::from_bytes(value.data(py)),
382 )
432 )
383 .map_err(|e| v2_error(py, e))?;
433 .map_err(|e| v2_error(py, e))?;
384 Ok(py.None())
434 Ok(py.None())
385 }
435 }
386 def copymappop(
436 def copymappop(
387 &self,
437 &self,
388 key: PyObject,
438 key: PyObject,
389 default: Option<PyObject>
439 default: Option<PyObject>
390 ) -> PyResult<Option<PyObject>> {
440 ) -> PyResult<Option<PyObject>> {
391 let key = key.extract::<PyBytes>(py)?;
441 let key = key.extract::<PyBytes>(py)?;
392 match self
442 match self
393 .inner(py)
443 .inner(py)
394 .borrow_mut()
444 .borrow_mut()
395 .copy_map_remove(HgPath::new(key.data(py)))
445 .copy_map_remove(HgPath::new(key.data(py)))
396 .map_err(|e| v2_error(py, e))?
446 .map_err(|e| v2_error(py, e))?
397 {
447 {
398 Some(copy) => Ok(Some(
448 Some(copy) => Ok(Some(
399 PyBytes::new(py, copy.as_bytes()).into_object(),
449 PyBytes::new(py, copy.as_bytes()).into_object(),
400 )),
450 )),
401 None => Ok(default),
451 None => Ok(default),
402 }
452 }
403 }
453 }
404
454
405 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
455 def copymapiter(&self) -> PyResult<CopyMapKeysIterator> {
406 let leaked_ref = self.inner(py).leak_immutable();
456 let leaked_ref = self.inner(py).leak_immutable();
407 CopyMapKeysIterator::from_inner(
457 CopyMapKeysIterator::from_inner(
408 py,
458 py,
409 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
459 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
410 )
460 )
411 }
461 }
412
462
413 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
463 def copymapitemsiter(&self) -> PyResult<CopyMapItemsIterator> {
414 let leaked_ref = self.inner(py).leak_immutable();
464 let leaked_ref = self.inner(py).leak_immutable();
415 CopyMapItemsIterator::from_inner(
465 CopyMapItemsIterator::from_inner(
416 py,
466 py,
417 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
467 unsafe { leaked_ref.map(py, |o| o.copy_map_iter()) },
418 )
468 )
419 }
469 }
420
470
421 def tracked_dirs(&self) -> PyResult<PyList> {
471 def tracked_dirs(&self) -> PyResult<PyList> {
422 let dirs = PyList::new(py, &[]);
472 let dirs = PyList::new(py, &[]);
423 for path in self.inner(py).borrow_mut().iter_tracked_dirs()
473 for path in self.inner(py).borrow_mut().iter_tracked_dirs()
424 .map_err(|e |dirstate_error(py, e))?
474 .map_err(|e |dirstate_error(py, e))?
425 {
475 {
426 let path = path.map_err(|e| v2_error(py, e))?;
476 let path = path.map_err(|e| v2_error(py, e))?;
427 let path = PyBytes::new(py, path.as_bytes());
477 let path = PyBytes::new(py, path.as_bytes());
428 dirs.append(py, path.into_object())
478 dirs.append(py, path.into_object())
429 }
479 }
430 Ok(dirs)
480 Ok(dirs)
431 }
481 }
432
482
433 def debug_iter(&self, all: bool) -> PyResult<PyList> {
483 def debug_iter(&self, all: bool) -> PyResult<PyList> {
434 let dirs = PyList::new(py, &[]);
484 let dirs = PyList::new(py, &[]);
435 for item in self.inner(py).borrow().debug_iter(all) {
485 for item in self.inner(py).borrow().debug_iter(all) {
436 let (path, (state, mode, size, mtime)) =
486 let (path, (state, mode, size, mtime)) =
437 item.map_err(|e| v2_error(py, e))?;
487 item.map_err(|e| v2_error(py, e))?;
438 let path = PyBytes::new(py, path.as_bytes());
488 let path = PyBytes::new(py, path.as_bytes());
439 let item = (path, state, mode, size, mtime);
489 let item = (path, state, mode, size, mtime);
440 dirs.append(py, item.to_py_object(py).into_object())
490 dirs.append(py, item.to_py_object(py).into_object())
441 }
491 }
442 Ok(dirs)
492 Ok(dirs)
443 }
493 }
444 });
494 });
445
495
446 impl DirstateMap {
496 impl DirstateMap {
447 pub fn get_inner_mut<'a>(
497 pub fn get_inner_mut<'a>(
448 &'a self,
498 &'a self,
449 py: Python<'a>,
499 py: Python<'a>,
450 ) -> RefMut<'a, OwningDirstateMap> {
500 ) -> RefMut<'a, OwningDirstateMap> {
451 self.inner(py).borrow_mut()
501 self.inner(py).borrow_mut()
452 }
502 }
453 fn translate_key(
503 fn translate_key(
454 py: Python,
504 py: Python,
455 res: Result<(&HgPath, DirstateEntry), DirstateV2ParseError>,
505 res: Result<(&HgPath, DirstateEntry), DirstateV2ParseError>,
456 ) -> PyResult<Option<PyBytes>> {
506 ) -> PyResult<Option<PyBytes>> {
457 let (f, _entry) = res.map_err(|e| v2_error(py, e))?;
507 let (f, _entry) = res.map_err(|e| v2_error(py, e))?;
458 Ok(Some(PyBytes::new(py, f.as_bytes())))
508 Ok(Some(PyBytes::new(py, f.as_bytes())))
459 }
509 }
460 fn translate_key_value(
510 fn translate_key_value(
461 py: Python,
511 py: Python,
462 res: Result<(&HgPath, DirstateEntry), DirstateV2ParseError>,
512 res: Result<(&HgPath, DirstateEntry), DirstateV2ParseError>,
463 ) -> PyResult<Option<(PyBytes, PyObject)>> {
513 ) -> PyResult<Option<(PyBytes, PyObject)>> {
464 let (f, entry) = res.map_err(|e| v2_error(py, e))?;
514 let (f, entry) = res.map_err(|e| v2_error(py, e))?;
465 Ok(Some((
515 Ok(Some((
466 PyBytes::new(py, f.as_bytes()),
516 PyBytes::new(py, f.as_bytes()),
467 DirstateItem::new_as_pyobject(py, entry)?,
517 DirstateItem::new_as_pyobject(py, entry)?,
468 )))
518 )))
469 }
519 }
470 }
520 }
471
521
472 py_shared_iterator!(
522 py_shared_iterator!(
473 DirstateMapKeysIterator,
523 DirstateMapKeysIterator,
474 UnsafePyLeaked<StateMapIter<'static>>,
524 UnsafePyLeaked<StateMapIter<'static>>,
475 DirstateMap::translate_key,
525 DirstateMap::translate_key,
476 Option<PyBytes>
526 Option<PyBytes>
477 );
527 );
478
528
479 py_shared_iterator!(
529 py_shared_iterator!(
480 DirstateMapItemsIterator,
530 DirstateMapItemsIterator,
481 UnsafePyLeaked<StateMapIter<'static>>,
531 UnsafePyLeaked<StateMapIter<'static>>,
482 DirstateMap::translate_key_value,
532 DirstateMap::translate_key_value,
483 Option<(PyBytes, PyObject)>
533 Option<(PyBytes, PyObject)>
484 );
534 );
485
535
486 fn extract_node_id(py: Python, obj: &PyObject) -> PyResult<Node> {
536 fn extract_node_id(py: Python, obj: &PyObject) -> PyResult<Node> {
487 let bytes = obj.extract::<PyBytes>(py)?;
537 let bytes = obj.extract::<PyBytes>(py)?;
488 match bytes.data(py).try_into() {
538 match bytes.data(py).try_into() {
489 Ok(s) => Ok(s),
539 Ok(s) => Ok(s),
490 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
540 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
491 }
541 }
492 }
542 }
493
543
494 pub(super) fn v2_error(py: Python<'_>, _: DirstateV2ParseError) -> PyErr {
544 pub(super) fn v2_error(py: Python<'_>, _: DirstateV2ParseError) -> PyErr {
495 PyErr::new::<exc::ValueError, _>(py, "corrupted dirstate-v2")
545 PyErr::new::<exc::ValueError, _>(py, "corrupted dirstate-v2")
496 }
546 }
497
547
498 fn dirstate_error(py: Python<'_>, e: DirstateError) -> PyErr {
548 fn dirstate_error(py: Python<'_>, e: DirstateError) -> PyErr {
499 PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e))
549 PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e))
500 }
550 }
General Comments 0
You need to be logged in to leave comments. Login now