##// END OF EJS Templates
dirstate-item: implement `drop_merge_data` on the Rust DirstateItem...
marmoute -
r48946:3c7db97c default
parent child Browse files
Show More
@@ -1,400 +1,427 b''
1 use crate::errors::HgError;
1 use crate::errors::HgError;
2 use bitflags::bitflags;
2 use bitflags::bitflags;
3 use std::convert::TryFrom;
3 use std::convert::TryFrom;
4
4
5 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
5 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
6 pub enum EntryState {
6 pub enum EntryState {
7 Normal,
7 Normal,
8 Added,
8 Added,
9 Removed,
9 Removed,
10 Merged,
10 Merged,
11 }
11 }
12
12
13 /// The C implementation uses all signed types. This will be an issue
13 /// The C implementation uses all signed types. This will be an issue
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: i32,
20 size: i32,
20 size: i32,
21 mtime: i32,
21 mtime: i32,
22 }
22 }
23
23
24 bitflags! {
24 bitflags! {
25 pub struct Flags: u8 {
25 pub struct Flags: u8 {
26 const WDIR_TRACKED = 1 << 0;
26 const WDIR_TRACKED = 1 << 0;
27 const P1_TRACKED = 1 << 1;
27 const P1_TRACKED = 1 << 1;
28 const P2_TRACKED = 1 << 2;
28 const P2_TRACKED = 1 << 2;
29 const POSSIBLY_DIRTY = 1 << 3;
29 const POSSIBLY_DIRTY = 1 << 3;
30 const MERGED = 1 << 4;
30 const MERGED = 1 << 4;
31 const CLEAN_P1 = 1 << 5;
31 const CLEAN_P1 = 1 << 5;
32 const CLEAN_P2 = 1 << 6;
32 const CLEAN_P2 = 1 << 6;
33 const ENTRYLESS_TREE_NODE = 1 << 7;
33 const ENTRYLESS_TREE_NODE = 1 << 7;
34 }
34 }
35 }
35 }
36
36
37 pub const V1_RANGEMASK: i32 = 0x7FFFFFFF;
37 pub const V1_RANGEMASK: i32 = 0x7FFFFFFF;
38
38
39 pub const MTIME_UNSET: i32 = -1;
39 pub const MTIME_UNSET: i32 = -1;
40
40
41 /// A `DirstateEntry` with a size of `-2` means that it was merged from the
41 /// A `DirstateEntry` with a size of `-2` means that it was merged from the
42 /// other parent. This allows revert to pick the right status back during a
42 /// other parent. This allows revert to pick the right status back during a
43 /// merge.
43 /// merge.
44 pub const SIZE_FROM_OTHER_PARENT: i32 = -2;
44 pub const SIZE_FROM_OTHER_PARENT: i32 = -2;
45 /// A special value used for internal representation of special case in
45 /// A special value used for internal representation of special case in
46 /// dirstate v1 format.
46 /// dirstate v1 format.
47 pub const SIZE_NON_NORMAL: i32 = -1;
47 pub const SIZE_NON_NORMAL: i32 = -1;
48
48
49 impl DirstateEntry {
49 impl DirstateEntry {
50 pub fn new(
50 pub fn new(
51 flags: Flags,
51 flags: Flags,
52 mode_size_mtime: Option<(i32, i32, i32)>,
52 mode_size_mtime: Option<(i32, i32, i32)>,
53 ) -> Self {
53 ) -> Self {
54 let (mode, size, mtime) =
54 let (mode, size, mtime) =
55 mode_size_mtime.unwrap_or((0, SIZE_NON_NORMAL, MTIME_UNSET));
55 mode_size_mtime.unwrap_or((0, SIZE_NON_NORMAL, MTIME_UNSET));
56 Self {
56 Self {
57 flags,
57 flags,
58 mode,
58 mode,
59 size,
59 size,
60 mtime,
60 mtime,
61 }
61 }
62 }
62 }
63
63
64 pub fn from_v1_data(
64 pub fn from_v1_data(
65 state: EntryState,
65 state: EntryState,
66 mode: i32,
66 mode: i32,
67 size: i32,
67 size: i32,
68 mtime: i32,
68 mtime: i32,
69 ) -> Self {
69 ) -> Self {
70 match state {
70 match state {
71 EntryState::Normal => {
71 EntryState::Normal => {
72 if size == SIZE_FROM_OTHER_PARENT {
72 if size == SIZE_FROM_OTHER_PARENT {
73 Self::new_from_p2()
73 Self::new_from_p2()
74 } else if size == SIZE_NON_NORMAL {
74 } else if size == SIZE_NON_NORMAL {
75 Self::new_possibly_dirty()
75 Self::new_possibly_dirty()
76 } else if mtime == MTIME_UNSET {
76 } else if mtime == MTIME_UNSET {
77 Self {
77 Self {
78 flags: Flags::WDIR_TRACKED
78 flags: Flags::WDIR_TRACKED
79 | Flags::P1_TRACKED
79 | Flags::P1_TRACKED
80 | Flags::POSSIBLY_DIRTY,
80 | Flags::POSSIBLY_DIRTY,
81 mode,
81 mode,
82 size,
82 size,
83 mtime: 0,
83 mtime: 0,
84 }
84 }
85 } else {
85 } else {
86 Self::new_normal(mode, size, mtime)
86 Self::new_normal(mode, size, mtime)
87 }
87 }
88 }
88 }
89 EntryState::Added => Self::new_added(),
89 EntryState::Added => Self::new_added(),
90 EntryState::Removed => Self {
90 EntryState::Removed => Self {
91 flags: if size == SIZE_NON_NORMAL {
91 flags: if size == SIZE_NON_NORMAL {
92 Flags::P1_TRACKED // might not be true because of rename ?
92 Flags::P1_TRACKED // might not be true because of rename ?
93 | Flags::P2_TRACKED // might not be true because of rename ?
93 | Flags::P2_TRACKED // might not be true because of rename ?
94 | Flags::MERGED
94 | Flags::MERGED
95 } else if size == SIZE_FROM_OTHER_PARENT {
95 } else if size == SIZE_FROM_OTHER_PARENT {
96 // We don’t know if P1_TRACKED should be set (file history)
96 // We don’t know if P1_TRACKED should be set (file history)
97 Flags::P2_TRACKED | Flags::CLEAN_P2
97 Flags::P2_TRACKED | Flags::CLEAN_P2
98 } else {
98 } else {
99 Flags::P1_TRACKED
99 Flags::P1_TRACKED
100 },
100 },
101 mode: 0,
101 mode: 0,
102 size: 0,
102 size: 0,
103 mtime: 0,
103 mtime: 0,
104 },
104 },
105 EntryState::Merged => Self::new_merged(),
105 EntryState::Merged => Self::new_merged(),
106 }
106 }
107 }
107 }
108
108
109 pub fn new_from_p2() -> Self {
109 pub fn new_from_p2() -> Self {
110 Self {
110 Self {
111 // might be missing P1_TRACKED
111 // might be missing P1_TRACKED
112 flags: Flags::WDIR_TRACKED | Flags::P2_TRACKED | Flags::CLEAN_P2,
112 flags: Flags::WDIR_TRACKED | Flags::P2_TRACKED | Flags::CLEAN_P2,
113 mode: 0,
113 mode: 0,
114 size: SIZE_FROM_OTHER_PARENT,
114 size: SIZE_FROM_OTHER_PARENT,
115 mtime: MTIME_UNSET,
115 mtime: MTIME_UNSET,
116 }
116 }
117 }
117 }
118
118
119 pub fn new_possibly_dirty() -> Self {
119 pub fn new_possibly_dirty() -> Self {
120 Self {
120 Self {
121 flags: Flags::WDIR_TRACKED
121 flags: Flags::WDIR_TRACKED
122 | Flags::P1_TRACKED
122 | Flags::P1_TRACKED
123 | Flags::POSSIBLY_DIRTY,
123 | Flags::POSSIBLY_DIRTY,
124 mode: 0,
124 mode: 0,
125 size: SIZE_NON_NORMAL,
125 size: SIZE_NON_NORMAL,
126 mtime: MTIME_UNSET,
126 mtime: MTIME_UNSET,
127 }
127 }
128 }
128 }
129
129
130 pub fn new_added() -> Self {
130 pub fn new_added() -> Self {
131 Self {
131 Self {
132 flags: Flags::WDIR_TRACKED,
132 flags: Flags::WDIR_TRACKED,
133 mode: 0,
133 mode: 0,
134 size: SIZE_NON_NORMAL,
134 size: SIZE_NON_NORMAL,
135 mtime: MTIME_UNSET,
135 mtime: MTIME_UNSET,
136 }
136 }
137 }
137 }
138
138
139 pub fn new_merged() -> Self {
139 pub fn new_merged() -> Self {
140 Self {
140 Self {
141 flags: Flags::WDIR_TRACKED
141 flags: Flags::WDIR_TRACKED
142 | Flags::P1_TRACKED // might not be true because of rename ?
142 | Flags::P1_TRACKED // might not be true because of rename ?
143 | Flags::P2_TRACKED // might not be true because of rename ?
143 | Flags::P2_TRACKED // might not be true because of rename ?
144 | Flags::MERGED,
144 | Flags::MERGED,
145 mode: 0,
145 mode: 0,
146 size: SIZE_NON_NORMAL,
146 size: SIZE_NON_NORMAL,
147 mtime: MTIME_UNSET,
147 mtime: MTIME_UNSET,
148 }
148 }
149 }
149 }
150
150
151 pub fn new_normal(mode: i32, size: i32, mtime: i32) -> Self {
151 pub fn new_normal(mode: i32, size: i32, mtime: i32) -> Self {
152 Self {
152 Self {
153 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
153 flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
154 mode,
154 mode,
155 size,
155 size,
156 mtime,
156 mtime,
157 }
157 }
158 }
158 }
159
159
160 /// Creates a new entry in "removed" state.
160 /// Creates a new entry in "removed" state.
161 ///
161 ///
162 /// `size` is expected to be zero, `SIZE_NON_NORMAL`, or
162 /// `size` is expected to be zero, `SIZE_NON_NORMAL`, or
163 /// `SIZE_FROM_OTHER_PARENT`
163 /// `SIZE_FROM_OTHER_PARENT`
164 pub fn new_removed(size: i32) -> Self {
164 pub fn new_removed(size: i32) -> Self {
165 Self::from_v1_data(EntryState::Removed, 0, size, 0)
165 Self::from_v1_data(EntryState::Removed, 0, size, 0)
166 }
166 }
167
167
168 pub fn tracked(&self) -> bool {
168 pub fn tracked(&self) -> bool {
169 self.flags.contains(Flags::WDIR_TRACKED)
169 self.flags.contains(Flags::WDIR_TRACKED)
170 }
170 }
171
171
172 fn tracked_in_any_parent(&self) -> bool {
172 fn tracked_in_any_parent(&self) -> bool {
173 self.flags.intersects(Flags::P1_TRACKED | Flags::P2_TRACKED)
173 self.flags.intersects(Flags::P1_TRACKED | Flags::P2_TRACKED)
174 }
174 }
175
175
176 pub fn removed(&self) -> bool {
176 pub fn removed(&self) -> bool {
177 self.tracked_in_any_parent()
177 self.tracked_in_any_parent()
178 && !self.flags.contains(Flags::WDIR_TRACKED)
178 && !self.flags.contains(Flags::WDIR_TRACKED)
179 }
179 }
180
180
181 pub fn merged(&self) -> bool {
181 pub fn merged(&self) -> bool {
182 self.flags.contains(Flags::WDIR_TRACKED | Flags::MERGED)
182 self.flags.contains(Flags::WDIR_TRACKED | Flags::MERGED)
183 }
183 }
184
184
185 pub fn added(&self) -> bool {
185 pub fn added(&self) -> bool {
186 self.flags.contains(Flags::WDIR_TRACKED)
186 self.flags.contains(Flags::WDIR_TRACKED)
187 && !self.tracked_in_any_parent()
187 && !self.tracked_in_any_parent()
188 }
188 }
189
189
190 pub fn from_p2(&self) -> bool {
190 pub fn from_p2(&self) -> bool {
191 self.flags.contains(Flags::WDIR_TRACKED | Flags::CLEAN_P2)
191 self.flags.contains(Flags::WDIR_TRACKED | Flags::CLEAN_P2)
192 }
192 }
193
193
194 pub fn maybe_clean(&self) -> bool {
194 pub fn maybe_clean(&self) -> bool {
195 if !self.flags.contains(Flags::WDIR_TRACKED) {
195 if !self.flags.contains(Flags::WDIR_TRACKED) {
196 false
196 false
197 } else if self.added() {
197 } else if self.added() {
198 false
198 false
199 } else if self.flags.contains(Flags::MERGED) {
199 } else if self.flags.contains(Flags::MERGED) {
200 false
200 false
201 } else if self.flags.contains(Flags::CLEAN_P2) {
201 } else if self.flags.contains(Flags::CLEAN_P2) {
202 false
202 false
203 } else {
203 } else {
204 true
204 true
205 }
205 }
206 }
206 }
207
207
208 pub fn any_tracked(&self) -> bool {
208 pub fn any_tracked(&self) -> bool {
209 self.flags.intersects(
209 self.flags.intersects(
210 Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_TRACKED,
210 Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_TRACKED,
211 )
211 )
212 }
212 }
213
213
214 pub fn state(&self) -> EntryState {
214 pub fn state(&self) -> EntryState {
215 if self.removed() {
215 if self.removed() {
216 EntryState::Removed
216 EntryState::Removed
217 } else if self.merged() {
217 } else if self.merged() {
218 EntryState::Merged
218 EntryState::Merged
219 } else if self.added() {
219 } else if self.added() {
220 EntryState::Added
220 EntryState::Added
221 } else {
221 } else {
222 EntryState::Normal
222 EntryState::Normal
223 }
223 }
224 }
224 }
225
225
226 pub fn mode(&self) -> i32 {
226 pub fn mode(&self) -> i32 {
227 self.mode
227 self.mode
228 }
228 }
229
229
230 pub fn size(&self) -> i32 {
230 pub fn size(&self) -> i32 {
231 if self.removed() && self.flags.contains(Flags::MERGED) {
231 if self.removed() && self.flags.contains(Flags::MERGED) {
232 SIZE_NON_NORMAL
232 SIZE_NON_NORMAL
233 } else if self.removed() && self.flags.contains(Flags::CLEAN_P2) {
233 } else if self.removed() && self.flags.contains(Flags::CLEAN_P2) {
234 SIZE_FROM_OTHER_PARENT
234 SIZE_FROM_OTHER_PARENT
235 } else if self.removed() {
235 } else if self.removed() {
236 0
236 0
237 } else if self.merged() {
237 } else if self.merged() {
238 SIZE_FROM_OTHER_PARENT
238 SIZE_FROM_OTHER_PARENT
239 } else if self.added() {
239 } else if self.added() {
240 SIZE_NON_NORMAL
240 SIZE_NON_NORMAL
241 } else if self.from_p2() {
241 } else if self.from_p2() {
242 SIZE_FROM_OTHER_PARENT
242 SIZE_FROM_OTHER_PARENT
243 } else if self.flags.contains(Flags::POSSIBLY_DIRTY) {
243 } else if self.flags.contains(Flags::POSSIBLY_DIRTY) {
244 self.size // TODO: SIZE_NON_NORMAL ?
244 self.size // TODO: SIZE_NON_NORMAL ?
245 } else {
245 } else {
246 self.size
246 self.size
247 }
247 }
248 }
248 }
249
249
250 pub fn mtime(&self) -> i32 {
250 pub fn mtime(&self) -> i32 {
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::POSSIBLY_DIRTY) {
254 MTIME_UNSET
254 MTIME_UNSET
255 } else if self.merged() {
255 } else if self.merged() {
256 MTIME_UNSET
256 MTIME_UNSET
257 } else if self.added() {
257 } else if self.added() {
258 MTIME_UNSET
258 MTIME_UNSET
259 } else if self.from_p2() {
259 } else if self.from_p2() {
260 MTIME_UNSET
260 MTIME_UNSET
261 } else {
261 } else {
262 self.mtime
262 self.mtime
263 }
263 }
264 }
264 }
265
265
266 pub fn drop_merge_data(&mut self) {
267 if self.flags.contains(Flags::CLEAN_P1)
268 || self.flags.contains(Flags::CLEAN_P2)
269 || self.flags.contains(Flags::MERGED)
270 || self.flags.contains(Flags::P2_TRACKED)
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 }
291 }
292
266 pub fn set_possibly_dirty(&mut self) {
293 pub fn set_possibly_dirty(&mut self) {
267 self.flags.insert(Flags::POSSIBLY_DIRTY)
294 self.flags.insert(Flags::POSSIBLY_DIRTY)
268 }
295 }
269
296
270 pub fn set_clean(&mut self, mode: i32, size: i32, mtime: i32) {
297 pub fn set_clean(&mut self, mode: i32, size: i32, mtime: i32) {
271 self.flags.insert(Flags::WDIR_TRACKED | Flags::P1_TRACKED);
298 self.flags.insert(Flags::WDIR_TRACKED | Flags::P1_TRACKED);
272 self.flags.remove(
299 self.flags.remove(
273 Flags::P2_TRACKED // This might be wrong
300 Flags::P2_TRACKED // This might be wrong
274 | Flags::MERGED
301 | Flags::MERGED
275 | Flags::CLEAN_P2
302 | Flags::CLEAN_P2
276 | Flags::POSSIBLY_DIRTY,
303 | Flags::POSSIBLY_DIRTY,
277 );
304 );
278 self.mode = mode;
305 self.mode = mode;
279 self.size = size;
306 self.size = size;
280 self.mtime = mtime;
307 self.mtime = mtime;
281 }
308 }
282
309
283 pub fn set_tracked(&mut self) {
310 pub fn set_tracked(&mut self) {
284 self.flags
311 self.flags
285 .insert(Flags::WDIR_TRACKED | Flags::POSSIBLY_DIRTY);
312 .insert(Flags::WDIR_TRACKED | Flags::POSSIBLY_DIRTY);
286 // size = None on the python size turn into size = NON_NORMAL when
313 // size = None on the python size turn into size = NON_NORMAL when
287 // accessed. So the next line is currently required, but a some future
314 // accessed. So the next line is currently required, but a some future
288 // clean up would be welcome.
315 // clean up would be welcome.
289 self.size = SIZE_NON_NORMAL;
316 self.size = SIZE_NON_NORMAL;
290 }
317 }
291
318
292 pub fn set_untracked(&mut self) {
319 pub fn set_untracked(&mut self) {
293 self.flags.remove(Flags::WDIR_TRACKED);
320 self.flags.remove(Flags::WDIR_TRACKED);
294 self.mode = 0;
321 self.mode = 0;
295 self.size = 0;
322 self.size = 0;
296 self.mtime = 0;
323 self.mtime = 0;
297 }
324 }
298
325
299 /// Returns `(state, mode, size, mtime)` for the puprose of serialization
326 /// Returns `(state, mode, size, mtime)` for the puprose of serialization
300 /// in the dirstate-v1 format.
327 /// in the dirstate-v1 format.
301 ///
328 ///
302 /// This includes marker values such as `mtime == -1`. In the future we may
329 /// This includes marker values such as `mtime == -1`. In the future we may
303 /// want to not represent these cases that way in memory, but serialization
330 /// want to not represent these cases that way in memory, but serialization
304 /// will need to keep the same format.
331 /// will need to keep the same format.
305 pub fn v1_data(&self) -> (u8, i32, i32, i32) {
332 pub fn v1_data(&self) -> (u8, i32, i32, i32) {
306 (self.state().into(), self.mode(), self.size(), self.mtime())
333 (self.state().into(), self.mode(), self.size(), self.mtime())
307 }
334 }
308
335
309 pub(crate) fn is_from_other_parent(&self) -> bool {
336 pub(crate) fn is_from_other_parent(&self) -> bool {
310 self.state() == EntryState::Normal
337 self.state() == EntryState::Normal
311 && self.size() == SIZE_FROM_OTHER_PARENT
338 && self.size() == SIZE_FROM_OTHER_PARENT
312 }
339 }
313
340
314 // TODO: other platforms
341 // TODO: other platforms
315 #[cfg(unix)]
342 #[cfg(unix)]
316 pub fn mode_changed(
343 pub fn mode_changed(
317 &self,
344 &self,
318 filesystem_metadata: &std::fs::Metadata,
345 filesystem_metadata: &std::fs::Metadata,
319 ) -> bool {
346 ) -> bool {
320 use std::os::unix::fs::MetadataExt;
347 use std::os::unix::fs::MetadataExt;
321 const EXEC_BIT_MASK: u32 = 0o100;
348 const EXEC_BIT_MASK: u32 = 0o100;
322 let dirstate_exec_bit = (self.mode() as u32) & EXEC_BIT_MASK;
349 let dirstate_exec_bit = (self.mode() as u32) & EXEC_BIT_MASK;
323 let fs_exec_bit = filesystem_metadata.mode() & EXEC_BIT_MASK;
350 let fs_exec_bit = filesystem_metadata.mode() & EXEC_BIT_MASK;
324 dirstate_exec_bit != fs_exec_bit
351 dirstate_exec_bit != fs_exec_bit
325 }
352 }
326
353
327 /// Returns a `(state, mode, size, mtime)` tuple as for
354 /// Returns a `(state, mode, size, mtime)` tuple as for
328 /// `DirstateMapMethods::debug_iter`.
355 /// `DirstateMapMethods::debug_iter`.
329 pub fn debug_tuple(&self) -> (u8, i32, i32, i32) {
356 pub fn debug_tuple(&self) -> (u8, i32, i32, i32) {
330 let state = if self.flags.contains(Flags::ENTRYLESS_TREE_NODE) {
357 let state = if self.flags.contains(Flags::ENTRYLESS_TREE_NODE) {
331 b' '
358 b' '
332 } else {
359 } else {
333 self.state().into()
360 self.state().into()
334 };
361 };
335 (state, self.mode(), self.size(), self.mtime())
362 (state, self.mode(), self.size(), self.mtime())
336 }
363 }
337
364
338 pub fn mtime_is_ambiguous(&self, now: i32) -> bool {
365 pub fn mtime_is_ambiguous(&self, now: i32) -> bool {
339 self.state() == EntryState::Normal && self.mtime() == now
366 self.state() == EntryState::Normal && self.mtime() == now
340 }
367 }
341
368
342 pub fn clear_ambiguous_mtime(&mut self, now: i32) -> bool {
369 pub fn clear_ambiguous_mtime(&mut self, now: i32) -> bool {
343 let ambiguous = self.mtime_is_ambiguous(now);
370 let ambiguous = self.mtime_is_ambiguous(now);
344 if ambiguous {
371 if ambiguous {
345 // The file was last modified "simultaneously" with the current
372 // The file was last modified "simultaneously" with the current
346 // write to dirstate (i.e. within the same second for file-
373 // write to dirstate (i.e. within the same second for file-
347 // systems with a granularity of 1 sec). This commonly happens
374 // systems with a granularity of 1 sec). This commonly happens
348 // for at least a couple of files on 'update'.
375 // for at least a couple of files on 'update'.
349 // The user could change the file without changing its size
376 // The user could change the file without changing its size
350 // within the same second. Invalidate the file's mtime in
377 // within the same second. Invalidate the file's mtime in
351 // dirstate, forcing future 'status' calls to compare the
378 // dirstate, forcing future 'status' calls to compare the
352 // contents of the file if the size is the same. This prevents
379 // contents of the file if the size is the same. This prevents
353 // mistakenly treating such files as clean.
380 // mistakenly treating such files as clean.
354 self.clear_mtime()
381 self.clear_mtime()
355 }
382 }
356 ambiguous
383 ambiguous
357 }
384 }
358
385
359 pub fn clear_mtime(&mut self) {
386 pub fn clear_mtime(&mut self) {
360 self.mtime = -1;
387 self.mtime = -1;
361 }
388 }
362 }
389 }
363
390
364 impl EntryState {
391 impl EntryState {
365 pub fn is_tracked(self) -> bool {
392 pub fn is_tracked(self) -> bool {
366 use EntryState::*;
393 use EntryState::*;
367 match self {
394 match self {
368 Normal | Added | Merged => true,
395 Normal | Added | Merged => true,
369 Removed => false,
396 Removed => false,
370 }
397 }
371 }
398 }
372 }
399 }
373
400
374 impl TryFrom<u8> for EntryState {
401 impl TryFrom<u8> for EntryState {
375 type Error = HgError;
402 type Error = HgError;
376
403
377 fn try_from(value: u8) -> Result<Self, Self::Error> {
404 fn try_from(value: u8) -> Result<Self, Self::Error> {
378 match value {
405 match value {
379 b'n' => Ok(EntryState::Normal),
406 b'n' => Ok(EntryState::Normal),
380 b'a' => Ok(EntryState::Added),
407 b'a' => Ok(EntryState::Added),
381 b'r' => Ok(EntryState::Removed),
408 b'r' => Ok(EntryState::Removed),
382 b'm' => Ok(EntryState::Merged),
409 b'm' => Ok(EntryState::Merged),
383 _ => Err(HgError::CorruptedRepository(format!(
410 _ => Err(HgError::CorruptedRepository(format!(
384 "Incorrect dirstate entry state {}",
411 "Incorrect dirstate entry state {}",
385 value
412 value
386 ))),
413 ))),
387 }
414 }
388 }
415 }
389 }
416 }
390
417
391 impl Into<u8> for EntryState {
418 impl Into<u8> for EntryState {
392 fn into(self) -> u8 {
419 fn into(self) -> u8 {
393 match self {
420 match self {
394 EntryState::Normal => b'n',
421 EntryState::Normal => b'n',
395 EntryState::Added => b'a',
422 EntryState::Added => b'a',
396 EntryState::Removed => b'r',
423 EntryState::Removed => b'r',
397 EntryState::Merged => b'm',
424 EntryState::Merged => b'm',
398 }
425 }
399 }
426 }
400 }
427 }
@@ -1,213 +1,218 b''
1 use cpython::exc;
1 use cpython::exc;
2 use cpython::PyBytes;
2 use cpython::PyBytes;
3 use cpython::PyErr;
3 use cpython::PyErr;
4 use cpython::PyNone;
4 use cpython::PyNone;
5 use cpython::PyObject;
5 use cpython::PyObject;
6 use cpython::PyResult;
6 use cpython::PyResult;
7 use cpython::Python;
7 use cpython::Python;
8 use cpython::PythonObject;
8 use cpython::PythonObject;
9 use hg::dirstate::entry::Flags;
9 use hg::dirstate::entry::Flags;
10 use hg::dirstate::DirstateEntry;
10 use hg::dirstate::DirstateEntry;
11 use hg::dirstate::EntryState;
11 use hg::dirstate::EntryState;
12 use std::cell::Cell;
12 use std::cell::Cell;
13 use std::convert::TryFrom;
13 use std::convert::TryFrom;
14
14
15 py_class!(pub class DirstateItem |py| {
15 py_class!(pub class DirstateItem |py| {
16 data entry: Cell<DirstateEntry>;
16 data entry: Cell<DirstateEntry>;
17
17
18 def __new__(
18 def __new__(
19 _cls,
19 _cls,
20 wc_tracked: bool = false,
20 wc_tracked: bool = false,
21 p1_tracked: bool = false,
21 p1_tracked: bool = false,
22 p2_tracked: bool = false,
22 p2_tracked: bool = false,
23 merged: bool = false,
23 merged: bool = false,
24 clean_p1: bool = false,
24 clean_p1: bool = false,
25 clean_p2: bool = false,
25 clean_p2: bool = false,
26 possibly_dirty: bool = false,
26 possibly_dirty: bool = false,
27 parentfiledata: Option<(i32, i32, i32)> = None,
27 parentfiledata: Option<(i32, i32, i32)> = None,
28
28
29 ) -> PyResult<DirstateItem> {
29 ) -> PyResult<DirstateItem> {
30 let mut flags = Flags::empty();
30 let mut flags = Flags::empty();
31 flags.set(Flags::WDIR_TRACKED, wc_tracked);
31 flags.set(Flags::WDIR_TRACKED, wc_tracked);
32 flags.set(Flags::P1_TRACKED, p1_tracked);
32 flags.set(Flags::P1_TRACKED, p1_tracked);
33 flags.set(Flags::P2_TRACKED, p2_tracked);
33 flags.set(Flags::P2_TRACKED, p2_tracked);
34 flags.set(Flags::MERGED, merged);
34 flags.set(Flags::MERGED, merged);
35 flags.set(Flags::CLEAN_P1, clean_p1);
35 flags.set(Flags::CLEAN_P1, clean_p1);
36 flags.set(Flags::CLEAN_P2, clean_p2);
36 flags.set(Flags::CLEAN_P2, clean_p2);
37 flags.set(Flags::POSSIBLY_DIRTY, possibly_dirty);
37 flags.set(Flags::POSSIBLY_DIRTY, possibly_dirty);
38 let entry = DirstateEntry::new(flags, parentfiledata);
38 let entry = DirstateEntry::new(flags, parentfiledata);
39 DirstateItem::create_instance(py, Cell::new(entry))
39 DirstateItem::create_instance(py, Cell::new(entry))
40 }
40 }
41
41
42 @property
42 @property
43 def state(&self) -> PyResult<PyBytes> {
43 def state(&self) -> PyResult<PyBytes> {
44 let state_byte: u8 = self.entry(py).get().state().into();
44 let state_byte: u8 = self.entry(py).get().state().into();
45 Ok(PyBytes::new(py, &[state_byte]))
45 Ok(PyBytes::new(py, &[state_byte]))
46 }
46 }
47
47
48 @property
48 @property
49 def mode(&self) -> PyResult<i32> {
49 def mode(&self) -> PyResult<i32> {
50 Ok(self.entry(py).get().mode())
50 Ok(self.entry(py).get().mode())
51 }
51 }
52
52
53 @property
53 @property
54 def size(&self) -> PyResult<i32> {
54 def size(&self) -> PyResult<i32> {
55 Ok(self.entry(py).get().size())
55 Ok(self.entry(py).get().size())
56 }
56 }
57
57
58 @property
58 @property
59 def mtime(&self) -> PyResult<i32> {
59 def mtime(&self) -> PyResult<i32> {
60 Ok(self.entry(py).get().mtime())
60 Ok(self.entry(py).get().mtime())
61 }
61 }
62
62
63 @property
63 @property
64 def tracked(&self) -> PyResult<bool> {
64 def tracked(&self) -> PyResult<bool> {
65 Ok(self.entry(py).get().tracked())
65 Ok(self.entry(py).get().tracked())
66 }
66 }
67
67
68 @property
68 @property
69 def added(&self) -> PyResult<bool> {
69 def added(&self) -> PyResult<bool> {
70 Ok(self.entry(py).get().added())
70 Ok(self.entry(py).get().added())
71 }
71 }
72
72
73 @property
73 @property
74 def merged(&self) -> PyResult<bool> {
74 def merged(&self) -> PyResult<bool> {
75 Ok(self.entry(py).get().merged())
75 Ok(self.entry(py).get().merged())
76 }
76 }
77
77
78 @property
78 @property
79 def removed(&self) -> PyResult<bool> {
79 def removed(&self) -> PyResult<bool> {
80 Ok(self.entry(py).get().removed())
80 Ok(self.entry(py).get().removed())
81 }
81 }
82
82
83 @property
83 @property
84 def from_p2(&self) -> PyResult<bool> {
84 def from_p2(&self) -> PyResult<bool> {
85 Ok(self.entry(py).get().from_p2())
85 Ok(self.entry(py).get().from_p2())
86 }
86 }
87
87
88 @property
88 @property
89 def maybe_clean(&self) -> PyResult<bool> {
89 def maybe_clean(&self) -> PyResult<bool> {
90 Ok(self.entry(py).get().maybe_clean())
90 Ok(self.entry(py).get().maybe_clean())
91 }
91 }
92
92
93 @property
93 @property
94 def any_tracked(&self) -> PyResult<bool> {
94 def any_tracked(&self) -> PyResult<bool> {
95 Ok(self.entry(py).get().any_tracked())
95 Ok(self.entry(py).get().any_tracked())
96 }
96 }
97
97
98 def v1_state(&self) -> PyResult<PyBytes> {
98 def v1_state(&self) -> PyResult<PyBytes> {
99 let (state, _mode, _size, _mtime) = self.entry(py).get().v1_data();
99 let (state, _mode, _size, _mtime) = self.entry(py).get().v1_data();
100 let state_byte: u8 = state.into();
100 let state_byte: u8 = state.into();
101 Ok(PyBytes::new(py, &[state_byte]))
101 Ok(PyBytes::new(py, &[state_byte]))
102 }
102 }
103
103
104 def v1_mode(&self) -> PyResult<i32> {
104 def v1_mode(&self) -> PyResult<i32> {
105 let (_state, mode, _size, _mtime) = self.entry(py).get().v1_data();
105 let (_state, mode, _size, _mtime) = self.entry(py).get().v1_data();
106 Ok(mode)
106 Ok(mode)
107 }
107 }
108
108
109 def v1_size(&self) -> PyResult<i32> {
109 def v1_size(&self) -> PyResult<i32> {
110 let (_state, _mode, size, _mtime) = self.entry(py).get().v1_data();
110 let (_state, _mode, size, _mtime) = self.entry(py).get().v1_data();
111 Ok(size)
111 Ok(size)
112 }
112 }
113
113
114 def v1_mtime(&self) -> PyResult<i32> {
114 def v1_mtime(&self) -> PyResult<i32> {
115 let (_state, _mode, _size, mtime) = self.entry(py).get().v1_data();
115 let (_state, _mode, _size, mtime) = self.entry(py).get().v1_data();
116 Ok(mtime)
116 Ok(mtime)
117 }
117 }
118
118
119 def need_delay(&self, now: i32) -> PyResult<bool> {
119 def need_delay(&self, now: i32) -> PyResult<bool> {
120 Ok(self.entry(py).get().mtime_is_ambiguous(now))
120 Ok(self.entry(py).get().mtime_is_ambiguous(now))
121 }
121 }
122
122
123 @classmethod
123 @classmethod
124 def from_v1_data(
124 def from_v1_data(
125 _cls,
125 _cls,
126 state: PyBytes,
126 state: PyBytes,
127 mode: i32,
127 mode: i32,
128 size: i32,
128 size: i32,
129 mtime: i32,
129 mtime: i32,
130 ) -> PyResult<Self> {
130 ) -> PyResult<Self> {
131 let state = <[u8; 1]>::try_from(state.data(py))
131 let state = <[u8; 1]>::try_from(state.data(py))
132 .ok()
132 .ok()
133 .and_then(|state| EntryState::try_from(state[0]).ok())
133 .and_then(|state| EntryState::try_from(state[0]).ok())
134 .ok_or_else(|| PyErr::new::<exc::ValueError, _>(py, "invalid state"))?;
134 .ok_or_else(|| PyErr::new::<exc::ValueError, _>(py, "invalid state"))?;
135 let entry = DirstateEntry::from_v1_data(state, mode, size, mtime);
135 let entry = DirstateEntry::from_v1_data(state, mode, size, mtime);
136 DirstateItem::create_instance(py, Cell::new(entry))
136 DirstateItem::create_instance(py, Cell::new(entry))
137 }
137 }
138
138
139 @classmethod
139 @classmethod
140 def new_added(_cls) -> PyResult<Self> {
140 def new_added(_cls) -> PyResult<Self> {
141 let entry = DirstateEntry::new_added();
141 let entry = DirstateEntry::new_added();
142 DirstateItem::create_instance(py, Cell::new(entry))
142 DirstateItem::create_instance(py, Cell::new(entry))
143 }
143 }
144
144
145 @classmethod
145 @classmethod
146 def new_merged(_cls) -> PyResult<Self> {
146 def new_merged(_cls) -> PyResult<Self> {
147 let entry = DirstateEntry::new_merged();
147 let entry = DirstateEntry::new_merged();
148 DirstateItem::create_instance(py, Cell::new(entry))
148 DirstateItem::create_instance(py, Cell::new(entry))
149 }
149 }
150
150
151 @classmethod
151 @classmethod
152 def new_from_p2(_cls) -> PyResult<Self> {
152 def new_from_p2(_cls) -> PyResult<Self> {
153 let entry = DirstateEntry::new_from_p2();
153 let entry = DirstateEntry::new_from_p2();
154 DirstateItem::create_instance(py, Cell::new(entry))
154 DirstateItem::create_instance(py, Cell::new(entry))
155 }
155 }
156
156
157 @classmethod
157 @classmethod
158 def new_possibly_dirty(_cls) -> PyResult<Self> {
158 def new_possibly_dirty(_cls) -> PyResult<Self> {
159 let entry = DirstateEntry::new_possibly_dirty();
159 let entry = DirstateEntry::new_possibly_dirty();
160 DirstateItem::create_instance(py, Cell::new(entry))
160 DirstateItem::create_instance(py, Cell::new(entry))
161 }
161 }
162
162
163 @classmethod
163 @classmethod
164 def new_normal(_cls, mode: i32, size: i32, mtime: i32) -> PyResult<Self> {
164 def new_normal(_cls, mode: i32, size: i32, mtime: i32) -> PyResult<Self> {
165 let entry = DirstateEntry::new_normal(mode, size, mtime);
165 let entry = DirstateEntry::new_normal(mode, size, mtime);
166 DirstateItem::create_instance(py, Cell::new(entry))
166 DirstateItem::create_instance(py, Cell::new(entry))
167 }
167 }
168
168
169 def drop_merge_data(&self) -> PyResult<PyNone> {
170 self.update(py, |entry| entry.drop_merge_data());
171 Ok(PyNone)
172 }
173
169 def set_clean(
174 def set_clean(
170 &self,
175 &self,
171 mode: i32,
176 mode: i32,
172 size: i32,
177 size: i32,
173 mtime: i32,
178 mtime: i32,
174 ) -> PyResult<PyNone> {
179 ) -> PyResult<PyNone> {
175 self.update(py, |entry| entry.set_clean(mode, size, mtime));
180 self.update(py, |entry| entry.set_clean(mode, size, mtime));
176 Ok(PyNone)
181 Ok(PyNone)
177 }
182 }
178
183
179 def set_possibly_dirty(&self) -> PyResult<PyNone> {
184 def set_possibly_dirty(&self) -> PyResult<PyNone> {
180 self.update(py, |entry| entry.set_possibly_dirty());
185 self.update(py, |entry| entry.set_possibly_dirty());
181 Ok(PyNone)
186 Ok(PyNone)
182 }
187 }
183
188
184 def set_tracked(&self) -> PyResult<PyNone> {
189 def set_tracked(&self) -> PyResult<PyNone> {
185 self.update(py, |entry| entry.set_tracked());
190 self.update(py, |entry| entry.set_tracked());
186 Ok(PyNone)
191 Ok(PyNone)
187 }
192 }
188
193
189 def set_untracked(&self) -> PyResult<PyNone> {
194 def set_untracked(&self) -> PyResult<PyNone> {
190 self.update(py, |entry| entry.set_untracked());
195 self.update(py, |entry| entry.set_untracked());
191 Ok(PyNone)
196 Ok(PyNone)
192 }
197 }
193 });
198 });
194
199
195 impl DirstateItem {
200 impl DirstateItem {
196 pub fn new_as_pyobject(
201 pub fn new_as_pyobject(
197 py: Python<'_>,
202 py: Python<'_>,
198 entry: DirstateEntry,
203 entry: DirstateEntry,
199 ) -> PyResult<PyObject> {
204 ) -> PyResult<PyObject> {
200 Ok(DirstateItem::create_instance(py, Cell::new(entry))?.into_object())
205 Ok(DirstateItem::create_instance(py, Cell::new(entry))?.into_object())
201 }
206 }
202
207
203 pub fn get_entry(&self, py: Python<'_>) -> DirstateEntry {
208 pub fn get_entry(&self, py: Python<'_>) -> DirstateEntry {
204 self.entry(py).get()
209 self.entry(py).get()
205 }
210 }
206
211
207 // TODO: Use https://doc.rust-lang.org/std/cell/struct.Cell.html#method.update instead when it’s stable
212 // TODO: Use https://doc.rust-lang.org/std/cell/struct.Cell.html#method.update instead when it’s stable
208 pub fn update(&self, py: Python<'_>, f: impl FnOnce(&mut DirstateEntry)) {
213 pub fn update(&self, py: Python<'_>, f: impl FnOnce(&mut DirstateEntry)) {
209 let mut entry = self.entry(py).get();
214 let mut entry = self.entry(py).get();
210 f(&mut entry);
215 f(&mut entry);
211 self.entry(py).set(entry)
216 self.entry(py).set(entry)
212 }
217 }
213 }
218 }
General Comments 0
You need to be logged in to leave comments. Login now