Mercurial > hg
comparison rust/hg-core/src/dirstate/entry.rs @ 48138:38488d488ec1
dirstate-item: change the internal storage and constructor value
This should be closer to what we do need and what we can actually reliably
record.
In practice it means that we abandon the prospect of storing much more refined
data for now. We don't have the necessary information nor code using it right
now. So it seems safer to just use a clearer version of what we had so far.
See the documentation changes for details.
Differential Revision: https://phab.mercurial-scm.org/D11557
author | Pierre-Yves David <pierre-yves.david@octobus.net> |
---|---|
date | Fri, 01 Oct 2021 20:35:30 +0200 |
parents | 3c7db97ce541 |
children | ab5a7fdbf75c |
comparison
equal
deleted
inserted
replaced
48137:25836b0029f5 | 48138:38488d488ec1 |
---|---|
14 /// either when 4GB+ source files are commonplace or in 2038, whichever | 14 /// either when 4GB+ source files are commonplace or in 2038, whichever |
15 /// comes first. | 15 /// comes first. |
16 #[derive(Debug, PartialEq, Copy, Clone)] | 16 #[derive(Debug, PartialEq, Copy, Clone)] |
17 pub struct DirstateEntry { | 17 pub struct DirstateEntry { |
18 flags: Flags, | 18 flags: Flags, |
19 mode: i32, | 19 mode_size: Option<(i32, i32)>, |
20 size: i32, | 20 mtime: Option<i32>, |
21 mtime: i32, | |
22 } | 21 } |
23 | 22 |
24 bitflags! { | 23 bitflags! { |
25 pub struct Flags: u8 { | 24 struct Flags: u8 { |
26 const WDIR_TRACKED = 1 << 0; | 25 const WDIR_TRACKED = 1 << 0; |
27 const P1_TRACKED = 1 << 1; | 26 const P1_TRACKED = 1 << 1; |
28 const P2_TRACKED = 1 << 2; | 27 const P2_INFO = 1 << 2; |
29 const POSSIBLY_DIRTY = 1 << 3; | |
30 const MERGED = 1 << 4; | |
31 const CLEAN_P1 = 1 << 5; | |
32 const CLEAN_P2 = 1 << 6; | |
33 const ENTRYLESS_TREE_NODE = 1 << 7; | |
34 } | 28 } |
35 } | 29 } |
36 | 30 |
37 pub const V1_RANGEMASK: i32 = 0x7FFFFFFF; | 31 pub const V1_RANGEMASK: i32 = 0x7FFFFFFF; |
38 | 32 |
46 /// dirstate v1 format. | 40 /// dirstate v1 format. |
47 pub const SIZE_NON_NORMAL: i32 = -1; | 41 pub const SIZE_NON_NORMAL: i32 = -1; |
48 | 42 |
49 impl DirstateEntry { | 43 impl DirstateEntry { |
50 pub fn new( | 44 pub fn new( |
51 flags: Flags, | 45 wdir_tracked: bool, |
52 mode_size_mtime: Option<(i32, i32, i32)>, | 46 p1_tracked: bool, |
47 p2_info: bool, | |
48 mode_size: Option<(i32, i32)>, | |
49 mtime: Option<i32>, | |
53 ) -> Self { | 50 ) -> Self { |
54 let (mode, size, mtime) = | 51 let mut flags = Flags::empty(); |
55 mode_size_mtime.unwrap_or((0, SIZE_NON_NORMAL, MTIME_UNSET)); | 52 flags.set(Flags::WDIR_TRACKED, wdir_tracked); |
53 flags.set(Flags::P1_TRACKED, p1_tracked); | |
54 flags.set(Flags::P2_INFO, p2_info); | |
56 Self { | 55 Self { |
57 flags, | 56 flags, |
58 mode, | 57 mode_size, |
59 size, | |
60 mtime, | 58 mtime, |
61 } | 59 } |
62 } | 60 } |
63 | 61 |
64 pub fn from_v1_data( | 62 pub fn from_v1_data( |
73 Self::new_from_p2() | 71 Self::new_from_p2() |
74 } else if size == SIZE_NON_NORMAL { | 72 } else if size == SIZE_NON_NORMAL { |
75 Self::new_possibly_dirty() | 73 Self::new_possibly_dirty() |
76 } else if mtime == MTIME_UNSET { | 74 } else if mtime == MTIME_UNSET { |
77 Self { | 75 Self { |
78 flags: Flags::WDIR_TRACKED | 76 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED, |
79 | Flags::P1_TRACKED | 77 mode_size: Some((mode, size)), |
80 | Flags::POSSIBLY_DIRTY, | 78 mtime: None, |
81 mode, | |
82 size, | |
83 mtime: 0, | |
84 } | 79 } |
85 } else { | 80 } else { |
86 Self::new_normal(mode, size, mtime) | 81 Self::new_normal(mode, size, mtime) |
87 } | 82 } |
88 } | 83 } |
89 EntryState::Added => Self::new_added(), | 84 EntryState::Added => Self::new_added(), |
90 EntryState::Removed => Self { | 85 EntryState::Removed => Self { |
91 flags: if size == SIZE_NON_NORMAL { | 86 flags: if size == SIZE_NON_NORMAL { |
92 Flags::P1_TRACKED // might not be true because of rename ? | 87 Flags::P1_TRACKED | Flags::P2_INFO |
93 | Flags::P2_TRACKED // might not be true because of rename ? | |
94 | Flags::MERGED | |
95 } else if size == SIZE_FROM_OTHER_PARENT { | 88 } else if size == SIZE_FROM_OTHER_PARENT { |
96 // We don’t know if P1_TRACKED should be set (file history) | 89 // We don’t know if P1_TRACKED should be set (file history) |
97 Flags::P2_TRACKED | Flags::CLEAN_P2 | 90 Flags::P2_INFO |
98 } else { | 91 } else { |
99 Flags::P1_TRACKED | 92 Flags::P1_TRACKED |
100 }, | 93 }, |
101 mode: 0, | 94 mode_size: None, |
102 size: 0, | 95 mtime: None, |
103 mtime: 0, | |
104 }, | 96 }, |
105 EntryState::Merged => Self::new_merged(), | 97 EntryState::Merged => Self::new_merged(), |
106 } | 98 } |
107 } | 99 } |
108 | 100 |
109 pub fn new_from_p2() -> Self { | 101 pub fn new_from_p2() -> Self { |
110 Self { | 102 Self { |
111 // might be missing P1_TRACKED | 103 // might be missing P1_TRACKED |
112 flags: Flags::WDIR_TRACKED | Flags::P2_TRACKED | Flags::CLEAN_P2, | 104 flags: Flags::WDIR_TRACKED | Flags::P2_INFO, |
113 mode: 0, | 105 mode_size: None, |
114 size: SIZE_FROM_OTHER_PARENT, | 106 mtime: None, |
115 mtime: MTIME_UNSET, | |
116 } | 107 } |
117 } | 108 } |
118 | 109 |
119 pub fn new_possibly_dirty() -> Self { | 110 pub fn new_possibly_dirty() -> Self { |
120 Self { | 111 Self { |
121 flags: Flags::WDIR_TRACKED | 112 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED, |
122 | Flags::P1_TRACKED | 113 mode_size: None, |
123 | Flags::POSSIBLY_DIRTY, | 114 mtime: None, |
124 mode: 0, | |
125 size: SIZE_NON_NORMAL, | |
126 mtime: MTIME_UNSET, | |
127 } | 115 } |
128 } | 116 } |
129 | 117 |
130 pub fn new_added() -> Self { | 118 pub fn new_added() -> Self { |
131 Self { | 119 Self { |
132 flags: Flags::WDIR_TRACKED, | 120 flags: Flags::WDIR_TRACKED, |
133 mode: 0, | 121 mode_size: None, |
134 size: SIZE_NON_NORMAL, | 122 mtime: None, |
135 mtime: MTIME_UNSET, | |
136 } | 123 } |
137 } | 124 } |
138 | 125 |
139 pub fn new_merged() -> Self { | 126 pub fn new_merged() -> Self { |
140 Self { | 127 Self { |
141 flags: Flags::WDIR_TRACKED | 128 flags: Flags::WDIR_TRACKED |
142 | Flags::P1_TRACKED // might not be true because of rename ? | 129 | Flags::P1_TRACKED // might not be true because of rename ? |
143 | Flags::P2_TRACKED // might not be true because of rename ? | 130 | Flags::P2_INFO, // might not be true because of rename ? |
144 | Flags::MERGED, | 131 mode_size: None, |
145 mode: 0, | 132 mtime: None, |
146 size: SIZE_NON_NORMAL, | |
147 mtime: MTIME_UNSET, | |
148 } | 133 } |
149 } | 134 } |
150 | 135 |
151 pub fn new_normal(mode: i32, size: i32, mtime: i32) -> Self { | 136 pub fn new_normal(mode: i32, size: i32, mtime: i32) -> Self { |
152 Self { | 137 Self { |
153 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED, | 138 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED, |
154 mode, | 139 mode_size: Some((mode, size)), |
155 size, | 140 mtime: Some(mtime), |
156 mtime, | |
157 } | 141 } |
158 } | 142 } |
159 | 143 |
160 /// Creates a new entry in "removed" state. | 144 /// Creates a new entry in "removed" state. |
161 /// | 145 /// |
167 | 151 |
168 pub fn tracked(&self) -> bool { | 152 pub fn tracked(&self) -> bool { |
169 self.flags.contains(Flags::WDIR_TRACKED) | 153 self.flags.contains(Flags::WDIR_TRACKED) |
170 } | 154 } |
171 | 155 |
172 fn tracked_in_any_parent(&self) -> bool { | 156 fn in_either_parent(&self) -> bool { |
173 self.flags.intersects(Flags::P1_TRACKED | Flags::P2_TRACKED) | 157 self.flags.intersects(Flags::P1_TRACKED | Flags::P2_INFO) |
174 } | 158 } |
175 | 159 |
176 pub fn removed(&self) -> bool { | 160 pub fn removed(&self) -> bool { |
177 self.tracked_in_any_parent() | 161 self.in_either_parent() && !self.flags.contains(Flags::WDIR_TRACKED) |
178 && !self.flags.contains(Flags::WDIR_TRACKED) | |
179 } | 162 } |
180 | 163 |
181 pub fn merged(&self) -> bool { | 164 pub fn merged(&self) -> bool { |
182 self.flags.contains(Flags::WDIR_TRACKED | Flags::MERGED) | 165 self.flags |
166 .contains(Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_INFO) | |
183 } | 167 } |
184 | 168 |
185 pub fn added(&self) -> bool { | 169 pub fn added(&self) -> bool { |
186 self.flags.contains(Flags::WDIR_TRACKED) | 170 self.flags.contains(Flags::WDIR_TRACKED) && !self.in_either_parent() |
187 && !self.tracked_in_any_parent() | |
188 } | 171 } |
189 | 172 |
190 pub fn from_p2(&self) -> bool { | 173 pub fn from_p2(&self) -> bool { |
191 self.flags.contains(Flags::WDIR_TRACKED | Flags::CLEAN_P2) | 174 self.flags.contains(Flags::WDIR_TRACKED | Flags::P2_INFO) |
175 && !self.flags.contains(Flags::P1_TRACKED) | |
192 } | 176 } |
193 | 177 |
194 pub fn maybe_clean(&self) -> bool { | 178 pub fn maybe_clean(&self) -> bool { |
195 if !self.flags.contains(Flags::WDIR_TRACKED) { | 179 if !self.flags.contains(Flags::WDIR_TRACKED) { |
196 false | 180 false |
197 } else if self.added() { | 181 } else if !self.flags.contains(Flags::P1_TRACKED) { |
198 false | 182 false |
199 } else if self.flags.contains(Flags::MERGED) { | 183 } else if self.flags.contains(Flags::P2_INFO) { |
200 false | |
201 } else if self.flags.contains(Flags::CLEAN_P2) { | |
202 false | 184 false |
203 } else { | 185 } else { |
204 true | 186 true |
205 } | 187 } |
206 } | 188 } |
207 | 189 |
208 pub fn any_tracked(&self) -> bool { | 190 pub fn any_tracked(&self) -> bool { |
209 self.flags.intersects( | 191 self.flags.intersects( |
210 Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_TRACKED, | 192 Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_INFO, |
211 ) | 193 ) |
212 } | 194 } |
213 | 195 |
214 pub fn state(&self) -> EntryState { | 196 fn v1_state(&self) -> EntryState { |
197 if !self.any_tracked() { | |
198 // TODO: return an Option instead? | |
199 panic!("Accessing v1_state of an untracked DirstateEntry") | |
200 } | |
215 if self.removed() { | 201 if self.removed() { |
216 EntryState::Removed | 202 EntryState::Removed |
217 } else if self.merged() { | 203 } else if self.merged() { |
218 EntryState::Merged | 204 EntryState::Merged |
219 } else if self.added() { | 205 } else if self.added() { |
221 } else { | 207 } else { |
222 EntryState::Normal | 208 EntryState::Normal |
223 } | 209 } |
224 } | 210 } |
225 | 211 |
226 pub fn mode(&self) -> i32 { | 212 fn v1_mode(&self) -> i32 { |
227 self.mode | 213 if let Some((mode, _size)) = self.mode_size { |
228 } | 214 mode |
229 | 215 } else { |
230 pub fn size(&self) -> i32 { | 216 0 |
231 if self.removed() && self.flags.contains(Flags::MERGED) { | 217 } |
218 } | |
219 | |
220 fn v1_size(&self) -> i32 { | |
221 if !self.any_tracked() { | |
222 // TODO: return an Option instead? | |
223 panic!("Accessing v1_size of an untracked DirstateEntry") | |
224 } | |
225 if self.removed() | |
226 && self.flags.contains(Flags::P1_TRACKED | Flags::P2_INFO) | |
227 { | |
232 SIZE_NON_NORMAL | 228 SIZE_NON_NORMAL |
233 } else if self.removed() && self.flags.contains(Flags::CLEAN_P2) { | 229 } else if self.removed() && self.flags.contains(Flags::P2_INFO) { |
234 SIZE_FROM_OTHER_PARENT | 230 SIZE_FROM_OTHER_PARENT |
235 } else if self.removed() { | 231 } else if self.removed() { |
236 0 | 232 0 |
237 } else if self.merged() { | 233 } else if self.merged() { |
238 SIZE_FROM_OTHER_PARENT | 234 SIZE_FROM_OTHER_PARENT |
239 } else if self.added() { | 235 } else if self.added() { |
240 SIZE_NON_NORMAL | 236 SIZE_NON_NORMAL |
241 } else if self.from_p2() { | 237 } else if self.from_p2() { |
242 SIZE_FROM_OTHER_PARENT | 238 SIZE_FROM_OTHER_PARENT |
243 } else if self.flags.contains(Flags::POSSIBLY_DIRTY) { | 239 } else if let Some((_mode, size)) = self.mode_size { |
244 self.size // TODO: SIZE_NON_NORMAL ? | 240 size |
245 } else { | 241 } else { |
246 self.size | 242 SIZE_NON_NORMAL |
247 } | 243 } |
248 } | 244 } |
249 | 245 |
250 pub fn mtime(&self) -> i32 { | 246 fn v1_mtime(&self) -> i32 { |
247 if !self.any_tracked() { | |
248 // TODO: return an Option instead? | |
249 panic!("Accessing v1_mtime of an untracked DirstateEntry") | |
250 } | |
251 if self.removed() { | 251 if self.removed() { |
252 0 | 252 0 |
253 } else if self.flags.contains(Flags::POSSIBLY_DIRTY) { | 253 } else if self.flags.contains(Flags::P2_INFO) { |
254 MTIME_UNSET | 254 MTIME_UNSET |
255 } else if self.merged() { | 255 } else if !self.flags.contains(Flags::P1_TRACKED) { |
256 MTIME_UNSET | |
257 } else if self.added() { | |
258 MTIME_UNSET | |
259 } else if self.from_p2() { | |
260 MTIME_UNSET | 256 MTIME_UNSET |
261 } else { | 257 } else { |
262 self.mtime | 258 self.mtime.unwrap_or(MTIME_UNSET) |
263 } | 259 } |
260 } | |
261 | |
262 // TODO: return `Option<EntryState>`? None when `!self.any_tracked` | |
263 pub fn state(&self) -> EntryState { | |
264 self.v1_state() | |
265 } | |
266 | |
267 // TODO: return Option? | |
268 pub fn mode(&self) -> i32 { | |
269 self.v1_mode() | |
270 } | |
271 | |
272 // TODO: return Option? | |
273 pub fn size(&self) -> i32 { | |
274 self.v1_size() | |
275 } | |
276 | |
277 // TODO: return Option? | |
278 pub fn mtime(&self) -> i32 { | |
279 self.v1_mtime() | |
264 } | 280 } |
265 | 281 |
266 pub fn drop_merge_data(&mut self) { | 282 pub fn drop_merge_data(&mut self) { |
267 if self.flags.contains(Flags::CLEAN_P1) | 283 if self.flags.contains(Flags::P2_INFO) { |
268 || self.flags.contains(Flags::CLEAN_P2) | 284 self.flags.remove(Flags::P2_INFO); |
269 || self.flags.contains(Flags::MERGED) | 285 self.mode_size = None; |
270 || self.flags.contains(Flags::P2_TRACKED) | 286 self.mtime = None; |
271 { | |
272 if self.flags.contains(Flags::MERGED) { | |
273 self.flags.insert(Flags::P1_TRACKED); | |
274 } else { | |
275 self.flags.remove(Flags::P1_TRACKED); | |
276 } | |
277 self.flags.remove( | |
278 Flags::MERGED | |
279 | Flags::CLEAN_P1 | |
280 | Flags::CLEAN_P2 | |
281 | Flags::P2_TRACKED, | |
282 ); | |
283 self.flags.insert(Flags::POSSIBLY_DIRTY); | |
284 self.mode = 0; | |
285 self.mtime = 0; | |
286 // size = None on the python size turn into size = NON_NORMAL when | |
287 // accessed. So the next line is currently required, but a some | |
288 // future clean up would be welcome. | |
289 self.size = SIZE_NON_NORMAL; | |
290 } | 287 } |
291 } | 288 } |
292 | 289 |
293 pub fn set_possibly_dirty(&mut self) { | 290 pub fn set_possibly_dirty(&mut self) { |
294 self.flags.insert(Flags::POSSIBLY_DIRTY) | 291 self.mtime = None |
295 } | 292 } |
296 | 293 |
297 pub fn set_clean(&mut self, mode: i32, size: i32, mtime: i32) { | 294 pub fn set_clean(&mut self, mode: i32, size: i32, mtime: i32) { |
298 self.flags.insert(Flags::WDIR_TRACKED | Flags::P1_TRACKED); | 295 self.flags.insert(Flags::WDIR_TRACKED | Flags::P1_TRACKED); |
299 self.flags.remove( | 296 self.mode_size = Some((mode, size)); |
300 Flags::P2_TRACKED // This might be wrong | 297 self.mtime = Some(mtime); |
301 | Flags::MERGED | |
302 | Flags::CLEAN_P2 | |
303 | Flags::POSSIBLY_DIRTY, | |
304 ); | |
305 self.mode = mode; | |
306 self.size = size; | |
307 self.mtime = mtime; | |
308 } | 298 } |
309 | 299 |
310 pub fn set_tracked(&mut self) { | 300 pub fn set_tracked(&mut self) { |
311 self.flags | 301 self.flags.insert(Flags::WDIR_TRACKED); |
312 .insert(Flags::WDIR_TRACKED | Flags::POSSIBLY_DIRTY); | 302 // `set_tracked` is replacing various `normallookup` call. So we mark |
313 // size = None on the python size turn into size = NON_NORMAL when | 303 // the files as needing lookup |
314 // accessed. So the next line is currently required, but a some future | 304 // |
315 // clean up would be welcome. | 305 // Consider dropping this in the future in favor of something less |
316 self.size = SIZE_NON_NORMAL; | 306 // broad. |
307 self.mtime = None; | |
317 } | 308 } |
318 | 309 |
319 pub fn set_untracked(&mut self) { | 310 pub fn set_untracked(&mut self) { |
320 self.flags.remove(Flags::WDIR_TRACKED); | 311 self.flags.remove(Flags::WDIR_TRACKED); |
321 self.mode = 0; | 312 self.mode_size = None; |
322 self.size = 0; | 313 self.mtime = None; |
323 self.mtime = 0; | |
324 } | 314 } |
325 | 315 |
326 /// Returns `(state, mode, size, mtime)` for the puprose of serialization | 316 /// Returns `(state, mode, size, mtime)` for the puprose of serialization |
327 /// in the dirstate-v1 format. | 317 /// in the dirstate-v1 format. |
328 /// | 318 /// |
329 /// This includes marker values such as `mtime == -1`. In the future we may | 319 /// This includes marker values such as `mtime == -1`. In the future we may |
330 /// want to not represent these cases that way in memory, but serialization | 320 /// want to not represent these cases that way in memory, but serialization |
331 /// will need to keep the same format. | 321 /// will need to keep the same format. |
332 pub fn v1_data(&self) -> (u8, i32, i32, i32) { | 322 pub fn v1_data(&self) -> (u8, i32, i32, i32) { |
333 (self.state().into(), self.mode(), self.size(), self.mtime()) | 323 ( |
324 self.v1_state().into(), | |
325 self.v1_mode(), | |
326 self.v1_size(), | |
327 self.v1_mtime(), | |
328 ) | |
334 } | 329 } |
335 | 330 |
336 pub(crate) fn is_from_other_parent(&self) -> bool { | 331 pub(crate) fn is_from_other_parent(&self) -> bool { |
337 self.state() == EntryState::Normal | 332 self.state() == EntryState::Normal |
338 && self.size() == SIZE_FROM_OTHER_PARENT | 333 && self.size() == SIZE_FROM_OTHER_PARENT |
352 } | 347 } |
353 | 348 |
354 /// Returns a `(state, mode, size, mtime)` tuple as for | 349 /// Returns a `(state, mode, size, mtime)` tuple as for |
355 /// `DirstateMapMethods::debug_iter`. | 350 /// `DirstateMapMethods::debug_iter`. |
356 pub fn debug_tuple(&self) -> (u8, i32, i32, i32) { | 351 pub fn debug_tuple(&self) -> (u8, i32, i32, i32) { |
357 let state = if self.flags.contains(Flags::ENTRYLESS_TREE_NODE) { | 352 (self.state().into(), self.mode(), self.size(), self.mtime()) |
358 b' ' | |
359 } else { | |
360 self.state().into() | |
361 }; | |
362 (state, self.mode(), self.size(), self.mtime()) | |
363 } | 353 } |
364 | 354 |
365 pub fn mtime_is_ambiguous(&self, now: i32) -> bool { | 355 pub fn mtime_is_ambiguous(&self, now: i32) -> bool { |
366 self.state() == EntryState::Normal && self.mtime() == now | 356 self.state() == EntryState::Normal && self.mtime() == now |
367 } | 357 } |
376 // The user could change the file without changing its size | 366 // The user could change the file without changing its size |
377 // within the same second. Invalidate the file's mtime in | 367 // within the same second. Invalidate the file's mtime in |
378 // dirstate, forcing future 'status' calls to compare the | 368 // dirstate, forcing future 'status' calls to compare the |
379 // contents of the file if the size is the same. This prevents | 369 // contents of the file if the size is the same. This prevents |
380 // mistakenly treating such files as clean. | 370 // mistakenly treating such files as clean. |
381 self.clear_mtime() | 371 self.set_possibly_dirty() |
382 } | 372 } |
383 ambiguous | 373 ambiguous |
384 } | |
385 | |
386 pub fn clear_mtime(&mut self) { | |
387 self.mtime = -1; | |
388 } | 374 } |
389 } | 375 } |
390 | 376 |
391 impl EntryState { | 377 impl EntryState { |
392 pub fn is_tracked(self) -> bool { | 378 pub fn is_tracked(self) -> bool { |