##// END OF EJS Templates
dirstate-item: introduce a `any_tracked` property...
marmoute -
r48899:79bc60ca default
parent child Browse files
Show More
@@ -1,1233 +1,1247
1 /*
1 /*
2 parsers.c - efficient content parsing
2 parsers.c - efficient content parsing
3
3
4 Copyright 2008 Olivia Mackall <olivia@selenic.com> and others
4 Copyright 2008 Olivia Mackall <olivia@selenic.com> and others
5
5
6 This software may be used and distributed according to the terms of
6 This software may be used and distributed according to the terms of
7 the GNU General Public License, incorporated herein by reference.
7 the GNU General Public License, incorporated herein by reference.
8 */
8 */
9
9
10 #define PY_SSIZE_T_CLEAN
10 #define PY_SSIZE_T_CLEAN
11 #include <Python.h>
11 #include <Python.h>
12 #include <ctype.h>
12 #include <ctype.h>
13 #include <stddef.h>
13 #include <stddef.h>
14 #include <string.h>
14 #include <string.h>
15
15
16 #include "bitmanipulation.h"
16 #include "bitmanipulation.h"
17 #include "charencode.h"
17 #include "charencode.h"
18 #include "util.h"
18 #include "util.h"
19
19
20 #ifdef IS_PY3K
20 #ifdef IS_PY3K
21 /* The mapping of Python types is meant to be temporary to get Python
21 /* The mapping of Python types is meant to be temporary to get Python
22 * 3 to compile. We should remove this once Python 3 support is fully
22 * 3 to compile. We should remove this once Python 3 support is fully
23 * supported and proper types are used in the extensions themselves. */
23 * supported and proper types are used in the extensions themselves. */
24 #define PyInt_Check PyLong_Check
24 #define PyInt_Check PyLong_Check
25 #define PyInt_FromLong PyLong_FromLong
25 #define PyInt_FromLong PyLong_FromLong
26 #define PyInt_FromSsize_t PyLong_FromSsize_t
26 #define PyInt_FromSsize_t PyLong_FromSsize_t
27 #define PyInt_AsLong PyLong_AsLong
27 #define PyInt_AsLong PyLong_AsLong
28 #endif
28 #endif
29
29
30 static const char *const versionerrortext = "Python minor version mismatch";
30 static const char *const versionerrortext = "Python minor version mismatch";
31
31
32 static const int dirstate_v1_from_p2 = -2;
32 static const int dirstate_v1_from_p2 = -2;
33 static const int dirstate_v1_nonnormal = -1;
33 static const int dirstate_v1_nonnormal = -1;
34 static const int ambiguous_time = -1;
34 static const int ambiguous_time = -1;
35
35
36 static PyObject *dict_new_presized(PyObject *self, PyObject *args)
36 static PyObject *dict_new_presized(PyObject *self, PyObject *args)
37 {
37 {
38 Py_ssize_t expected_size;
38 Py_ssize_t expected_size;
39
39
40 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size)) {
40 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size)) {
41 return NULL;
41 return NULL;
42 }
42 }
43
43
44 return _dict_new_presized(expected_size);
44 return _dict_new_presized(expected_size);
45 }
45 }
46
46
47 static PyObject *dirstate_item_new(PyTypeObject *subtype, PyObject *args,
47 static PyObject *dirstate_item_new(PyTypeObject *subtype, PyObject *args,
48 PyObject *kwds)
48 PyObject *kwds)
49 {
49 {
50 /* We do all the initialization here and not a tp_init function because
50 /* We do all the initialization here and not a tp_init function because
51 * dirstate_item is immutable. */
51 * dirstate_item is immutable. */
52 dirstateItemObject *t;
52 dirstateItemObject *t;
53 int wc_tracked;
53 int wc_tracked;
54 int p1_tracked;
54 int p1_tracked;
55 int p2_tracked;
55 int p2_tracked;
56 int merged;
56 int merged;
57 int clean_p1;
57 int clean_p1;
58 int clean_p2;
58 int clean_p2;
59 int possibly_dirty;
59 int possibly_dirty;
60 PyObject *parentfiledata;
60 PyObject *parentfiledata;
61 static char *keywords_name[] = {
61 static char *keywords_name[] = {
62 "wc_tracked", "p1_tracked", "p2_tracked",
62 "wc_tracked", "p1_tracked", "p2_tracked",
63 "merged", "clean_p1", "clean_p2",
63 "merged", "clean_p1", "clean_p2",
64 "possibly_dirty", "parentfiledata", NULL,
64 "possibly_dirty", "parentfiledata", NULL,
65 };
65 };
66 wc_tracked = 0;
66 wc_tracked = 0;
67 p1_tracked = 0;
67 p1_tracked = 0;
68 p2_tracked = 0;
68 p2_tracked = 0;
69 merged = 0;
69 merged = 0;
70 clean_p1 = 0;
70 clean_p1 = 0;
71 clean_p2 = 0;
71 clean_p2 = 0;
72 possibly_dirty = 0;
72 possibly_dirty = 0;
73 parentfiledata = Py_None;
73 parentfiledata = Py_None;
74 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iiiiiiiO", keywords_name,
74 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iiiiiiiO", keywords_name,
75 &wc_tracked, &p1_tracked, &p2_tracked,
75 &wc_tracked, &p1_tracked, &p2_tracked,
76 &merged, &clean_p1, &clean_p2,
76 &merged, &clean_p1, &clean_p2,
77 &possibly_dirty, &parentfiledata
77 &possibly_dirty, &parentfiledata
78
78
79 )) {
79 )) {
80 return NULL;
80 return NULL;
81 }
81 }
82 if (merged && (clean_p1 || clean_p2)) {
82 if (merged && (clean_p1 || clean_p2)) {
83 PyErr_SetString(PyExc_RuntimeError,
83 PyErr_SetString(PyExc_RuntimeError,
84 "`merged` argument incompatible with "
84 "`merged` argument incompatible with "
85 "`clean_p1`/`clean_p2`");
85 "`clean_p1`/`clean_p2`");
86 return NULL;
86 return NULL;
87 }
87 }
88 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
88 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
89 if (!t) {
89 if (!t) {
90 return NULL;
90 return NULL;
91 }
91 }
92
92
93 t->flags = 0;
93 t->flags = 0;
94 if (wc_tracked) {
94 if (wc_tracked) {
95 t->flags |= dirstate_flag_wc_tracked;
95 t->flags |= dirstate_flag_wc_tracked;
96 }
96 }
97 if (p1_tracked) {
97 if (p1_tracked) {
98 t->flags |= dirstate_flag_p1_tracked;
98 t->flags |= dirstate_flag_p1_tracked;
99 }
99 }
100 if (p2_tracked) {
100 if (p2_tracked) {
101 t->flags |= dirstate_flag_p2_tracked;
101 t->flags |= dirstate_flag_p2_tracked;
102 }
102 }
103 if (possibly_dirty) {
103 if (possibly_dirty) {
104 t->flags |= dirstate_flag_possibly_dirty;
104 t->flags |= dirstate_flag_possibly_dirty;
105 }
105 }
106 if (merged) {
106 if (merged) {
107 t->flags |= dirstate_flag_merged;
107 t->flags |= dirstate_flag_merged;
108 }
108 }
109 if (clean_p1) {
109 if (clean_p1) {
110 t->flags |= dirstate_flag_clean_p1;
110 t->flags |= dirstate_flag_clean_p1;
111 }
111 }
112 if (clean_p2) {
112 if (clean_p2) {
113 t->flags |= dirstate_flag_clean_p2;
113 t->flags |= dirstate_flag_clean_p2;
114 }
114 }
115 t->mode = 0;
115 t->mode = 0;
116 t->size = dirstate_v1_nonnormal;
116 t->size = dirstate_v1_nonnormal;
117 t->mtime = ambiguous_time;
117 t->mtime = ambiguous_time;
118 if (parentfiledata != Py_None) {
118 if (parentfiledata != Py_None) {
119 if (!PyTuple_CheckExact(parentfiledata)) {
119 if (!PyTuple_CheckExact(parentfiledata)) {
120 PyErr_SetString(
120 PyErr_SetString(
121 PyExc_TypeError,
121 PyExc_TypeError,
122 "parentfiledata should be a Tuple or None");
122 "parentfiledata should be a Tuple or None");
123 return NULL;
123 return NULL;
124 }
124 }
125 t->mode =
125 t->mode =
126 (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 0));
126 (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 0));
127 t->size =
127 t->size =
128 (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 1));
128 (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 1));
129 t->mtime =
129 t->mtime =
130 (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 2));
130 (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 2));
131 }
131 }
132 return (PyObject *)t;
132 return (PyObject *)t;
133 }
133 }
134
134
135 static void dirstate_item_dealloc(PyObject *o)
135 static void dirstate_item_dealloc(PyObject *o)
136 {
136 {
137 PyObject_Del(o);
137 PyObject_Del(o);
138 }
138 }
139
139
140 static inline bool dirstate_item_c_tracked(dirstateItemObject *self)
140 static inline bool dirstate_item_c_tracked(dirstateItemObject *self)
141 {
141 {
142 return (self->flags & dirstate_flag_wc_tracked);
142 return (self->flags & dirstate_flag_wc_tracked);
143 }
143 }
144
144
145 static inline bool dirstate_item_c_added(dirstateItemObject *self)
145 static inline bool dirstate_item_c_added(dirstateItemObject *self)
146 {
146 {
147 unsigned char mask =
147 unsigned char mask =
148 (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
148 (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
149 dirstate_flag_p2_tracked);
149 dirstate_flag_p2_tracked);
150 unsigned char target = dirstate_flag_wc_tracked;
150 unsigned char target = dirstate_flag_wc_tracked;
151 return (self->flags & mask) == target;
151 return (self->flags & mask) == target;
152 }
152 }
153
153
154 static inline bool dirstate_item_c_removed(dirstateItemObject *self)
154 static inline bool dirstate_item_c_removed(dirstateItemObject *self)
155 {
155 {
156 if (self->flags & dirstate_flag_wc_tracked) {
156 if (self->flags & dirstate_flag_wc_tracked) {
157 return false;
157 return false;
158 }
158 }
159 return (self->flags &
159 return (self->flags &
160 (dirstate_flag_p1_tracked | dirstate_flag_p2_tracked));
160 (dirstate_flag_p1_tracked | dirstate_flag_p2_tracked));
161 }
161 }
162
162
163 static inline bool dirstate_item_c_merged(dirstateItemObject *self)
163 static inline bool dirstate_item_c_merged(dirstateItemObject *self)
164 {
164 {
165 return ((self->flags & dirstate_flag_wc_tracked) &&
165 return ((self->flags & dirstate_flag_wc_tracked) &&
166 (self->flags & dirstate_flag_merged));
166 (self->flags & dirstate_flag_merged));
167 }
167 }
168
168
169 static inline bool dirstate_item_c_from_p2(dirstateItemObject *self)
169 static inline bool dirstate_item_c_from_p2(dirstateItemObject *self)
170 {
170 {
171 if (!dirstate_item_c_tracked(self)) {
171 if (!dirstate_item_c_tracked(self)) {
172 return false;
172 return false;
173 }
173 }
174 return (self->flags & dirstate_flag_clean_p2);
174 return (self->flags & dirstate_flag_clean_p2);
175 }
175 }
176
176
177 static inline char dirstate_item_c_v1_state(dirstateItemObject *self)
177 static inline char dirstate_item_c_v1_state(dirstateItemObject *self)
178 {
178 {
179 if (dirstate_item_c_removed(self)) {
179 if (dirstate_item_c_removed(self)) {
180 return 'r';
180 return 'r';
181 } else if (dirstate_item_c_merged(self)) {
181 } else if (dirstate_item_c_merged(self)) {
182 return 'm';
182 return 'm';
183 } else if (dirstate_item_c_added(self)) {
183 } else if (dirstate_item_c_added(self)) {
184 return 'a';
184 return 'a';
185 } else {
185 } else {
186 return 'n';
186 return 'n';
187 }
187 }
188 }
188 }
189
189
190 static inline int dirstate_item_c_v1_mode(dirstateItemObject *self)
190 static inline int dirstate_item_c_v1_mode(dirstateItemObject *self)
191 {
191 {
192 return self->mode;
192 return self->mode;
193 }
193 }
194
194
195 static inline int dirstate_item_c_v1_size(dirstateItemObject *self)
195 static inline int dirstate_item_c_v1_size(dirstateItemObject *self)
196 {
196 {
197 if (dirstate_item_c_removed(self) &&
197 if (dirstate_item_c_removed(self) &&
198 (self->flags & dirstate_flag_merged)) {
198 (self->flags & dirstate_flag_merged)) {
199 return dirstate_v1_nonnormal;
199 return dirstate_v1_nonnormal;
200 } else if (dirstate_item_c_removed(self) &&
200 } else if (dirstate_item_c_removed(self) &&
201 (self->flags & dirstate_flag_clean_p2)) {
201 (self->flags & dirstate_flag_clean_p2)) {
202 return dirstate_v1_from_p2;
202 return dirstate_v1_from_p2;
203 } else if (dirstate_item_c_removed(self)) {
203 } else if (dirstate_item_c_removed(self)) {
204 return 0;
204 return 0;
205 } else if (dirstate_item_c_merged(self)) {
205 } else if (dirstate_item_c_merged(self)) {
206 return dirstate_v1_from_p2;
206 return dirstate_v1_from_p2;
207 } else if (dirstate_item_c_added(self)) {
207 } else if (dirstate_item_c_added(self)) {
208 return dirstate_v1_nonnormal;
208 return dirstate_v1_nonnormal;
209 } else if (dirstate_item_c_from_p2(self)) {
209 } else if (dirstate_item_c_from_p2(self)) {
210 return dirstate_v1_from_p2;
210 return dirstate_v1_from_p2;
211 } else if (self->flags & dirstate_flag_possibly_dirty) {
211 } else if (self->flags & dirstate_flag_possibly_dirty) {
212 return self->size; /* NON NORMAL ? */
212 return self->size; /* NON NORMAL ? */
213 } else {
213 } else {
214 return self->size;
214 return self->size;
215 }
215 }
216 }
216 }
217
217
218 static inline int dirstate_item_c_v1_mtime(dirstateItemObject *self)
218 static inline int dirstate_item_c_v1_mtime(dirstateItemObject *self)
219 {
219 {
220 if (dirstate_item_c_removed(self)) {
220 if (dirstate_item_c_removed(self)) {
221 return 0;
221 return 0;
222 } else if (self->flags & dirstate_flag_possibly_dirty) {
222 } else if (self->flags & dirstate_flag_possibly_dirty) {
223 return ambiguous_time;
223 return ambiguous_time;
224 } else if (dirstate_item_c_merged(self)) {
224 } else if (dirstate_item_c_merged(self)) {
225 return ambiguous_time;
225 return ambiguous_time;
226 } else if (dirstate_item_c_added(self)) {
226 } else if (dirstate_item_c_added(self)) {
227 return ambiguous_time;
227 return ambiguous_time;
228 } else if (dirstate_item_c_from_p2(self)) {
228 } else if (dirstate_item_c_from_p2(self)) {
229 return ambiguous_time;
229 return ambiguous_time;
230 } else {
230 } else {
231 return self->mtime;
231 return self->mtime;
232 }
232 }
233 }
233 }
234
234
235 static PyObject *dirstate_item_v1_state(dirstateItemObject *self)
235 static PyObject *dirstate_item_v1_state(dirstateItemObject *self)
236 {
236 {
237 char state = dirstate_item_c_v1_state(self);
237 char state = dirstate_item_c_v1_state(self);
238 return PyBytes_FromStringAndSize(&state, 1);
238 return PyBytes_FromStringAndSize(&state, 1);
239 };
239 };
240
240
241 static PyObject *dirstate_item_v1_mode(dirstateItemObject *self)
241 static PyObject *dirstate_item_v1_mode(dirstateItemObject *self)
242 {
242 {
243 return PyInt_FromLong(dirstate_item_c_v1_mode(self));
243 return PyInt_FromLong(dirstate_item_c_v1_mode(self));
244 };
244 };
245
245
246 static PyObject *dirstate_item_v1_size(dirstateItemObject *self)
246 static PyObject *dirstate_item_v1_size(dirstateItemObject *self)
247 {
247 {
248 return PyInt_FromLong(dirstate_item_c_v1_size(self));
248 return PyInt_FromLong(dirstate_item_c_v1_size(self));
249 };
249 };
250
250
251 static PyObject *dirstate_item_v1_mtime(dirstateItemObject *self)
251 static PyObject *dirstate_item_v1_mtime(dirstateItemObject *self)
252 {
252 {
253 return PyInt_FromLong(dirstate_item_c_v1_mtime(self));
253 return PyInt_FromLong(dirstate_item_c_v1_mtime(self));
254 };
254 };
255
255
256 static PyObject *dirstate_item_need_delay(dirstateItemObject *self,
256 static PyObject *dirstate_item_need_delay(dirstateItemObject *self,
257 PyObject *value)
257 PyObject *value)
258 {
258 {
259 long now;
259 long now;
260 if (!pylong_to_long(value, &now)) {
260 if (!pylong_to_long(value, &now)) {
261 return NULL;
261 return NULL;
262 }
262 }
263 if (dirstate_item_c_v1_state(self) == 'n' &&
263 if (dirstate_item_c_v1_state(self) == 'n' &&
264 dirstate_item_c_v1_mtime(self) == now) {
264 dirstate_item_c_v1_mtime(self) == now) {
265 Py_RETURN_TRUE;
265 Py_RETURN_TRUE;
266 } else {
266 } else {
267 Py_RETURN_FALSE;
267 Py_RETURN_FALSE;
268 }
268 }
269 };
269 };
270
270
271 /* This will never change since it's bound to V1
271 /* This will never change since it's bound to V1
272 */
272 */
273 static inline dirstateItemObject *
273 static inline dirstateItemObject *
274 dirstate_item_from_v1_data(char state, int mode, int size, int mtime)
274 dirstate_item_from_v1_data(char state, int mode, int size, int mtime)
275 {
275 {
276 dirstateItemObject *t =
276 dirstateItemObject *t =
277 PyObject_New(dirstateItemObject, &dirstateItemType);
277 PyObject_New(dirstateItemObject, &dirstateItemType);
278 if (!t) {
278 if (!t) {
279 return NULL;
279 return NULL;
280 }
280 }
281
281
282 if (state == 'm') {
282 if (state == 'm') {
283 t->flags =
283 t->flags =
284 (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
284 (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
285 dirstate_flag_p2_tracked | dirstate_flag_merged);
285 dirstate_flag_p2_tracked | dirstate_flag_merged);
286 t->mode = 0;
286 t->mode = 0;
287 t->size = dirstate_v1_from_p2;
287 t->size = dirstate_v1_from_p2;
288 t->mtime = ambiguous_time;
288 t->mtime = ambiguous_time;
289 } else if (state == 'a') {
289 } else if (state == 'a') {
290 t->flags = dirstate_flag_wc_tracked;
290 t->flags = dirstate_flag_wc_tracked;
291 t->mode = 0;
291 t->mode = 0;
292 t->size = dirstate_v1_nonnormal;
292 t->size = dirstate_v1_nonnormal;
293 t->mtime = ambiguous_time;
293 t->mtime = ambiguous_time;
294 } else if (state == 'r') {
294 } else if (state == 'r') {
295 t->mode = 0;
295 t->mode = 0;
296 t->size = 0;
296 t->size = 0;
297 t->mtime = 0;
297 t->mtime = 0;
298 if (size == dirstate_v1_nonnormal) {
298 if (size == dirstate_v1_nonnormal) {
299 t->flags =
299 t->flags =
300 (dirstate_flag_p1_tracked |
300 (dirstate_flag_p1_tracked |
301 dirstate_flag_p2_tracked | dirstate_flag_merged);
301 dirstate_flag_p2_tracked | dirstate_flag_merged);
302 } else if (size == dirstate_v1_from_p2) {
302 } else if (size == dirstate_v1_from_p2) {
303 t->flags =
303 t->flags =
304 (dirstate_flag_p2_tracked | dirstate_flag_clean_p2);
304 (dirstate_flag_p2_tracked | dirstate_flag_clean_p2);
305 } else {
305 } else {
306 t->flags = dirstate_flag_p1_tracked;
306 t->flags = dirstate_flag_p1_tracked;
307 }
307 }
308 } else if (state == 'n') {
308 } else if (state == 'n') {
309 if (size == dirstate_v1_from_p2) {
309 if (size == dirstate_v1_from_p2) {
310 t->flags =
310 t->flags =
311 (dirstate_flag_wc_tracked |
311 (dirstate_flag_wc_tracked |
312 dirstate_flag_p2_tracked | dirstate_flag_clean_p2);
312 dirstate_flag_p2_tracked | dirstate_flag_clean_p2);
313 t->mode = 0;
313 t->mode = 0;
314 t->size = dirstate_v1_from_p2;
314 t->size = dirstate_v1_from_p2;
315 t->mtime = ambiguous_time;
315 t->mtime = ambiguous_time;
316 } else if (size == dirstate_v1_nonnormal) {
316 } else if (size == dirstate_v1_nonnormal) {
317 t->flags = (dirstate_flag_wc_tracked |
317 t->flags = (dirstate_flag_wc_tracked |
318 dirstate_flag_p1_tracked |
318 dirstate_flag_p1_tracked |
319 dirstate_flag_possibly_dirty);
319 dirstate_flag_possibly_dirty);
320 t->mode = 0;
320 t->mode = 0;
321 t->size = dirstate_v1_nonnormal;
321 t->size = dirstate_v1_nonnormal;
322 t->mtime = ambiguous_time;
322 t->mtime = ambiguous_time;
323 } else if (mtime == ambiguous_time) {
323 } else if (mtime == ambiguous_time) {
324 t->flags = (dirstate_flag_wc_tracked |
324 t->flags = (dirstate_flag_wc_tracked |
325 dirstate_flag_p1_tracked |
325 dirstate_flag_p1_tracked |
326 dirstate_flag_possibly_dirty);
326 dirstate_flag_possibly_dirty);
327 t->mode = mode;
327 t->mode = mode;
328 t->size = size;
328 t->size = size;
329 t->mtime = 0;
329 t->mtime = 0;
330 } else {
330 } else {
331 t->flags = (dirstate_flag_wc_tracked |
331 t->flags = (dirstate_flag_wc_tracked |
332 dirstate_flag_p1_tracked);
332 dirstate_flag_p1_tracked);
333 t->mode = mode;
333 t->mode = mode;
334 t->size = size;
334 t->size = size;
335 t->mtime = mtime;
335 t->mtime = mtime;
336 }
336 }
337 } else {
337 } else {
338 PyErr_Format(PyExc_RuntimeError,
338 PyErr_Format(PyExc_RuntimeError,
339 "unknown state: `%c` (%d, %d, %d)", state, mode,
339 "unknown state: `%c` (%d, %d, %d)", state, mode,
340 size, mtime, NULL);
340 size, mtime, NULL);
341 Py_DECREF(t);
341 Py_DECREF(t);
342 return NULL;
342 return NULL;
343 }
343 }
344
344
345 return t;
345 return t;
346 }
346 }
347
347
348 /* This will never change since it's bound to V1, unlike `dirstate_item_new` */
348 /* This will never change since it's bound to V1, unlike `dirstate_item_new` */
349 static PyObject *dirstate_item_from_v1_meth(PyTypeObject *subtype,
349 static PyObject *dirstate_item_from_v1_meth(PyTypeObject *subtype,
350 PyObject *args)
350 PyObject *args)
351 {
351 {
352 /* We do all the initialization here and not a tp_init function because
352 /* We do all the initialization here and not a tp_init function because
353 * dirstate_item is immutable. */
353 * dirstate_item is immutable. */
354 char state;
354 char state;
355 int size, mode, mtime;
355 int size, mode, mtime;
356 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime)) {
356 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime)) {
357 return NULL;
357 return NULL;
358 }
358 }
359 return (PyObject *)dirstate_item_from_v1_data(state, mode, size, mtime);
359 return (PyObject *)dirstate_item_from_v1_data(state, mode, size, mtime);
360 };
360 };
361
361
362 /* constructor to help legacy API to build a new "added" item
362 /* constructor to help legacy API to build a new "added" item
363
363
364 Should eventually be removed */
364 Should eventually be removed */
365 static PyObject *dirstate_item_new_added(PyTypeObject *subtype)
365 static PyObject *dirstate_item_new_added(PyTypeObject *subtype)
366 {
366 {
367 dirstateItemObject *t;
367 dirstateItemObject *t;
368 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
368 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
369 if (!t) {
369 if (!t) {
370 return NULL;
370 return NULL;
371 }
371 }
372 t->flags = dirstate_flag_wc_tracked;
372 t->flags = dirstate_flag_wc_tracked;
373 t->mode = 0;
373 t->mode = 0;
374 t->size = dirstate_v1_nonnormal;
374 t->size = dirstate_v1_nonnormal;
375 t->mtime = ambiguous_time;
375 t->mtime = ambiguous_time;
376 return (PyObject *)t;
376 return (PyObject *)t;
377 };
377 };
378
378
379 /* constructor to help legacy API to build a new "merged" item
379 /* constructor to help legacy API to build a new "merged" item
380
380
381 Should eventually be removed */
381 Should eventually be removed */
382 static PyObject *dirstate_item_new_merged(PyTypeObject *subtype)
382 static PyObject *dirstate_item_new_merged(PyTypeObject *subtype)
383 {
383 {
384 dirstateItemObject *t;
384 dirstateItemObject *t;
385 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
385 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
386 if (!t) {
386 if (!t) {
387 return NULL;
387 return NULL;
388 }
388 }
389 t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
389 t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
390 dirstate_flag_p2_tracked | dirstate_flag_merged);
390 dirstate_flag_p2_tracked | dirstate_flag_merged);
391 t->mode = 0;
391 t->mode = 0;
392 t->size = dirstate_v1_from_p2;
392 t->size = dirstate_v1_from_p2;
393 t->mtime = ambiguous_time;
393 t->mtime = ambiguous_time;
394 return (PyObject *)t;
394 return (PyObject *)t;
395 };
395 };
396
396
397 /* constructor to help legacy API to build a new "from_p2" item
397 /* constructor to help legacy API to build a new "from_p2" item
398
398
399 Should eventually be removed */
399 Should eventually be removed */
400 static PyObject *dirstate_item_new_from_p2(PyTypeObject *subtype)
400 static PyObject *dirstate_item_new_from_p2(PyTypeObject *subtype)
401 {
401 {
402 /* We do all the initialization here and not a tp_init function because
402 /* We do all the initialization here and not a tp_init function because
403 * dirstate_item is immutable. */
403 * dirstate_item is immutable. */
404 dirstateItemObject *t;
404 dirstateItemObject *t;
405 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
405 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
406 if (!t) {
406 if (!t) {
407 return NULL;
407 return NULL;
408 }
408 }
409 t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p2_tracked |
409 t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p2_tracked |
410 dirstate_flag_clean_p2);
410 dirstate_flag_clean_p2);
411 t->mode = 0;
411 t->mode = 0;
412 t->size = dirstate_v1_from_p2;
412 t->size = dirstate_v1_from_p2;
413 t->mtime = ambiguous_time;
413 t->mtime = ambiguous_time;
414 return (PyObject *)t;
414 return (PyObject *)t;
415 };
415 };
416
416
417 /* constructor to help legacy API to build a new "possibly" item
417 /* constructor to help legacy API to build a new "possibly" item
418
418
419 Should eventually be removed */
419 Should eventually be removed */
420 static PyObject *dirstate_item_new_possibly_dirty(PyTypeObject *subtype)
420 static PyObject *dirstate_item_new_possibly_dirty(PyTypeObject *subtype)
421 {
421 {
422 /* We do all the initialization here and not a tp_init function because
422 /* We do all the initialization here and not a tp_init function because
423 * dirstate_item is immutable. */
423 * dirstate_item is immutable. */
424 dirstateItemObject *t;
424 dirstateItemObject *t;
425 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
425 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
426 if (!t) {
426 if (!t) {
427 return NULL;
427 return NULL;
428 }
428 }
429 t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
429 t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
430 dirstate_flag_possibly_dirty);
430 dirstate_flag_possibly_dirty);
431 t->mode = 0;
431 t->mode = 0;
432 t->size = dirstate_v1_nonnormal;
432 t->size = dirstate_v1_nonnormal;
433 t->mtime = ambiguous_time;
433 t->mtime = ambiguous_time;
434 return (PyObject *)t;
434 return (PyObject *)t;
435 };
435 };
436
436
437 /* constructor to help legacy API to build a new "normal" item
437 /* constructor to help legacy API to build a new "normal" item
438
438
439 Should eventually be removed */
439 Should eventually be removed */
440 static PyObject *dirstate_item_new_normal(PyTypeObject *subtype, PyObject *args)
440 static PyObject *dirstate_item_new_normal(PyTypeObject *subtype, PyObject *args)
441 {
441 {
442 /* We do all the initialization here and not a tp_init function because
442 /* We do all the initialization here and not a tp_init function because
443 * dirstate_item is immutable. */
443 * dirstate_item is immutable. */
444 dirstateItemObject *t;
444 dirstateItemObject *t;
445 int size, mode, mtime;
445 int size, mode, mtime;
446 if (!PyArg_ParseTuple(args, "iii", &mode, &size, &mtime)) {
446 if (!PyArg_ParseTuple(args, "iii", &mode, &size, &mtime)) {
447 return NULL;
447 return NULL;
448 }
448 }
449
449
450 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
450 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
451 if (!t) {
451 if (!t) {
452 return NULL;
452 return NULL;
453 }
453 }
454 t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked);
454 t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked);
455 t->mode = mode;
455 t->mode = mode;
456 t->size = size;
456 t->size = size;
457 t->mtime = mtime;
457 t->mtime = mtime;
458 return (PyObject *)t;
458 return (PyObject *)t;
459 };
459 };
460
460
461 /* This means the next status call will have to actually check its content
461 /* This means the next status call will have to actually check its content
462 to make sure it is correct. */
462 to make sure it is correct. */
463 static PyObject *dirstate_item_set_possibly_dirty(dirstateItemObject *self)
463 static PyObject *dirstate_item_set_possibly_dirty(dirstateItemObject *self)
464 {
464 {
465 self->flags |= dirstate_flag_possibly_dirty;
465 self->flags |= dirstate_flag_possibly_dirty;
466 Py_RETURN_NONE;
466 Py_RETURN_NONE;
467 }
467 }
468
468
469 /* See docstring of the python implementation for details */
469 /* See docstring of the python implementation for details */
470 static PyObject *dirstate_item_set_clean(dirstateItemObject *self,
470 static PyObject *dirstate_item_set_clean(dirstateItemObject *self,
471 PyObject *args)
471 PyObject *args)
472 {
472 {
473 int size, mode, mtime;
473 int size, mode, mtime;
474 if (!PyArg_ParseTuple(args, "iii", &mode, &size, &mtime)) {
474 if (!PyArg_ParseTuple(args, "iii", &mode, &size, &mtime)) {
475 return NULL;
475 return NULL;
476 }
476 }
477 self->flags = dirstate_flag_wc_tracked | dirstate_flag_p1_tracked;
477 self->flags = dirstate_flag_wc_tracked | dirstate_flag_p1_tracked;
478 self->mode = mode;
478 self->mode = mode;
479 self->size = size;
479 self->size = size;
480 self->mtime = mtime;
480 self->mtime = mtime;
481 Py_RETURN_NONE;
481 Py_RETURN_NONE;
482 }
482 }
483
483
484 static PyObject *dirstate_item_set_tracked(dirstateItemObject *self)
484 static PyObject *dirstate_item_set_tracked(dirstateItemObject *self)
485 {
485 {
486 self->flags |= dirstate_flag_wc_tracked;
486 self->flags |= dirstate_flag_wc_tracked;
487 self->flags |= dirstate_flag_possibly_dirty;
487 self->flags |= dirstate_flag_possibly_dirty;
488 /* size = None on the python size turn into size = NON_NORMAL when
488 /* size = None on the python size turn into size = NON_NORMAL when
489 * accessed. So the next line is currently required, but a some future
489 * accessed. So the next line is currently required, but a some future
490 * clean up would be welcome. */
490 * clean up would be welcome. */
491 self->size = dirstate_v1_nonnormal;
491 self->size = dirstate_v1_nonnormal;
492 Py_RETURN_NONE;
492 Py_RETURN_NONE;
493 }
493 }
494
494
495 static PyObject *dirstate_item_set_untracked(dirstateItemObject *self)
495 static PyObject *dirstate_item_set_untracked(dirstateItemObject *self)
496 {
496 {
497 self->flags &= ~dirstate_flag_wc_tracked;
497 self->flags &= ~dirstate_flag_wc_tracked;
498 self->mode = 0;
498 self->mode = 0;
499 self->mtime = 0;
499 self->mtime = 0;
500 self->size = 0;
500 self->size = 0;
501 Py_RETURN_NONE;
501 Py_RETURN_NONE;
502 }
502 }
503
503
504 static PyObject *dirstate_item_drop_merge_data(dirstateItemObject *self)
504 static PyObject *dirstate_item_drop_merge_data(dirstateItemObject *self)
505 {
505 {
506 if (dirstate_item_c_merged(self) || dirstate_item_c_from_p2(self)) {
506 if (dirstate_item_c_merged(self) || dirstate_item_c_from_p2(self)) {
507 if (dirstate_item_c_merged(self)) {
507 if (dirstate_item_c_merged(self)) {
508 self->flags |= dirstate_flag_p1_tracked;
508 self->flags |= dirstate_flag_p1_tracked;
509 } else {
509 } else {
510 self->flags &= ~dirstate_flag_p1_tracked;
510 self->flags &= ~dirstate_flag_p1_tracked;
511 }
511 }
512 self->flags &=
512 self->flags &=
513 ~(dirstate_flag_merged | dirstate_flag_clean_p1 |
513 ~(dirstate_flag_merged | dirstate_flag_clean_p1 |
514 dirstate_flag_clean_p2 | dirstate_flag_p2_tracked);
514 dirstate_flag_clean_p2 | dirstate_flag_p2_tracked);
515 self->flags |= dirstate_flag_possibly_dirty;
515 self->flags |= dirstate_flag_possibly_dirty;
516 self->mode = 0;
516 self->mode = 0;
517 self->mtime = 0;
517 self->mtime = 0;
518 /* size = None on the python size turn into size = NON_NORMAL
518 /* size = None on the python size turn into size = NON_NORMAL
519 * when accessed. So the next line is currently required, but a
519 * when accessed. So the next line is currently required, but a
520 * some future clean up would be welcome. */
520 * some future clean up would be welcome. */
521 self->size = dirstate_v1_nonnormal;
521 self->size = dirstate_v1_nonnormal;
522 }
522 }
523 Py_RETURN_NONE;
523 Py_RETURN_NONE;
524 }
524 }
525 static PyMethodDef dirstate_item_methods[] = {
525 static PyMethodDef dirstate_item_methods[] = {
526 {"v1_state", (PyCFunction)dirstate_item_v1_state, METH_NOARGS,
526 {"v1_state", (PyCFunction)dirstate_item_v1_state, METH_NOARGS,
527 "return a \"state\" suitable for v1 serialization"},
527 "return a \"state\" suitable for v1 serialization"},
528 {"v1_mode", (PyCFunction)dirstate_item_v1_mode, METH_NOARGS,
528 {"v1_mode", (PyCFunction)dirstate_item_v1_mode, METH_NOARGS,
529 "return a \"mode\" suitable for v1 serialization"},
529 "return a \"mode\" suitable for v1 serialization"},
530 {"v1_size", (PyCFunction)dirstate_item_v1_size, METH_NOARGS,
530 {"v1_size", (PyCFunction)dirstate_item_v1_size, METH_NOARGS,
531 "return a \"size\" suitable for v1 serialization"},
531 "return a \"size\" suitable for v1 serialization"},
532 {"v1_mtime", (PyCFunction)dirstate_item_v1_mtime, METH_NOARGS,
532 {"v1_mtime", (PyCFunction)dirstate_item_v1_mtime, METH_NOARGS,
533 "return a \"mtime\" suitable for v1 serialization"},
533 "return a \"mtime\" suitable for v1 serialization"},
534 {"need_delay", (PyCFunction)dirstate_item_need_delay, METH_O,
534 {"need_delay", (PyCFunction)dirstate_item_need_delay, METH_O,
535 "True if the stored mtime would be ambiguous with the current time"},
535 "True if the stored mtime would be ambiguous with the current time"},
536 {"from_v1_data", (PyCFunction)dirstate_item_from_v1_meth,
536 {"from_v1_data", (PyCFunction)dirstate_item_from_v1_meth,
537 METH_VARARGS | METH_CLASS, "build a new DirstateItem object from V1 data"},
537 METH_VARARGS | METH_CLASS, "build a new DirstateItem object from V1 data"},
538 {"new_added", (PyCFunction)dirstate_item_new_added,
538 {"new_added", (PyCFunction)dirstate_item_new_added,
539 METH_NOARGS | METH_CLASS,
539 METH_NOARGS | METH_CLASS,
540 "constructor to help legacy API to build a new \"added\" item"},
540 "constructor to help legacy API to build a new \"added\" item"},
541 {"new_merged", (PyCFunction)dirstate_item_new_merged,
541 {"new_merged", (PyCFunction)dirstate_item_new_merged,
542 METH_NOARGS | METH_CLASS,
542 METH_NOARGS | METH_CLASS,
543 "constructor to help legacy API to build a new \"merged\" item"},
543 "constructor to help legacy API to build a new \"merged\" item"},
544 {"new_from_p2", (PyCFunction)dirstate_item_new_from_p2,
544 {"new_from_p2", (PyCFunction)dirstate_item_new_from_p2,
545 METH_NOARGS | METH_CLASS,
545 METH_NOARGS | METH_CLASS,
546 "constructor to help legacy API to build a new \"from_p2\" item"},
546 "constructor to help legacy API to build a new \"from_p2\" item"},
547 {"new_possibly_dirty", (PyCFunction)dirstate_item_new_possibly_dirty,
547 {"new_possibly_dirty", (PyCFunction)dirstate_item_new_possibly_dirty,
548 METH_NOARGS | METH_CLASS,
548 METH_NOARGS | METH_CLASS,
549 "constructor to help legacy API to build a new \"possibly_dirty\" item"},
549 "constructor to help legacy API to build a new \"possibly_dirty\" item"},
550 {"new_normal", (PyCFunction)dirstate_item_new_normal,
550 {"new_normal", (PyCFunction)dirstate_item_new_normal,
551 METH_VARARGS | METH_CLASS,
551 METH_VARARGS | METH_CLASS,
552 "constructor to help legacy API to build a new \"normal\" item"},
552 "constructor to help legacy API to build a new \"normal\" item"},
553 {"set_possibly_dirty", (PyCFunction)dirstate_item_set_possibly_dirty,
553 {"set_possibly_dirty", (PyCFunction)dirstate_item_set_possibly_dirty,
554 METH_NOARGS, "mark a file as \"possibly dirty\""},
554 METH_NOARGS, "mark a file as \"possibly dirty\""},
555 {"set_clean", (PyCFunction)dirstate_item_set_clean, METH_VARARGS,
555 {"set_clean", (PyCFunction)dirstate_item_set_clean, METH_VARARGS,
556 "mark a file as \"clean\""},
556 "mark a file as \"clean\""},
557 {"set_tracked", (PyCFunction)dirstate_item_set_tracked, METH_NOARGS,
557 {"set_tracked", (PyCFunction)dirstate_item_set_tracked, METH_NOARGS,
558 "mark a file as \"tracked\""},
558 "mark a file as \"tracked\""},
559 {"set_untracked", (PyCFunction)dirstate_item_set_untracked, METH_NOARGS,
559 {"set_untracked", (PyCFunction)dirstate_item_set_untracked, METH_NOARGS,
560 "mark a file as \"untracked\""},
560 "mark a file as \"untracked\""},
561 {"drop_merge_data", (PyCFunction)dirstate_item_drop_merge_data, METH_NOARGS,
561 {"drop_merge_data", (PyCFunction)dirstate_item_drop_merge_data, METH_NOARGS,
562 "remove all \"merge-only\" from a DirstateItem"},
562 "remove all \"merge-only\" from a DirstateItem"},
563 {NULL} /* Sentinel */
563 {NULL} /* Sentinel */
564 };
564 };
565
565
566 static PyObject *dirstate_item_get_mode(dirstateItemObject *self)
566 static PyObject *dirstate_item_get_mode(dirstateItemObject *self)
567 {
567 {
568 return PyInt_FromLong(dirstate_item_c_v1_mode(self));
568 return PyInt_FromLong(dirstate_item_c_v1_mode(self));
569 };
569 };
570
570
571 static PyObject *dirstate_item_get_size(dirstateItemObject *self)
571 static PyObject *dirstate_item_get_size(dirstateItemObject *self)
572 {
572 {
573 return PyInt_FromLong(dirstate_item_c_v1_size(self));
573 return PyInt_FromLong(dirstate_item_c_v1_size(self));
574 };
574 };
575
575
576 static PyObject *dirstate_item_get_mtime(dirstateItemObject *self)
576 static PyObject *dirstate_item_get_mtime(dirstateItemObject *self)
577 {
577 {
578 return PyInt_FromLong(dirstate_item_c_v1_mtime(self));
578 return PyInt_FromLong(dirstate_item_c_v1_mtime(self));
579 };
579 };
580
580
581 static PyObject *dirstate_item_get_state(dirstateItemObject *self)
581 static PyObject *dirstate_item_get_state(dirstateItemObject *self)
582 {
582 {
583 char state = dirstate_item_c_v1_state(self);
583 char state = dirstate_item_c_v1_state(self);
584 return PyBytes_FromStringAndSize(&state, 1);
584 return PyBytes_FromStringAndSize(&state, 1);
585 };
585 };
586
586
587 static PyObject *dirstate_item_get_tracked(dirstateItemObject *self)
587 static PyObject *dirstate_item_get_tracked(dirstateItemObject *self)
588 {
588 {
589 if (dirstate_item_c_tracked(self)) {
589 if (dirstate_item_c_tracked(self)) {
590 Py_RETURN_TRUE;
590 Py_RETURN_TRUE;
591 } else {
591 } else {
592 Py_RETURN_FALSE;
592 Py_RETURN_FALSE;
593 }
593 }
594 };
594 };
595
595
596 static PyObject *dirstate_item_get_added(dirstateItemObject *self)
596 static PyObject *dirstate_item_get_added(dirstateItemObject *self)
597 {
597 {
598 if (dirstate_item_c_added(self)) {
598 if (dirstate_item_c_added(self)) {
599 Py_RETURN_TRUE;
599 Py_RETURN_TRUE;
600 } else {
600 } else {
601 Py_RETURN_FALSE;
601 Py_RETURN_FALSE;
602 }
602 }
603 };
603 };
604
604
605 static PyObject *dirstate_item_get_merged(dirstateItemObject *self)
605 static PyObject *dirstate_item_get_merged(dirstateItemObject *self)
606 {
606 {
607 if (dirstate_item_c_merged(self)) {
607 if (dirstate_item_c_merged(self)) {
608 Py_RETURN_TRUE;
608 Py_RETURN_TRUE;
609 } else {
609 } else {
610 Py_RETURN_FALSE;
610 Py_RETURN_FALSE;
611 }
611 }
612 };
612 };
613
613
614 static PyObject *dirstate_item_get_from_p2(dirstateItemObject *self)
614 static PyObject *dirstate_item_get_from_p2(dirstateItemObject *self)
615 {
615 {
616 if (dirstate_item_c_from_p2(self)) {
616 if (dirstate_item_c_from_p2(self)) {
617 Py_RETURN_TRUE;
617 Py_RETURN_TRUE;
618 } else {
618 } else {
619 Py_RETURN_FALSE;
619 Py_RETURN_FALSE;
620 }
620 }
621 };
621 };
622
622
623 static PyObject *dirstate_item_get_maybe_clean(dirstateItemObject *self)
623 static PyObject *dirstate_item_get_maybe_clean(dirstateItemObject *self)
624 {
624 {
625 if (!(self->flags & dirstate_flag_wc_tracked)) {
625 if (!(self->flags & dirstate_flag_wc_tracked)) {
626 Py_RETURN_FALSE;
626 Py_RETURN_FALSE;
627 } else if (dirstate_item_c_added(self)) {
627 } else if (dirstate_item_c_added(self)) {
628 Py_RETURN_FALSE;
628 Py_RETURN_FALSE;
629 } else if (self->flags & dirstate_flag_merged) {
629 } else if (self->flags & dirstate_flag_merged) {
630 Py_RETURN_FALSE;
630 Py_RETURN_FALSE;
631 } else if (self->flags & dirstate_flag_clean_p2) {
631 } else if (self->flags & dirstate_flag_clean_p2) {
632 Py_RETURN_FALSE;
632 Py_RETURN_FALSE;
633 } else {
633 } else {
634 Py_RETURN_TRUE;
634 Py_RETURN_TRUE;
635 }
635 }
636 };
636 };
637
637
638 static PyObject *dirstate_item_get_any_tracked(dirstateItemObject *self)
639 {
640 unsigned char mask = dirstate_flag_wc_tracked |
641 dirstate_flag_p1_tracked |
642 dirstate_flag_p2_tracked;
643 if ((self->flags & mask) != 0) {
644 Py_RETURN_TRUE;
645 } else {
646 Py_RETURN_FALSE;
647 }
648 };
649
638 static PyObject *dirstate_item_get_removed(dirstateItemObject *self)
650 static PyObject *dirstate_item_get_removed(dirstateItemObject *self)
639 {
651 {
640 if (dirstate_item_c_removed(self)) {
652 if (dirstate_item_c_removed(self)) {
641 Py_RETURN_TRUE;
653 Py_RETURN_TRUE;
642 } else {
654 } else {
643 Py_RETURN_FALSE;
655 Py_RETURN_FALSE;
644 }
656 }
645 };
657 };
646
658
647 static PyGetSetDef dirstate_item_getset[] = {
659 static PyGetSetDef dirstate_item_getset[] = {
648 {"mode", (getter)dirstate_item_get_mode, NULL, "mode", NULL},
660 {"mode", (getter)dirstate_item_get_mode, NULL, "mode", NULL},
649 {"size", (getter)dirstate_item_get_size, NULL, "size", NULL},
661 {"size", (getter)dirstate_item_get_size, NULL, "size", NULL},
650 {"mtime", (getter)dirstate_item_get_mtime, NULL, "mtime", NULL},
662 {"mtime", (getter)dirstate_item_get_mtime, NULL, "mtime", NULL},
651 {"state", (getter)dirstate_item_get_state, NULL, "state", NULL},
663 {"state", (getter)dirstate_item_get_state, NULL, "state", NULL},
652 {"tracked", (getter)dirstate_item_get_tracked, NULL, "tracked", NULL},
664 {"tracked", (getter)dirstate_item_get_tracked, NULL, "tracked", NULL},
653 {"added", (getter)dirstate_item_get_added, NULL, "added", NULL},
665 {"added", (getter)dirstate_item_get_added, NULL, "added", NULL},
654 {"merged", (getter)dirstate_item_get_merged, NULL, "merged", NULL},
666 {"merged", (getter)dirstate_item_get_merged, NULL, "merged", NULL},
655 {"from_p2", (getter)dirstate_item_get_from_p2, NULL, "from_p2", NULL},
667 {"from_p2", (getter)dirstate_item_get_from_p2, NULL, "from_p2", NULL},
656 {"maybe_clean", (getter)dirstate_item_get_maybe_clean, NULL, "maybe_clean",
668 {"maybe_clean", (getter)dirstate_item_get_maybe_clean, NULL, "maybe_clean",
657 NULL},
669 NULL},
670 {"any_tracked", (getter)dirstate_item_get_any_tracked, NULL, "any_tracked",
671 NULL},
658 {"removed", (getter)dirstate_item_get_removed, NULL, "removed", NULL},
672 {"removed", (getter)dirstate_item_get_removed, NULL, "removed", NULL},
659 {NULL} /* Sentinel */
673 {NULL} /* Sentinel */
660 };
674 };
661
675
662 PyTypeObject dirstateItemType = {
676 PyTypeObject dirstateItemType = {
663 PyVarObject_HEAD_INIT(NULL, 0) /* header */
677 PyVarObject_HEAD_INIT(NULL, 0) /* header */
664 "dirstate_tuple", /* tp_name */
678 "dirstate_tuple", /* tp_name */
665 sizeof(dirstateItemObject), /* tp_basicsize */
679 sizeof(dirstateItemObject), /* tp_basicsize */
666 0, /* tp_itemsize */
680 0, /* tp_itemsize */
667 (destructor)dirstate_item_dealloc, /* tp_dealloc */
681 (destructor)dirstate_item_dealloc, /* tp_dealloc */
668 0, /* tp_print */
682 0, /* tp_print */
669 0, /* tp_getattr */
683 0, /* tp_getattr */
670 0, /* tp_setattr */
684 0, /* tp_setattr */
671 0, /* tp_compare */
685 0, /* tp_compare */
672 0, /* tp_repr */
686 0, /* tp_repr */
673 0, /* tp_as_number */
687 0, /* tp_as_number */
674 0, /* tp_as_sequence */
688 0, /* tp_as_sequence */
675 0, /* tp_as_mapping */
689 0, /* tp_as_mapping */
676 0, /* tp_hash */
690 0, /* tp_hash */
677 0, /* tp_call */
691 0, /* tp_call */
678 0, /* tp_str */
692 0, /* tp_str */
679 0, /* tp_getattro */
693 0, /* tp_getattro */
680 0, /* tp_setattro */
694 0, /* tp_setattro */
681 0, /* tp_as_buffer */
695 0, /* tp_as_buffer */
682 Py_TPFLAGS_DEFAULT, /* tp_flags */
696 Py_TPFLAGS_DEFAULT, /* tp_flags */
683 "dirstate tuple", /* tp_doc */
697 "dirstate tuple", /* tp_doc */
684 0, /* tp_traverse */
698 0, /* tp_traverse */
685 0, /* tp_clear */
699 0, /* tp_clear */
686 0, /* tp_richcompare */
700 0, /* tp_richcompare */
687 0, /* tp_weaklistoffset */
701 0, /* tp_weaklistoffset */
688 0, /* tp_iter */
702 0, /* tp_iter */
689 0, /* tp_iternext */
703 0, /* tp_iternext */
690 dirstate_item_methods, /* tp_methods */
704 dirstate_item_methods, /* tp_methods */
691 0, /* tp_members */
705 0, /* tp_members */
692 dirstate_item_getset, /* tp_getset */
706 dirstate_item_getset, /* tp_getset */
693 0, /* tp_base */
707 0, /* tp_base */
694 0, /* tp_dict */
708 0, /* tp_dict */
695 0, /* tp_descr_get */
709 0, /* tp_descr_get */
696 0, /* tp_descr_set */
710 0, /* tp_descr_set */
697 0, /* tp_dictoffset */
711 0, /* tp_dictoffset */
698 0, /* tp_init */
712 0, /* tp_init */
699 0, /* tp_alloc */
713 0, /* tp_alloc */
700 dirstate_item_new, /* tp_new */
714 dirstate_item_new, /* tp_new */
701 };
715 };
702
716
703 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
717 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
704 {
718 {
705 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
719 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
706 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
720 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
707 char state, *cur, *str, *cpos;
721 char state, *cur, *str, *cpos;
708 int mode, size, mtime;
722 int mode, size, mtime;
709 unsigned int flen, pos = 40;
723 unsigned int flen, pos = 40;
710 Py_ssize_t len = 40;
724 Py_ssize_t len = 40;
711 Py_ssize_t readlen;
725 Py_ssize_t readlen;
712
726
713 if (!PyArg_ParseTuple(
727 if (!PyArg_ParseTuple(
714 args, PY23("O!O!s#:parse_dirstate", "O!O!y#:parse_dirstate"),
728 args, PY23("O!O!s#:parse_dirstate", "O!O!y#:parse_dirstate"),
715 &PyDict_Type, &dmap, &PyDict_Type, &cmap, &str, &readlen)) {
729 &PyDict_Type, &dmap, &PyDict_Type, &cmap, &str, &readlen)) {
716 goto quit;
730 goto quit;
717 }
731 }
718
732
719 len = readlen;
733 len = readlen;
720
734
721 /* read parents */
735 /* read parents */
722 if (len < 40) {
736 if (len < 40) {
723 PyErr_SetString(PyExc_ValueError,
737 PyErr_SetString(PyExc_ValueError,
724 "too little data for parents");
738 "too little data for parents");
725 goto quit;
739 goto quit;
726 }
740 }
727
741
728 parents = Py_BuildValue(PY23("s#s#", "y#y#"), str, (Py_ssize_t)20,
742 parents = Py_BuildValue(PY23("s#s#", "y#y#"), str, (Py_ssize_t)20,
729 str + 20, (Py_ssize_t)20);
743 str + 20, (Py_ssize_t)20);
730 if (!parents) {
744 if (!parents) {
731 goto quit;
745 goto quit;
732 }
746 }
733
747
734 /* read filenames */
748 /* read filenames */
735 while (pos >= 40 && pos < len) {
749 while (pos >= 40 && pos < len) {
736 if (pos + 17 > len) {
750 if (pos + 17 > len) {
737 PyErr_SetString(PyExc_ValueError,
751 PyErr_SetString(PyExc_ValueError,
738 "overflow in dirstate");
752 "overflow in dirstate");
739 goto quit;
753 goto quit;
740 }
754 }
741 cur = str + pos;
755 cur = str + pos;
742 /* unpack header */
756 /* unpack header */
743 state = *cur;
757 state = *cur;
744 mode = getbe32(cur + 1);
758 mode = getbe32(cur + 1);
745 size = getbe32(cur + 5);
759 size = getbe32(cur + 5);
746 mtime = getbe32(cur + 9);
760 mtime = getbe32(cur + 9);
747 flen = getbe32(cur + 13);
761 flen = getbe32(cur + 13);
748 pos += 17;
762 pos += 17;
749 cur += 17;
763 cur += 17;
750 if (flen > len - pos) {
764 if (flen > len - pos) {
751 PyErr_SetString(PyExc_ValueError,
765 PyErr_SetString(PyExc_ValueError,
752 "overflow in dirstate");
766 "overflow in dirstate");
753 goto quit;
767 goto quit;
754 }
768 }
755
769
756 entry = (PyObject *)dirstate_item_from_v1_data(state, mode,
770 entry = (PyObject *)dirstate_item_from_v1_data(state, mode,
757 size, mtime);
771 size, mtime);
758 if (!entry)
772 if (!entry)
759 goto quit;
773 goto quit;
760 cpos = memchr(cur, 0, flen);
774 cpos = memchr(cur, 0, flen);
761 if (cpos) {
775 if (cpos) {
762 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
776 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
763 cname = PyBytes_FromStringAndSize(
777 cname = PyBytes_FromStringAndSize(
764 cpos + 1, flen - (cpos - cur) - 1);
778 cpos + 1, flen - (cpos - cur) - 1);
765 if (!fname || !cname ||
779 if (!fname || !cname ||
766 PyDict_SetItem(cmap, fname, cname) == -1 ||
780 PyDict_SetItem(cmap, fname, cname) == -1 ||
767 PyDict_SetItem(dmap, fname, entry) == -1) {
781 PyDict_SetItem(dmap, fname, entry) == -1) {
768 goto quit;
782 goto quit;
769 }
783 }
770 Py_DECREF(cname);
784 Py_DECREF(cname);
771 } else {
785 } else {
772 fname = PyBytes_FromStringAndSize(cur, flen);
786 fname = PyBytes_FromStringAndSize(cur, flen);
773 if (!fname ||
787 if (!fname ||
774 PyDict_SetItem(dmap, fname, entry) == -1) {
788 PyDict_SetItem(dmap, fname, entry) == -1) {
775 goto quit;
789 goto quit;
776 }
790 }
777 }
791 }
778 Py_DECREF(fname);
792 Py_DECREF(fname);
779 Py_DECREF(entry);
793 Py_DECREF(entry);
780 fname = cname = entry = NULL;
794 fname = cname = entry = NULL;
781 pos += flen;
795 pos += flen;
782 }
796 }
783
797
784 ret = parents;
798 ret = parents;
785 Py_INCREF(ret);
799 Py_INCREF(ret);
786 quit:
800 quit:
787 Py_XDECREF(fname);
801 Py_XDECREF(fname);
788 Py_XDECREF(cname);
802 Py_XDECREF(cname);
789 Py_XDECREF(entry);
803 Py_XDECREF(entry);
790 Py_XDECREF(parents);
804 Py_XDECREF(parents);
791 return ret;
805 return ret;
792 }
806 }
793
807
794 /*
808 /*
795 * Efficiently pack a dirstate object into its on-disk format.
809 * Efficiently pack a dirstate object into its on-disk format.
796 */
810 */
797 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
811 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
798 {
812 {
799 PyObject *packobj = NULL;
813 PyObject *packobj = NULL;
800 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
814 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
801 Py_ssize_t nbytes, pos, l;
815 Py_ssize_t nbytes, pos, l;
802 PyObject *k, *v = NULL, *pn;
816 PyObject *k, *v = NULL, *pn;
803 char *p, *s;
817 char *p, *s;
804 int now;
818 int now;
805
819
806 if (!PyArg_ParseTuple(args, "O!O!O!i:pack_dirstate", &PyDict_Type, &map,
820 if (!PyArg_ParseTuple(args, "O!O!O!i:pack_dirstate", &PyDict_Type, &map,
807 &PyDict_Type, &copymap, &PyTuple_Type, &pl,
821 &PyDict_Type, &copymap, &PyTuple_Type, &pl,
808 &now)) {
822 &now)) {
809 return NULL;
823 return NULL;
810 }
824 }
811
825
812 if (PyTuple_Size(pl) != 2) {
826 if (PyTuple_Size(pl) != 2) {
813 PyErr_SetString(PyExc_TypeError, "expected 2-element tuple");
827 PyErr_SetString(PyExc_TypeError, "expected 2-element tuple");
814 return NULL;
828 return NULL;
815 }
829 }
816
830
817 /* Figure out how much we need to allocate. */
831 /* Figure out how much we need to allocate. */
818 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
832 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
819 PyObject *c;
833 PyObject *c;
820 if (!PyBytes_Check(k)) {
834 if (!PyBytes_Check(k)) {
821 PyErr_SetString(PyExc_TypeError, "expected string key");
835 PyErr_SetString(PyExc_TypeError, "expected string key");
822 goto bail;
836 goto bail;
823 }
837 }
824 nbytes += PyBytes_GET_SIZE(k) + 17;
838 nbytes += PyBytes_GET_SIZE(k) + 17;
825 c = PyDict_GetItem(copymap, k);
839 c = PyDict_GetItem(copymap, k);
826 if (c) {
840 if (c) {
827 if (!PyBytes_Check(c)) {
841 if (!PyBytes_Check(c)) {
828 PyErr_SetString(PyExc_TypeError,
842 PyErr_SetString(PyExc_TypeError,
829 "expected string key");
843 "expected string key");
830 goto bail;
844 goto bail;
831 }
845 }
832 nbytes += PyBytes_GET_SIZE(c) + 1;
846 nbytes += PyBytes_GET_SIZE(c) + 1;
833 }
847 }
834 }
848 }
835
849
836 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
850 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
837 if (packobj == NULL) {
851 if (packobj == NULL) {
838 goto bail;
852 goto bail;
839 }
853 }
840
854
841 p = PyBytes_AS_STRING(packobj);
855 p = PyBytes_AS_STRING(packobj);
842
856
843 pn = PyTuple_GET_ITEM(pl, 0);
857 pn = PyTuple_GET_ITEM(pl, 0);
844 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
858 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
845 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
859 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
846 goto bail;
860 goto bail;
847 }
861 }
848 memcpy(p, s, l);
862 memcpy(p, s, l);
849 p += 20;
863 p += 20;
850 pn = PyTuple_GET_ITEM(pl, 1);
864 pn = PyTuple_GET_ITEM(pl, 1);
851 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
865 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
852 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
866 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
853 goto bail;
867 goto bail;
854 }
868 }
855 memcpy(p, s, l);
869 memcpy(p, s, l);
856 p += 20;
870 p += 20;
857
871
858 for (pos = 0; PyDict_Next(map, &pos, &k, &v);) {
872 for (pos = 0; PyDict_Next(map, &pos, &k, &v);) {
859 dirstateItemObject *tuple;
873 dirstateItemObject *tuple;
860 char state;
874 char state;
861 int mode, size, mtime;
875 int mode, size, mtime;
862 Py_ssize_t len, l;
876 Py_ssize_t len, l;
863 PyObject *o;
877 PyObject *o;
864 char *t;
878 char *t;
865
879
866 if (!dirstate_tuple_check(v)) {
880 if (!dirstate_tuple_check(v)) {
867 PyErr_SetString(PyExc_TypeError,
881 PyErr_SetString(PyExc_TypeError,
868 "expected a dirstate tuple");
882 "expected a dirstate tuple");
869 goto bail;
883 goto bail;
870 }
884 }
871 tuple = (dirstateItemObject *)v;
885 tuple = (dirstateItemObject *)v;
872
886
873 state = dirstate_item_c_v1_state(tuple);
887 state = dirstate_item_c_v1_state(tuple);
874 mode = dirstate_item_c_v1_mode(tuple);
888 mode = dirstate_item_c_v1_mode(tuple);
875 size = dirstate_item_c_v1_size(tuple);
889 size = dirstate_item_c_v1_size(tuple);
876 mtime = dirstate_item_c_v1_mtime(tuple);
890 mtime = dirstate_item_c_v1_mtime(tuple);
877 if (state == 'n' && mtime == now) {
891 if (state == 'n' && mtime == now) {
878 /* See pure/parsers.py:pack_dirstate for why we do
892 /* See pure/parsers.py:pack_dirstate for why we do
879 * this. */
893 * this. */
880 mtime = -1;
894 mtime = -1;
881 mtime_unset = (PyObject *)dirstate_item_from_v1_data(
895 mtime_unset = (PyObject *)dirstate_item_from_v1_data(
882 state, mode, size, mtime);
896 state, mode, size, mtime);
883 if (!mtime_unset) {
897 if (!mtime_unset) {
884 goto bail;
898 goto bail;
885 }
899 }
886 if (PyDict_SetItem(map, k, mtime_unset) == -1) {
900 if (PyDict_SetItem(map, k, mtime_unset) == -1) {
887 goto bail;
901 goto bail;
888 }
902 }
889 Py_DECREF(mtime_unset);
903 Py_DECREF(mtime_unset);
890 mtime_unset = NULL;
904 mtime_unset = NULL;
891 }
905 }
892 *p++ = state;
906 *p++ = state;
893 putbe32((uint32_t)mode, p);
907 putbe32((uint32_t)mode, p);
894 putbe32((uint32_t)size, p + 4);
908 putbe32((uint32_t)size, p + 4);
895 putbe32((uint32_t)mtime, p + 8);
909 putbe32((uint32_t)mtime, p + 8);
896 t = p + 12;
910 t = p + 12;
897 p += 16;
911 p += 16;
898 len = PyBytes_GET_SIZE(k);
912 len = PyBytes_GET_SIZE(k);
899 memcpy(p, PyBytes_AS_STRING(k), len);
913 memcpy(p, PyBytes_AS_STRING(k), len);
900 p += len;
914 p += len;
901 o = PyDict_GetItem(copymap, k);
915 o = PyDict_GetItem(copymap, k);
902 if (o) {
916 if (o) {
903 *p++ = '\0';
917 *p++ = '\0';
904 l = PyBytes_GET_SIZE(o);
918 l = PyBytes_GET_SIZE(o);
905 memcpy(p, PyBytes_AS_STRING(o), l);
919 memcpy(p, PyBytes_AS_STRING(o), l);
906 p += l;
920 p += l;
907 len += l + 1;
921 len += l + 1;
908 }
922 }
909 putbe32((uint32_t)len, t);
923 putbe32((uint32_t)len, t);
910 }
924 }
911
925
912 pos = p - PyBytes_AS_STRING(packobj);
926 pos = p - PyBytes_AS_STRING(packobj);
913 if (pos != nbytes) {
927 if (pos != nbytes) {
914 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
928 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
915 (long)pos, (long)nbytes);
929 (long)pos, (long)nbytes);
916 goto bail;
930 goto bail;
917 }
931 }
918
932
919 return packobj;
933 return packobj;
920 bail:
934 bail:
921 Py_XDECREF(mtime_unset);
935 Py_XDECREF(mtime_unset);
922 Py_XDECREF(packobj);
936 Py_XDECREF(packobj);
923 Py_XDECREF(v);
937 Py_XDECREF(v);
924 return NULL;
938 return NULL;
925 }
939 }
926
940
927 #define BUMPED_FIX 1
941 #define BUMPED_FIX 1
928 #define USING_SHA_256 2
942 #define USING_SHA_256 2
929 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
943 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
930
944
931 static PyObject *readshas(const char *source, unsigned char num,
945 static PyObject *readshas(const char *source, unsigned char num,
932 Py_ssize_t hashwidth)
946 Py_ssize_t hashwidth)
933 {
947 {
934 int i;
948 int i;
935 PyObject *list = PyTuple_New(num);
949 PyObject *list = PyTuple_New(num);
936 if (list == NULL) {
950 if (list == NULL) {
937 return NULL;
951 return NULL;
938 }
952 }
939 for (i = 0; i < num; i++) {
953 for (i = 0; i < num; i++) {
940 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
954 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
941 if (hash == NULL) {
955 if (hash == NULL) {
942 Py_DECREF(list);
956 Py_DECREF(list);
943 return NULL;
957 return NULL;
944 }
958 }
945 PyTuple_SET_ITEM(list, i, hash);
959 PyTuple_SET_ITEM(list, i, hash);
946 source += hashwidth;
960 source += hashwidth;
947 }
961 }
948 return list;
962 return list;
949 }
963 }
950
964
951 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
965 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
952 uint32_t *msize)
966 uint32_t *msize)
953 {
967 {
954 const char *data = databegin;
968 const char *data = databegin;
955 const char *meta;
969 const char *meta;
956
970
957 double mtime;
971 double mtime;
958 int16_t tz;
972 int16_t tz;
959 uint16_t flags;
973 uint16_t flags;
960 unsigned char nsuccs, nparents, nmetadata;
974 unsigned char nsuccs, nparents, nmetadata;
961 Py_ssize_t hashwidth = 20;
975 Py_ssize_t hashwidth = 20;
962
976
963 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
977 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
964 PyObject *metadata = NULL, *ret = NULL;
978 PyObject *metadata = NULL, *ret = NULL;
965 int i;
979 int i;
966
980
967 if (data + FM1_HEADER_SIZE > dataend) {
981 if (data + FM1_HEADER_SIZE > dataend) {
968 goto overflow;
982 goto overflow;
969 }
983 }
970
984
971 *msize = getbe32(data);
985 *msize = getbe32(data);
972 data += 4;
986 data += 4;
973 mtime = getbefloat64(data);
987 mtime = getbefloat64(data);
974 data += 8;
988 data += 8;
975 tz = getbeint16(data);
989 tz = getbeint16(data);
976 data += 2;
990 data += 2;
977 flags = getbeuint16(data);
991 flags = getbeuint16(data);
978 data += 2;
992 data += 2;
979
993
980 if (flags & USING_SHA_256) {
994 if (flags & USING_SHA_256) {
981 hashwidth = 32;
995 hashwidth = 32;
982 }
996 }
983
997
984 nsuccs = (unsigned char)(*data++);
998 nsuccs = (unsigned char)(*data++);
985 nparents = (unsigned char)(*data++);
999 nparents = (unsigned char)(*data++);
986 nmetadata = (unsigned char)(*data++);
1000 nmetadata = (unsigned char)(*data++);
987
1001
988 if (databegin + *msize > dataend) {
1002 if (databegin + *msize > dataend) {
989 goto overflow;
1003 goto overflow;
990 }
1004 }
991 dataend = databegin + *msize; /* narrow down to marker size */
1005 dataend = databegin + *msize; /* narrow down to marker size */
992
1006
993 if (data + hashwidth > dataend) {
1007 if (data + hashwidth > dataend) {
994 goto overflow;
1008 goto overflow;
995 }
1009 }
996 prec = PyBytes_FromStringAndSize(data, hashwidth);
1010 prec = PyBytes_FromStringAndSize(data, hashwidth);
997 data += hashwidth;
1011 data += hashwidth;
998 if (prec == NULL) {
1012 if (prec == NULL) {
999 goto bail;
1013 goto bail;
1000 }
1014 }
1001
1015
1002 if (data + nsuccs * hashwidth > dataend) {
1016 if (data + nsuccs * hashwidth > dataend) {
1003 goto overflow;
1017 goto overflow;
1004 }
1018 }
1005 succs = readshas(data, nsuccs, hashwidth);
1019 succs = readshas(data, nsuccs, hashwidth);
1006 if (succs == NULL) {
1020 if (succs == NULL) {
1007 goto bail;
1021 goto bail;
1008 }
1022 }
1009 data += nsuccs * hashwidth;
1023 data += nsuccs * hashwidth;
1010
1024
1011 if (nparents == 1 || nparents == 2) {
1025 if (nparents == 1 || nparents == 2) {
1012 if (data + nparents * hashwidth > dataend) {
1026 if (data + nparents * hashwidth > dataend) {
1013 goto overflow;
1027 goto overflow;
1014 }
1028 }
1015 parents = readshas(data, nparents, hashwidth);
1029 parents = readshas(data, nparents, hashwidth);
1016 if (parents == NULL) {
1030 if (parents == NULL) {
1017 goto bail;
1031 goto bail;
1018 }
1032 }
1019 data += nparents * hashwidth;
1033 data += nparents * hashwidth;
1020 } else {
1034 } else {
1021 parents = Py_None;
1035 parents = Py_None;
1022 Py_INCREF(parents);
1036 Py_INCREF(parents);
1023 }
1037 }
1024
1038
1025 if (data + 2 * nmetadata > dataend) {
1039 if (data + 2 * nmetadata > dataend) {
1026 goto overflow;
1040 goto overflow;
1027 }
1041 }
1028 meta = data + (2 * nmetadata);
1042 meta = data + (2 * nmetadata);
1029 metadata = PyTuple_New(nmetadata);
1043 metadata = PyTuple_New(nmetadata);
1030 if (metadata == NULL) {
1044 if (metadata == NULL) {
1031 goto bail;
1045 goto bail;
1032 }
1046 }
1033 for (i = 0; i < nmetadata; i++) {
1047 for (i = 0; i < nmetadata; i++) {
1034 PyObject *tmp, *left = NULL, *right = NULL;
1048 PyObject *tmp, *left = NULL, *right = NULL;
1035 Py_ssize_t leftsize = (unsigned char)(*data++);
1049 Py_ssize_t leftsize = (unsigned char)(*data++);
1036 Py_ssize_t rightsize = (unsigned char)(*data++);
1050 Py_ssize_t rightsize = (unsigned char)(*data++);
1037 if (meta + leftsize + rightsize > dataend) {
1051 if (meta + leftsize + rightsize > dataend) {
1038 goto overflow;
1052 goto overflow;
1039 }
1053 }
1040 left = PyBytes_FromStringAndSize(meta, leftsize);
1054 left = PyBytes_FromStringAndSize(meta, leftsize);
1041 meta += leftsize;
1055 meta += leftsize;
1042 right = PyBytes_FromStringAndSize(meta, rightsize);
1056 right = PyBytes_FromStringAndSize(meta, rightsize);
1043 meta += rightsize;
1057 meta += rightsize;
1044 tmp = PyTuple_New(2);
1058 tmp = PyTuple_New(2);
1045 if (!left || !right || !tmp) {
1059 if (!left || !right || !tmp) {
1046 Py_XDECREF(left);
1060 Py_XDECREF(left);
1047 Py_XDECREF(right);
1061 Py_XDECREF(right);
1048 Py_XDECREF(tmp);
1062 Py_XDECREF(tmp);
1049 goto bail;
1063 goto bail;
1050 }
1064 }
1051 PyTuple_SET_ITEM(tmp, 0, left);
1065 PyTuple_SET_ITEM(tmp, 0, left);
1052 PyTuple_SET_ITEM(tmp, 1, right);
1066 PyTuple_SET_ITEM(tmp, 1, right);
1053 PyTuple_SET_ITEM(metadata, i, tmp);
1067 PyTuple_SET_ITEM(metadata, i, tmp);
1054 }
1068 }
1055 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags, metadata, mtime,
1069 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags, metadata, mtime,
1056 (int)tz * 60, parents);
1070 (int)tz * 60, parents);
1057 goto bail; /* return successfully */
1071 goto bail; /* return successfully */
1058
1072
1059 overflow:
1073 overflow:
1060 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
1074 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
1061 bail:
1075 bail:
1062 Py_XDECREF(prec);
1076 Py_XDECREF(prec);
1063 Py_XDECREF(succs);
1077 Py_XDECREF(succs);
1064 Py_XDECREF(metadata);
1078 Py_XDECREF(metadata);
1065 Py_XDECREF(parents);
1079 Py_XDECREF(parents);
1066 return ret;
1080 return ret;
1067 }
1081 }
1068
1082
1069 static PyObject *fm1readmarkers(PyObject *self, PyObject *args)
1083 static PyObject *fm1readmarkers(PyObject *self, PyObject *args)
1070 {
1084 {
1071 const char *data, *dataend;
1085 const char *data, *dataend;
1072 Py_ssize_t datalen, offset, stop;
1086 Py_ssize_t datalen, offset, stop;
1073 PyObject *markers = NULL;
1087 PyObject *markers = NULL;
1074
1088
1075 if (!PyArg_ParseTuple(args, PY23("s#nn", "y#nn"), &data, &datalen,
1089 if (!PyArg_ParseTuple(args, PY23("s#nn", "y#nn"), &data, &datalen,
1076 &offset, &stop)) {
1090 &offset, &stop)) {
1077 return NULL;
1091 return NULL;
1078 }
1092 }
1079 if (offset < 0) {
1093 if (offset < 0) {
1080 PyErr_SetString(PyExc_ValueError,
1094 PyErr_SetString(PyExc_ValueError,
1081 "invalid negative offset in fm1readmarkers");
1095 "invalid negative offset in fm1readmarkers");
1082 return NULL;
1096 return NULL;
1083 }
1097 }
1084 if (stop > datalen) {
1098 if (stop > datalen) {
1085 PyErr_SetString(
1099 PyErr_SetString(
1086 PyExc_ValueError,
1100 PyExc_ValueError,
1087 "stop longer than data length in fm1readmarkers");
1101 "stop longer than data length in fm1readmarkers");
1088 return NULL;
1102 return NULL;
1089 }
1103 }
1090 dataend = data + datalen;
1104 dataend = data + datalen;
1091 data += offset;
1105 data += offset;
1092 markers = PyList_New(0);
1106 markers = PyList_New(0);
1093 if (!markers) {
1107 if (!markers) {
1094 return NULL;
1108 return NULL;
1095 }
1109 }
1096 while (offset < stop) {
1110 while (offset < stop) {
1097 uint32_t msize;
1111 uint32_t msize;
1098 int error;
1112 int error;
1099 PyObject *record = fm1readmarker(data, dataend, &msize);
1113 PyObject *record = fm1readmarker(data, dataend, &msize);
1100 if (!record) {
1114 if (!record) {
1101 goto bail;
1115 goto bail;
1102 }
1116 }
1103 error = PyList_Append(markers, record);
1117 error = PyList_Append(markers, record);
1104 Py_DECREF(record);
1118 Py_DECREF(record);
1105 if (error) {
1119 if (error) {
1106 goto bail;
1120 goto bail;
1107 }
1121 }
1108 data += msize;
1122 data += msize;
1109 offset += msize;
1123 offset += msize;
1110 }
1124 }
1111 return markers;
1125 return markers;
1112 bail:
1126 bail:
1113 Py_DECREF(markers);
1127 Py_DECREF(markers);
1114 return NULL;
1128 return NULL;
1115 }
1129 }
1116
1130
1117 static char parsers_doc[] = "Efficient content parsing.";
1131 static char parsers_doc[] = "Efficient content parsing.";
1118
1132
1119 PyObject *encodedir(PyObject *self, PyObject *args);
1133 PyObject *encodedir(PyObject *self, PyObject *args);
1120 PyObject *pathencode(PyObject *self, PyObject *args);
1134 PyObject *pathencode(PyObject *self, PyObject *args);
1121 PyObject *lowerencode(PyObject *self, PyObject *args);
1135 PyObject *lowerencode(PyObject *self, PyObject *args);
1122 PyObject *parse_index2(PyObject *self, PyObject *args, PyObject *kwargs);
1136 PyObject *parse_index2(PyObject *self, PyObject *args, PyObject *kwargs);
1123
1137
1124 static PyMethodDef methods[] = {
1138 static PyMethodDef methods[] = {
1125 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
1139 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
1126 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
1140 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
1127 {"parse_index2", (PyCFunction)parse_index2, METH_VARARGS | METH_KEYWORDS,
1141 {"parse_index2", (PyCFunction)parse_index2, METH_VARARGS | METH_KEYWORDS,
1128 "parse a revlog index\n"},
1142 "parse a revlog index\n"},
1129 {"isasciistr", isasciistr, METH_VARARGS, "check if an ASCII string\n"},
1143 {"isasciistr", isasciistr, METH_VARARGS, "check if an ASCII string\n"},
1130 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
1144 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
1131 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
1145 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
1132 {"dict_new_presized", dict_new_presized, METH_VARARGS,
1146 {"dict_new_presized", dict_new_presized, METH_VARARGS,
1133 "construct a dict with an expected size\n"},
1147 "construct a dict with an expected size\n"},
1134 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
1148 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
1135 "make file foldmap\n"},
1149 "make file foldmap\n"},
1136 {"jsonescapeu8fast", jsonescapeu8fast, METH_VARARGS,
1150 {"jsonescapeu8fast", jsonescapeu8fast, METH_VARARGS,
1137 "escape a UTF-8 byte string to JSON (fast path)\n"},
1151 "escape a UTF-8 byte string to JSON (fast path)\n"},
1138 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
1152 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
1139 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
1153 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
1140 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
1154 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
1141 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
1155 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
1142 "parse v1 obsolete markers\n"},
1156 "parse v1 obsolete markers\n"},
1143 {NULL, NULL}};
1157 {NULL, NULL}};
1144
1158
1145 void dirs_module_init(PyObject *mod);
1159 void dirs_module_init(PyObject *mod);
1146 void manifest_module_init(PyObject *mod);
1160 void manifest_module_init(PyObject *mod);
1147 void revlog_module_init(PyObject *mod);
1161 void revlog_module_init(PyObject *mod);
1148
1162
1149 static const int version = 20;
1163 static const int version = 20;
1150
1164
1151 static void module_init(PyObject *mod)
1165 static void module_init(PyObject *mod)
1152 {
1166 {
1153 PyModule_AddIntConstant(mod, "version", version);
1167 PyModule_AddIntConstant(mod, "version", version);
1154
1168
1155 /* This module constant has two purposes. First, it lets us unit test
1169 /* This module constant has two purposes. First, it lets us unit test
1156 * the ImportError raised without hard-coding any error text. This
1170 * the ImportError raised without hard-coding any error text. This
1157 * means we can change the text in the future without breaking tests,
1171 * means we can change the text in the future without breaking tests,
1158 * even across changesets without a recompile. Second, its presence
1172 * even across changesets without a recompile. Second, its presence
1159 * can be used to determine whether the version-checking logic is
1173 * can be used to determine whether the version-checking logic is
1160 * present, which also helps in testing across changesets without a
1174 * present, which also helps in testing across changesets without a
1161 * recompile. Note that this means the pure-Python version of parsers
1175 * recompile. Note that this means the pure-Python version of parsers
1162 * should not have this module constant. */
1176 * should not have this module constant. */
1163 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
1177 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
1164
1178
1165 dirs_module_init(mod);
1179 dirs_module_init(mod);
1166 manifest_module_init(mod);
1180 manifest_module_init(mod);
1167 revlog_module_init(mod);
1181 revlog_module_init(mod);
1168
1182
1169 if (PyType_Ready(&dirstateItemType) < 0) {
1183 if (PyType_Ready(&dirstateItemType) < 0) {
1170 return;
1184 return;
1171 }
1185 }
1172 Py_INCREF(&dirstateItemType);
1186 Py_INCREF(&dirstateItemType);
1173 PyModule_AddObject(mod, "DirstateItem", (PyObject *)&dirstateItemType);
1187 PyModule_AddObject(mod, "DirstateItem", (PyObject *)&dirstateItemType);
1174 }
1188 }
1175
1189
1176 static int check_python_version(void)
1190 static int check_python_version(void)
1177 {
1191 {
1178 PyObject *sys = PyImport_ImportModule("sys"), *ver;
1192 PyObject *sys = PyImport_ImportModule("sys"), *ver;
1179 long hexversion;
1193 long hexversion;
1180 if (!sys) {
1194 if (!sys) {
1181 return -1;
1195 return -1;
1182 }
1196 }
1183 ver = PyObject_GetAttrString(sys, "hexversion");
1197 ver = PyObject_GetAttrString(sys, "hexversion");
1184 Py_DECREF(sys);
1198 Py_DECREF(sys);
1185 if (!ver) {
1199 if (!ver) {
1186 return -1;
1200 return -1;
1187 }
1201 }
1188 hexversion = PyInt_AsLong(ver);
1202 hexversion = PyInt_AsLong(ver);
1189 Py_DECREF(ver);
1203 Py_DECREF(ver);
1190 /* sys.hexversion is a 32-bit number by default, so the -1 case
1204 /* sys.hexversion is a 32-bit number by default, so the -1 case
1191 * should only occur in unusual circumstances (e.g. if sys.hexversion
1205 * should only occur in unusual circumstances (e.g. if sys.hexversion
1192 * is manually set to an invalid value). */
1206 * is manually set to an invalid value). */
1193 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
1207 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
1194 PyErr_Format(PyExc_ImportError,
1208 PyErr_Format(PyExc_ImportError,
1195 "%s: The Mercurial extension "
1209 "%s: The Mercurial extension "
1196 "modules were compiled with Python " PY_VERSION
1210 "modules were compiled with Python " PY_VERSION
1197 ", but "
1211 ", but "
1198 "Mercurial is currently using Python with "
1212 "Mercurial is currently using Python with "
1199 "sys.hexversion=%ld: "
1213 "sys.hexversion=%ld: "
1200 "Python %s\n at: %s",
1214 "Python %s\n at: %s",
1201 versionerrortext, hexversion, Py_GetVersion(),
1215 versionerrortext, hexversion, Py_GetVersion(),
1202 Py_GetProgramFullPath());
1216 Py_GetProgramFullPath());
1203 return -1;
1217 return -1;
1204 }
1218 }
1205 return 0;
1219 return 0;
1206 }
1220 }
1207
1221
1208 #ifdef IS_PY3K
1222 #ifdef IS_PY3K
1209 static struct PyModuleDef parsers_module = {PyModuleDef_HEAD_INIT, "parsers",
1223 static struct PyModuleDef parsers_module = {PyModuleDef_HEAD_INIT, "parsers",
1210 parsers_doc, -1, methods};
1224 parsers_doc, -1, methods};
1211
1225
1212 PyMODINIT_FUNC PyInit_parsers(void)
1226 PyMODINIT_FUNC PyInit_parsers(void)
1213 {
1227 {
1214 PyObject *mod;
1228 PyObject *mod;
1215
1229
1216 if (check_python_version() == -1)
1230 if (check_python_version() == -1)
1217 return NULL;
1231 return NULL;
1218 mod = PyModule_Create(&parsers_module);
1232 mod = PyModule_Create(&parsers_module);
1219 module_init(mod);
1233 module_init(mod);
1220 return mod;
1234 return mod;
1221 }
1235 }
1222 #else
1236 #else
1223 PyMODINIT_FUNC initparsers(void)
1237 PyMODINIT_FUNC initparsers(void)
1224 {
1238 {
1225 PyObject *mod;
1239 PyObject *mod;
1226
1240
1227 if (check_python_version() == -1) {
1241 if (check_python_version() == -1) {
1228 return;
1242 return;
1229 }
1243 }
1230 mod = Py_InitModule3("parsers", methods, parsers_doc);
1244 mod = Py_InitModule3("parsers", methods, parsers_doc);
1231 module_init(mod);
1245 module_init(mod);
1232 }
1246 }
1233 #endif
1247 #endif
@@ -1,819 +1,824
1 # parsers.py - Python implementation of parsers.c
1 # parsers.py - Python implementation of parsers.c
2 #
2 #
3 # Copyright 2009 Olivia Mackall <olivia@selenic.com> and others
3 # Copyright 2009 Olivia Mackall <olivia@selenic.com> and others
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 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import struct
10 import struct
11 import zlib
11 import zlib
12
12
13 from ..node import (
13 from ..node import (
14 nullrev,
14 nullrev,
15 sha1nodeconstants,
15 sha1nodeconstants,
16 )
16 )
17 from ..thirdparty import attr
17 from ..thirdparty import attr
18 from .. import (
18 from .. import (
19 error,
19 error,
20 pycompat,
20 pycompat,
21 revlogutils,
21 revlogutils,
22 util,
22 util,
23 )
23 )
24
24
25 from ..revlogutils import nodemap as nodemaputil
25 from ..revlogutils import nodemap as nodemaputil
26 from ..revlogutils import constants as revlog_constants
26 from ..revlogutils import constants as revlog_constants
27
27
28 stringio = pycompat.bytesio
28 stringio = pycompat.bytesio
29
29
30
30
31 _pack = struct.pack
31 _pack = struct.pack
32 _unpack = struct.unpack
32 _unpack = struct.unpack
33 _compress = zlib.compress
33 _compress = zlib.compress
34 _decompress = zlib.decompress
34 _decompress = zlib.decompress
35
35
36
36
37 # a special value used internally for `size` if the file come from the other parent
37 # a special value used internally for `size` if the file come from the other parent
38 FROM_P2 = -2
38 FROM_P2 = -2
39
39
40 # a special value used internally for `size` if the file is modified/merged/added
40 # a special value used internally for `size` if the file is modified/merged/added
41 NONNORMAL = -1
41 NONNORMAL = -1
42
42
43 # a special value used internally for `time` if the time is ambigeous
43 # a special value used internally for `time` if the time is ambigeous
44 AMBIGUOUS_TIME = -1
44 AMBIGUOUS_TIME = -1
45
45
46
46
47 @attr.s(slots=True, init=False)
47 @attr.s(slots=True, init=False)
48 class DirstateItem(object):
48 class DirstateItem(object):
49 """represent a dirstate entry
49 """represent a dirstate entry
50
50
51 It contains:
51 It contains:
52
52
53 - state (one of 'n', 'a', 'r', 'm')
53 - state (one of 'n', 'a', 'r', 'm')
54 - mode,
54 - mode,
55 - size,
55 - size,
56 - mtime,
56 - mtime,
57 """
57 """
58
58
59 _wc_tracked = attr.ib()
59 _wc_tracked = attr.ib()
60 _p1_tracked = attr.ib()
60 _p1_tracked = attr.ib()
61 _p2_tracked = attr.ib()
61 _p2_tracked = attr.ib()
62 # the three item above should probably be combined
62 # the three item above should probably be combined
63 #
63 #
64 # However it is unclear if they properly cover some of the most advanced
64 # However it is unclear if they properly cover some of the most advanced
65 # merge case. So we should probably wait on this to be settled.
65 # merge case. So we should probably wait on this to be settled.
66 _merged = attr.ib()
66 _merged = attr.ib()
67 _clean_p1 = attr.ib()
67 _clean_p1 = attr.ib()
68 _clean_p2 = attr.ib()
68 _clean_p2 = attr.ib()
69 _possibly_dirty = attr.ib()
69 _possibly_dirty = attr.ib()
70 _mode = attr.ib()
70 _mode = attr.ib()
71 _size = attr.ib()
71 _size = attr.ib()
72 _mtime = attr.ib()
72 _mtime = attr.ib()
73
73
74 def __init__(
74 def __init__(
75 self,
75 self,
76 wc_tracked=False,
76 wc_tracked=False,
77 p1_tracked=False,
77 p1_tracked=False,
78 p2_tracked=False,
78 p2_tracked=False,
79 merged=False,
79 merged=False,
80 clean_p1=False,
80 clean_p1=False,
81 clean_p2=False,
81 clean_p2=False,
82 possibly_dirty=False,
82 possibly_dirty=False,
83 parentfiledata=None,
83 parentfiledata=None,
84 ):
84 ):
85 if merged and (clean_p1 or clean_p2):
85 if merged and (clean_p1 or clean_p2):
86 msg = b'`merged` argument incompatible with `clean_p1`/`clean_p2`'
86 msg = b'`merged` argument incompatible with `clean_p1`/`clean_p2`'
87 raise error.ProgrammingError(msg)
87 raise error.ProgrammingError(msg)
88
88
89 self._wc_tracked = wc_tracked
89 self._wc_tracked = wc_tracked
90 self._p1_tracked = p1_tracked
90 self._p1_tracked = p1_tracked
91 self._p2_tracked = p2_tracked
91 self._p2_tracked = p2_tracked
92 self._merged = merged
92 self._merged = merged
93 self._clean_p1 = clean_p1
93 self._clean_p1 = clean_p1
94 self._clean_p2 = clean_p2
94 self._clean_p2 = clean_p2
95 self._possibly_dirty = possibly_dirty
95 self._possibly_dirty = possibly_dirty
96 if parentfiledata is None:
96 if parentfiledata is None:
97 self._mode = None
97 self._mode = None
98 self._size = None
98 self._size = None
99 self._mtime = None
99 self._mtime = None
100 else:
100 else:
101 self._mode = parentfiledata[0]
101 self._mode = parentfiledata[0]
102 self._size = parentfiledata[1]
102 self._size = parentfiledata[1]
103 self._mtime = parentfiledata[2]
103 self._mtime = parentfiledata[2]
104
104
105 @classmethod
105 @classmethod
106 def new_added(cls):
106 def new_added(cls):
107 """constructor to help legacy API to build a new "added" item
107 """constructor to help legacy API to build a new "added" item
108
108
109 Should eventually be removed
109 Should eventually be removed
110 """
110 """
111 instance = cls()
111 instance = cls()
112 instance._wc_tracked = True
112 instance._wc_tracked = True
113 instance._p1_tracked = False
113 instance._p1_tracked = False
114 instance._p2_tracked = False
114 instance._p2_tracked = False
115 return instance
115 return instance
116
116
117 @classmethod
117 @classmethod
118 def new_merged(cls):
118 def new_merged(cls):
119 """constructor to help legacy API to build a new "merged" item
119 """constructor to help legacy API to build a new "merged" item
120
120
121 Should eventually be removed
121 Should eventually be removed
122 """
122 """
123 instance = cls()
123 instance = cls()
124 instance._wc_tracked = True
124 instance._wc_tracked = True
125 instance._p1_tracked = True # might not be True because of rename ?
125 instance._p1_tracked = True # might not be True because of rename ?
126 instance._p2_tracked = True # might not be True because of rename ?
126 instance._p2_tracked = True # might not be True because of rename ?
127 instance._merged = True
127 instance._merged = True
128 return instance
128 return instance
129
129
130 @classmethod
130 @classmethod
131 def new_from_p2(cls):
131 def new_from_p2(cls):
132 """constructor to help legacy API to build a new "from_p2" item
132 """constructor to help legacy API to build a new "from_p2" item
133
133
134 Should eventually be removed
134 Should eventually be removed
135 """
135 """
136 instance = cls()
136 instance = cls()
137 instance._wc_tracked = True
137 instance._wc_tracked = True
138 instance._p1_tracked = False # might actually be True
138 instance._p1_tracked = False # might actually be True
139 instance._p2_tracked = True
139 instance._p2_tracked = True
140 instance._clean_p2 = True
140 instance._clean_p2 = True
141 return instance
141 return instance
142
142
143 @classmethod
143 @classmethod
144 def new_possibly_dirty(cls):
144 def new_possibly_dirty(cls):
145 """constructor to help legacy API to build a new "possibly_dirty" item
145 """constructor to help legacy API to build a new "possibly_dirty" item
146
146
147 Should eventually be removed
147 Should eventually be removed
148 """
148 """
149 instance = cls()
149 instance = cls()
150 instance._wc_tracked = True
150 instance._wc_tracked = True
151 instance._p1_tracked = True
151 instance._p1_tracked = True
152 instance._possibly_dirty = True
152 instance._possibly_dirty = True
153 return instance
153 return instance
154
154
155 @classmethod
155 @classmethod
156 def new_normal(cls, mode, size, mtime):
156 def new_normal(cls, mode, size, mtime):
157 """constructor to help legacy API to build a new "normal" item
157 """constructor to help legacy API to build a new "normal" item
158
158
159 Should eventually be removed
159 Should eventually be removed
160 """
160 """
161 assert size != FROM_P2
161 assert size != FROM_P2
162 assert size != NONNORMAL
162 assert size != NONNORMAL
163 instance = cls()
163 instance = cls()
164 instance._wc_tracked = True
164 instance._wc_tracked = True
165 instance._p1_tracked = True
165 instance._p1_tracked = True
166 instance._mode = mode
166 instance._mode = mode
167 instance._size = size
167 instance._size = size
168 instance._mtime = mtime
168 instance._mtime = mtime
169 return instance
169 return instance
170
170
171 @classmethod
171 @classmethod
172 def from_v1_data(cls, state, mode, size, mtime):
172 def from_v1_data(cls, state, mode, size, mtime):
173 """Build a new DirstateItem object from V1 data
173 """Build a new DirstateItem object from V1 data
174
174
175 Since the dirstate-v1 format is frozen, the signature of this function
175 Since the dirstate-v1 format is frozen, the signature of this function
176 is not expected to change, unlike the __init__ one.
176 is not expected to change, unlike the __init__ one.
177 """
177 """
178 if state == b'm':
178 if state == b'm':
179 return cls.new_merged()
179 return cls.new_merged()
180 elif state == b'a':
180 elif state == b'a':
181 return cls.new_added()
181 return cls.new_added()
182 elif state == b'r':
182 elif state == b'r':
183 instance = cls()
183 instance = cls()
184 instance._wc_tracked = False
184 instance._wc_tracked = False
185 if size == NONNORMAL:
185 if size == NONNORMAL:
186 instance._merged = True
186 instance._merged = True
187 instance._p1_tracked = (
187 instance._p1_tracked = (
188 True # might not be True because of rename ?
188 True # might not be True because of rename ?
189 )
189 )
190 instance._p2_tracked = (
190 instance._p2_tracked = (
191 True # might not be True because of rename ?
191 True # might not be True because of rename ?
192 )
192 )
193 elif size == FROM_P2:
193 elif size == FROM_P2:
194 instance._clean_p2 = True
194 instance._clean_p2 = True
195 instance._p1_tracked = (
195 instance._p1_tracked = (
196 False # We actually don't know (file history)
196 False # We actually don't know (file history)
197 )
197 )
198 instance._p2_tracked = True
198 instance._p2_tracked = True
199 else:
199 else:
200 instance._p1_tracked = True
200 instance._p1_tracked = True
201 return instance
201 return instance
202 elif state == b'n':
202 elif state == b'n':
203 if size == FROM_P2:
203 if size == FROM_P2:
204 return cls.new_from_p2()
204 return cls.new_from_p2()
205 elif size == NONNORMAL:
205 elif size == NONNORMAL:
206 return cls.new_possibly_dirty()
206 return cls.new_possibly_dirty()
207 elif mtime == AMBIGUOUS_TIME:
207 elif mtime == AMBIGUOUS_TIME:
208 instance = cls.new_normal(mode, size, 42)
208 instance = cls.new_normal(mode, size, 42)
209 instance._mtime = None
209 instance._mtime = None
210 instance._possibly_dirty = True
210 instance._possibly_dirty = True
211 return instance
211 return instance
212 else:
212 else:
213 return cls.new_normal(mode, size, mtime)
213 return cls.new_normal(mode, size, mtime)
214 else:
214 else:
215 raise RuntimeError(b'unknown state: %s' % state)
215 raise RuntimeError(b'unknown state: %s' % state)
216
216
217 def set_possibly_dirty(self):
217 def set_possibly_dirty(self):
218 """Mark a file as "possibly dirty"
218 """Mark a file as "possibly dirty"
219
219
220 This means the next status call will have to actually check its content
220 This means the next status call will have to actually check its content
221 to make sure it is correct.
221 to make sure it is correct.
222 """
222 """
223 self._possibly_dirty = True
223 self._possibly_dirty = True
224
224
225 def set_clean(self, mode, size, mtime):
225 def set_clean(self, mode, size, mtime):
226 """mark a file as "clean" cancelling potential "possibly dirty call"
226 """mark a file as "clean" cancelling potential "possibly dirty call"
227
227
228 Note: this function is a descendant of `dirstate.normal` and is
228 Note: this function is a descendant of `dirstate.normal` and is
229 currently expected to be call on "normal" entry only. There are not
229 currently expected to be call on "normal" entry only. There are not
230 reason for this to not change in the future as long as the ccode is
230 reason for this to not change in the future as long as the ccode is
231 updated to preserve the proper state of the non-normal files.
231 updated to preserve the proper state of the non-normal files.
232 """
232 """
233 self._wc_tracked = True
233 self._wc_tracked = True
234 self._p1_tracked = True
234 self._p1_tracked = True
235 self._p2_tracked = False # this might be wrong
235 self._p2_tracked = False # this might be wrong
236 self._merged = False
236 self._merged = False
237 self._clean_p2 = False
237 self._clean_p2 = False
238 self._possibly_dirty = False
238 self._possibly_dirty = False
239 self._mode = mode
239 self._mode = mode
240 self._size = size
240 self._size = size
241 self._mtime = mtime
241 self._mtime = mtime
242
242
243 def set_tracked(self):
243 def set_tracked(self):
244 """mark a file as tracked in the working copy
244 """mark a file as tracked in the working copy
245
245
246 This will ultimately be called by command like `hg add`.
246 This will ultimately be called by command like `hg add`.
247 """
247 """
248 self._wc_tracked = True
248 self._wc_tracked = True
249 # `set_tracked` is replacing various `normallookup` call. So we set
249 # `set_tracked` is replacing various `normallookup` call. So we set
250 # "possibly dirty" to stay on the safe side.
250 # "possibly dirty" to stay on the safe side.
251 #
251 #
252 # Consider dropping this in the future in favor of something less broad.
252 # Consider dropping this in the future in favor of something less broad.
253 self._possibly_dirty = True
253 self._possibly_dirty = True
254
254
255 def set_untracked(self):
255 def set_untracked(self):
256 """mark a file as untracked in the working copy
256 """mark a file as untracked in the working copy
257
257
258 This will ultimately be called by command like `hg remove`.
258 This will ultimately be called by command like `hg remove`.
259 """
259 """
260 # backup the previous state (useful for merge)
260 # backup the previous state (useful for merge)
261 self._wc_tracked = False
261 self._wc_tracked = False
262 self._mode = None
262 self._mode = None
263 self._size = None
263 self._size = None
264 self._mtime = None
264 self._mtime = None
265
265
266 def drop_merge_data(self):
266 def drop_merge_data(self):
267 """remove all "merge-only" from a DirstateItem
267 """remove all "merge-only" from a DirstateItem
268
268
269 This is to be call by the dirstatemap code when the second parent is dropped
269 This is to be call by the dirstatemap code when the second parent is dropped
270 """
270 """
271 if not (self.merged or self.from_p2):
271 if not (self.merged or self.from_p2):
272 return
272 return
273 self._p1_tracked = self.merged # why is this not already properly set ?
273 self._p1_tracked = self.merged # why is this not already properly set ?
274
274
275 self._merged = False
275 self._merged = False
276 self._clean_p1 = False
276 self._clean_p1 = False
277 self._clean_p2 = False
277 self._clean_p2 = False
278 self._p2_tracked = False
278 self._p2_tracked = False
279 self._possibly_dirty = True
279 self._possibly_dirty = True
280 self._mode = None
280 self._mode = None
281 self._size = None
281 self._size = None
282 self._mtime = None
282 self._mtime = None
283
283
284 @property
284 @property
285 def mode(self):
285 def mode(self):
286 return self.v1_mode()
286 return self.v1_mode()
287
287
288 @property
288 @property
289 def size(self):
289 def size(self):
290 return self.v1_size()
290 return self.v1_size()
291
291
292 @property
292 @property
293 def mtime(self):
293 def mtime(self):
294 return self.v1_mtime()
294 return self.v1_mtime()
295
295
296 @property
296 @property
297 def state(self):
297 def state(self):
298 """
298 """
299 States are:
299 States are:
300 n normal
300 n normal
301 m needs merging
301 m needs merging
302 r marked for removal
302 r marked for removal
303 a marked for addition
303 a marked for addition
304
304
305 XXX This "state" is a bit obscure and mostly a direct expression of the
305 XXX This "state" is a bit obscure and mostly a direct expression of the
306 dirstatev1 format. It would make sense to ultimately deprecate it in
306 dirstatev1 format. It would make sense to ultimately deprecate it in
307 favor of the more "semantic" attributes.
307 favor of the more "semantic" attributes.
308 """
308 """
309 return self.v1_state()
309 return self.v1_state()
310
310
311 @property
311 @property
312 def tracked(self):
312 def tracked(self):
313 """True is the file is tracked in the working copy"""
313 """True is the file is tracked in the working copy"""
314 return self._wc_tracked
314 return self._wc_tracked
315
315
316 @property
316 @property
317 def any_tracked(self):
318 """True is the file is tracked anywhere (wc or parents)"""
319 return self._wc_tracked or self._p1_tracked or self._p2_tracked
320
321 @property
317 def added(self):
322 def added(self):
318 """True if the file has been added"""
323 """True if the file has been added"""
319 return self._wc_tracked and not (self._p1_tracked or self._p2_tracked)
324 return self._wc_tracked and not (self._p1_tracked or self._p2_tracked)
320
325
321 @property
326 @property
322 def maybe_clean(self):
327 def maybe_clean(self):
323 """True if the file has a chance to be in the "clean" state"""
328 """True if the file has a chance to be in the "clean" state"""
324 if not self._wc_tracked:
329 if not self._wc_tracked:
325 return False
330 return False
326 elif self.added:
331 elif self.added:
327 return False
332 return False
328 elif self._merged:
333 elif self._merged:
329 return False
334 return False
330 elif self._clean_p2:
335 elif self._clean_p2:
331 return False
336 return False
332 return True
337 return True
333
338
334 @property
339 @property
335 def merged(self):
340 def merged(self):
336 """True if the file has been merged
341 """True if the file has been merged
337
342
338 Should only be set if a merge is in progress in the dirstate
343 Should only be set if a merge is in progress in the dirstate
339 """
344 """
340 return self._wc_tracked and self._merged
345 return self._wc_tracked and self._merged
341
346
342 @property
347 @property
343 def from_p2(self):
348 def from_p2(self):
344 """True if the file have been fetched from p2 during the current merge
349 """True if the file have been fetched from p2 during the current merge
345
350
346 This is only True is the file is currently tracked.
351 This is only True is the file is currently tracked.
347
352
348 Should only be set if a merge is in progress in the dirstate
353 Should only be set if a merge is in progress in the dirstate
349 """
354 """
350 if not self._wc_tracked:
355 if not self._wc_tracked:
351 return False
356 return False
352 return self._clean_p2
357 return self._clean_p2
353
358
354 @property
359 @property
355 def removed(self):
360 def removed(self):
356 """True if the file has been removed"""
361 """True if the file has been removed"""
357 return not self._wc_tracked and (self._p1_tracked or self._p2_tracked)
362 return not self._wc_tracked and (self._p1_tracked or self._p2_tracked)
358
363
359 def v1_state(self):
364 def v1_state(self):
360 """return a "state" suitable for v1 serialization"""
365 """return a "state" suitable for v1 serialization"""
361 if not (self._p1_tracked or self._p2_tracked or self._wc_tracked):
366 if not (self._p1_tracked or self._p2_tracked or self._wc_tracked):
362 # the object has no state to record, this is -currently-
367 # the object has no state to record, this is -currently-
363 # unsupported
368 # unsupported
364 raise RuntimeError('untracked item')
369 raise RuntimeError('untracked item')
365 elif self.removed:
370 elif self.removed:
366 return b'r'
371 return b'r'
367 elif self.merged:
372 elif self.merged:
368 return b'm'
373 return b'm'
369 elif self.added:
374 elif self.added:
370 return b'a'
375 return b'a'
371 else:
376 else:
372 return b'n'
377 return b'n'
373
378
374 def v1_mode(self):
379 def v1_mode(self):
375 """return a "mode" suitable for v1 serialization"""
380 """return a "mode" suitable for v1 serialization"""
376 return self._mode if self._mode is not None else 0
381 return self._mode if self._mode is not None else 0
377
382
378 def v1_size(self):
383 def v1_size(self):
379 """return a "size" suitable for v1 serialization"""
384 """return a "size" suitable for v1 serialization"""
380 if not (self._p1_tracked or self._p2_tracked or self._wc_tracked):
385 if not (self._p1_tracked or self._p2_tracked or self._wc_tracked):
381 # the object has no state to record, this is -currently-
386 # the object has no state to record, this is -currently-
382 # unsupported
387 # unsupported
383 raise RuntimeError('untracked item')
388 raise RuntimeError('untracked item')
384 elif self.removed and self._merged:
389 elif self.removed and self._merged:
385 return NONNORMAL
390 return NONNORMAL
386 elif self.removed and self._clean_p2:
391 elif self.removed and self._clean_p2:
387 return FROM_P2
392 return FROM_P2
388 elif self.removed:
393 elif self.removed:
389 return 0
394 return 0
390 elif self.merged:
395 elif self.merged:
391 return FROM_P2
396 return FROM_P2
392 elif self.added:
397 elif self.added:
393 return NONNORMAL
398 return NONNORMAL
394 elif self.from_p2:
399 elif self.from_p2:
395 return FROM_P2
400 return FROM_P2
396 elif self._possibly_dirty:
401 elif self._possibly_dirty:
397 return self._size if self._size is not None else NONNORMAL
402 return self._size if self._size is not None else NONNORMAL
398 else:
403 else:
399 return self._size
404 return self._size
400
405
401 def v1_mtime(self):
406 def v1_mtime(self):
402 """return a "mtime" suitable for v1 serialization"""
407 """return a "mtime" suitable for v1 serialization"""
403 if not (self._p1_tracked or self._p2_tracked or self._wc_tracked):
408 if not (self._p1_tracked or self._p2_tracked or self._wc_tracked):
404 # the object has no state to record, this is -currently-
409 # the object has no state to record, this is -currently-
405 # unsupported
410 # unsupported
406 raise RuntimeError('untracked item')
411 raise RuntimeError('untracked item')
407 elif self.removed:
412 elif self.removed:
408 return 0
413 return 0
409 elif self._possibly_dirty:
414 elif self._possibly_dirty:
410 return AMBIGUOUS_TIME
415 return AMBIGUOUS_TIME
411 elif self.merged:
416 elif self.merged:
412 return AMBIGUOUS_TIME
417 return AMBIGUOUS_TIME
413 elif self.added:
418 elif self.added:
414 return AMBIGUOUS_TIME
419 return AMBIGUOUS_TIME
415 elif self.from_p2:
420 elif self.from_p2:
416 return AMBIGUOUS_TIME
421 return AMBIGUOUS_TIME
417 else:
422 else:
418 return self._mtime if self._mtime is not None else 0
423 return self._mtime if self._mtime is not None else 0
419
424
420 def need_delay(self, now):
425 def need_delay(self, now):
421 """True if the stored mtime would be ambiguous with the current time"""
426 """True if the stored mtime would be ambiguous with the current time"""
422 return self.v1_state() == b'n' and self.v1_mtime() == now
427 return self.v1_state() == b'n' and self.v1_mtime() == now
423
428
424
429
425 def gettype(q):
430 def gettype(q):
426 return int(q & 0xFFFF)
431 return int(q & 0xFFFF)
427
432
428
433
429 class BaseIndexObject(object):
434 class BaseIndexObject(object):
430 # Can I be passed to an algorithme implemented in Rust ?
435 # Can I be passed to an algorithme implemented in Rust ?
431 rust_ext_compat = 0
436 rust_ext_compat = 0
432 # Format of an index entry according to Python's `struct` language
437 # Format of an index entry according to Python's `struct` language
433 index_format = revlog_constants.INDEX_ENTRY_V1
438 index_format = revlog_constants.INDEX_ENTRY_V1
434 # Size of a C unsigned long long int, platform independent
439 # Size of a C unsigned long long int, platform independent
435 big_int_size = struct.calcsize(b'>Q')
440 big_int_size = struct.calcsize(b'>Q')
436 # Size of a C long int, platform independent
441 # Size of a C long int, platform independent
437 int_size = struct.calcsize(b'>i')
442 int_size = struct.calcsize(b'>i')
438 # An empty index entry, used as a default value to be overridden, or nullrev
443 # An empty index entry, used as a default value to be overridden, or nullrev
439 null_item = (
444 null_item = (
440 0,
445 0,
441 0,
446 0,
442 0,
447 0,
443 -1,
448 -1,
444 -1,
449 -1,
445 -1,
450 -1,
446 -1,
451 -1,
447 sha1nodeconstants.nullid,
452 sha1nodeconstants.nullid,
448 0,
453 0,
449 0,
454 0,
450 revlog_constants.COMP_MODE_INLINE,
455 revlog_constants.COMP_MODE_INLINE,
451 revlog_constants.COMP_MODE_INLINE,
456 revlog_constants.COMP_MODE_INLINE,
452 )
457 )
453
458
454 @util.propertycache
459 @util.propertycache
455 def entry_size(self):
460 def entry_size(self):
456 return self.index_format.size
461 return self.index_format.size
457
462
458 @property
463 @property
459 def nodemap(self):
464 def nodemap(self):
460 msg = b"index.nodemap is deprecated, use index.[has_node|rev|get_rev]"
465 msg = b"index.nodemap is deprecated, use index.[has_node|rev|get_rev]"
461 util.nouideprecwarn(msg, b'5.3', stacklevel=2)
466 util.nouideprecwarn(msg, b'5.3', stacklevel=2)
462 return self._nodemap
467 return self._nodemap
463
468
464 @util.propertycache
469 @util.propertycache
465 def _nodemap(self):
470 def _nodemap(self):
466 nodemap = nodemaputil.NodeMap({sha1nodeconstants.nullid: nullrev})
471 nodemap = nodemaputil.NodeMap({sha1nodeconstants.nullid: nullrev})
467 for r in range(0, len(self)):
472 for r in range(0, len(self)):
468 n = self[r][7]
473 n = self[r][7]
469 nodemap[n] = r
474 nodemap[n] = r
470 return nodemap
475 return nodemap
471
476
472 def has_node(self, node):
477 def has_node(self, node):
473 """return True if the node exist in the index"""
478 """return True if the node exist in the index"""
474 return node in self._nodemap
479 return node in self._nodemap
475
480
476 def rev(self, node):
481 def rev(self, node):
477 """return a revision for a node
482 """return a revision for a node
478
483
479 If the node is unknown, raise a RevlogError"""
484 If the node is unknown, raise a RevlogError"""
480 return self._nodemap[node]
485 return self._nodemap[node]
481
486
482 def get_rev(self, node):
487 def get_rev(self, node):
483 """return a revision for a node
488 """return a revision for a node
484
489
485 If the node is unknown, return None"""
490 If the node is unknown, return None"""
486 return self._nodemap.get(node)
491 return self._nodemap.get(node)
487
492
488 def _stripnodes(self, start):
493 def _stripnodes(self, start):
489 if '_nodemap' in vars(self):
494 if '_nodemap' in vars(self):
490 for r in range(start, len(self)):
495 for r in range(start, len(self)):
491 n = self[r][7]
496 n = self[r][7]
492 del self._nodemap[n]
497 del self._nodemap[n]
493
498
494 def clearcaches(self):
499 def clearcaches(self):
495 self.__dict__.pop('_nodemap', None)
500 self.__dict__.pop('_nodemap', None)
496
501
497 def __len__(self):
502 def __len__(self):
498 return self._lgt + len(self._extra)
503 return self._lgt + len(self._extra)
499
504
500 def append(self, tup):
505 def append(self, tup):
501 if '_nodemap' in vars(self):
506 if '_nodemap' in vars(self):
502 self._nodemap[tup[7]] = len(self)
507 self._nodemap[tup[7]] = len(self)
503 data = self._pack_entry(len(self), tup)
508 data = self._pack_entry(len(self), tup)
504 self._extra.append(data)
509 self._extra.append(data)
505
510
506 def _pack_entry(self, rev, entry):
511 def _pack_entry(self, rev, entry):
507 assert entry[8] == 0
512 assert entry[8] == 0
508 assert entry[9] == 0
513 assert entry[9] == 0
509 return self.index_format.pack(*entry[:8])
514 return self.index_format.pack(*entry[:8])
510
515
511 def _check_index(self, i):
516 def _check_index(self, i):
512 if not isinstance(i, int):
517 if not isinstance(i, int):
513 raise TypeError(b"expecting int indexes")
518 raise TypeError(b"expecting int indexes")
514 if i < 0 or i >= len(self):
519 if i < 0 or i >= len(self):
515 raise IndexError
520 raise IndexError
516
521
517 def __getitem__(self, i):
522 def __getitem__(self, i):
518 if i == -1:
523 if i == -1:
519 return self.null_item
524 return self.null_item
520 self._check_index(i)
525 self._check_index(i)
521 if i >= self._lgt:
526 if i >= self._lgt:
522 data = self._extra[i - self._lgt]
527 data = self._extra[i - self._lgt]
523 else:
528 else:
524 index = self._calculate_index(i)
529 index = self._calculate_index(i)
525 data = self._data[index : index + self.entry_size]
530 data = self._data[index : index + self.entry_size]
526 r = self._unpack_entry(i, data)
531 r = self._unpack_entry(i, data)
527 if self._lgt and i == 0:
532 if self._lgt and i == 0:
528 offset = revlogutils.offset_type(0, gettype(r[0]))
533 offset = revlogutils.offset_type(0, gettype(r[0]))
529 r = (offset,) + r[1:]
534 r = (offset,) + r[1:]
530 return r
535 return r
531
536
532 def _unpack_entry(self, rev, data):
537 def _unpack_entry(self, rev, data):
533 r = self.index_format.unpack(data)
538 r = self.index_format.unpack(data)
534 r = r + (
539 r = r + (
535 0,
540 0,
536 0,
541 0,
537 revlog_constants.COMP_MODE_INLINE,
542 revlog_constants.COMP_MODE_INLINE,
538 revlog_constants.COMP_MODE_INLINE,
543 revlog_constants.COMP_MODE_INLINE,
539 )
544 )
540 return r
545 return r
541
546
542 def pack_header(self, header):
547 def pack_header(self, header):
543 """pack header information as binary"""
548 """pack header information as binary"""
544 v_fmt = revlog_constants.INDEX_HEADER
549 v_fmt = revlog_constants.INDEX_HEADER
545 return v_fmt.pack(header)
550 return v_fmt.pack(header)
546
551
547 def entry_binary(self, rev):
552 def entry_binary(self, rev):
548 """return the raw binary string representing a revision"""
553 """return the raw binary string representing a revision"""
549 entry = self[rev]
554 entry = self[rev]
550 p = revlog_constants.INDEX_ENTRY_V1.pack(*entry[:8])
555 p = revlog_constants.INDEX_ENTRY_V1.pack(*entry[:8])
551 if rev == 0:
556 if rev == 0:
552 p = p[revlog_constants.INDEX_HEADER.size :]
557 p = p[revlog_constants.INDEX_HEADER.size :]
553 return p
558 return p
554
559
555
560
556 class IndexObject(BaseIndexObject):
561 class IndexObject(BaseIndexObject):
557 def __init__(self, data):
562 def __init__(self, data):
558 assert len(data) % self.entry_size == 0, (
563 assert len(data) % self.entry_size == 0, (
559 len(data),
564 len(data),
560 self.entry_size,
565 self.entry_size,
561 len(data) % self.entry_size,
566 len(data) % self.entry_size,
562 )
567 )
563 self._data = data
568 self._data = data
564 self._lgt = len(data) // self.entry_size
569 self._lgt = len(data) // self.entry_size
565 self._extra = []
570 self._extra = []
566
571
567 def _calculate_index(self, i):
572 def _calculate_index(self, i):
568 return i * self.entry_size
573 return i * self.entry_size
569
574
570 def __delitem__(self, i):
575 def __delitem__(self, i):
571 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
576 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
572 raise ValueError(b"deleting slices only supports a:-1 with step 1")
577 raise ValueError(b"deleting slices only supports a:-1 with step 1")
573 i = i.start
578 i = i.start
574 self._check_index(i)
579 self._check_index(i)
575 self._stripnodes(i)
580 self._stripnodes(i)
576 if i < self._lgt:
581 if i < self._lgt:
577 self._data = self._data[: i * self.entry_size]
582 self._data = self._data[: i * self.entry_size]
578 self._lgt = i
583 self._lgt = i
579 self._extra = []
584 self._extra = []
580 else:
585 else:
581 self._extra = self._extra[: i - self._lgt]
586 self._extra = self._extra[: i - self._lgt]
582
587
583
588
584 class PersistentNodeMapIndexObject(IndexObject):
589 class PersistentNodeMapIndexObject(IndexObject):
585 """a Debug oriented class to test persistent nodemap
590 """a Debug oriented class to test persistent nodemap
586
591
587 We need a simple python object to test API and higher level behavior. See
592 We need a simple python object to test API and higher level behavior. See
588 the Rust implementation for more serious usage. This should be used only
593 the Rust implementation for more serious usage. This should be used only
589 through the dedicated `devel.persistent-nodemap` config.
594 through the dedicated `devel.persistent-nodemap` config.
590 """
595 """
591
596
592 def nodemap_data_all(self):
597 def nodemap_data_all(self):
593 """Return bytes containing a full serialization of a nodemap
598 """Return bytes containing a full serialization of a nodemap
594
599
595 The nodemap should be valid for the full set of revisions in the
600 The nodemap should be valid for the full set of revisions in the
596 index."""
601 index."""
597 return nodemaputil.persistent_data(self)
602 return nodemaputil.persistent_data(self)
598
603
599 def nodemap_data_incremental(self):
604 def nodemap_data_incremental(self):
600 """Return bytes containing a incremental update to persistent nodemap
605 """Return bytes containing a incremental update to persistent nodemap
601
606
602 This containst the data for an append-only update of the data provided
607 This containst the data for an append-only update of the data provided
603 in the last call to `update_nodemap_data`.
608 in the last call to `update_nodemap_data`.
604 """
609 """
605 if self._nm_root is None:
610 if self._nm_root is None:
606 return None
611 return None
607 docket = self._nm_docket
612 docket = self._nm_docket
608 changed, data = nodemaputil.update_persistent_data(
613 changed, data = nodemaputil.update_persistent_data(
609 self, self._nm_root, self._nm_max_idx, self._nm_docket.tip_rev
614 self, self._nm_root, self._nm_max_idx, self._nm_docket.tip_rev
610 )
615 )
611
616
612 self._nm_root = self._nm_max_idx = self._nm_docket = None
617 self._nm_root = self._nm_max_idx = self._nm_docket = None
613 return docket, changed, data
618 return docket, changed, data
614
619
615 def update_nodemap_data(self, docket, nm_data):
620 def update_nodemap_data(self, docket, nm_data):
616 """provide full block of persisted binary data for a nodemap
621 """provide full block of persisted binary data for a nodemap
617
622
618 The data are expected to come from disk. See `nodemap_data_all` for a
623 The data are expected to come from disk. See `nodemap_data_all` for a
619 produceur of such data."""
624 produceur of such data."""
620 if nm_data is not None:
625 if nm_data is not None:
621 self._nm_root, self._nm_max_idx = nodemaputil.parse_data(nm_data)
626 self._nm_root, self._nm_max_idx = nodemaputil.parse_data(nm_data)
622 if self._nm_root:
627 if self._nm_root:
623 self._nm_docket = docket
628 self._nm_docket = docket
624 else:
629 else:
625 self._nm_root = self._nm_max_idx = self._nm_docket = None
630 self._nm_root = self._nm_max_idx = self._nm_docket = None
626
631
627
632
628 class InlinedIndexObject(BaseIndexObject):
633 class InlinedIndexObject(BaseIndexObject):
629 def __init__(self, data, inline=0):
634 def __init__(self, data, inline=0):
630 self._data = data
635 self._data = data
631 self._lgt = self._inline_scan(None)
636 self._lgt = self._inline_scan(None)
632 self._inline_scan(self._lgt)
637 self._inline_scan(self._lgt)
633 self._extra = []
638 self._extra = []
634
639
635 def _inline_scan(self, lgt):
640 def _inline_scan(self, lgt):
636 off = 0
641 off = 0
637 if lgt is not None:
642 if lgt is not None:
638 self._offsets = [0] * lgt
643 self._offsets = [0] * lgt
639 count = 0
644 count = 0
640 while off <= len(self._data) - self.entry_size:
645 while off <= len(self._data) - self.entry_size:
641 start = off + self.big_int_size
646 start = off + self.big_int_size
642 (s,) = struct.unpack(
647 (s,) = struct.unpack(
643 b'>i',
648 b'>i',
644 self._data[start : start + self.int_size],
649 self._data[start : start + self.int_size],
645 )
650 )
646 if lgt is not None:
651 if lgt is not None:
647 self._offsets[count] = off
652 self._offsets[count] = off
648 count += 1
653 count += 1
649 off += self.entry_size + s
654 off += self.entry_size + s
650 if off != len(self._data):
655 if off != len(self._data):
651 raise ValueError(b"corrupted data")
656 raise ValueError(b"corrupted data")
652 return count
657 return count
653
658
654 def __delitem__(self, i):
659 def __delitem__(self, i):
655 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
660 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
656 raise ValueError(b"deleting slices only supports a:-1 with step 1")
661 raise ValueError(b"deleting slices only supports a:-1 with step 1")
657 i = i.start
662 i = i.start
658 self._check_index(i)
663 self._check_index(i)
659 self._stripnodes(i)
664 self._stripnodes(i)
660 if i < self._lgt:
665 if i < self._lgt:
661 self._offsets = self._offsets[:i]
666 self._offsets = self._offsets[:i]
662 self._lgt = i
667 self._lgt = i
663 self._extra = []
668 self._extra = []
664 else:
669 else:
665 self._extra = self._extra[: i - self._lgt]
670 self._extra = self._extra[: i - self._lgt]
666
671
667 def _calculate_index(self, i):
672 def _calculate_index(self, i):
668 return self._offsets[i]
673 return self._offsets[i]
669
674
670
675
671 def parse_index2(data, inline, revlogv2=False):
676 def parse_index2(data, inline, revlogv2=False):
672 if not inline:
677 if not inline:
673 cls = IndexObject2 if revlogv2 else IndexObject
678 cls = IndexObject2 if revlogv2 else IndexObject
674 return cls(data), None
679 return cls(data), None
675 cls = InlinedIndexObject
680 cls = InlinedIndexObject
676 return cls(data, inline), (0, data)
681 return cls(data, inline), (0, data)
677
682
678
683
679 def parse_index_cl_v2(data):
684 def parse_index_cl_v2(data):
680 return IndexChangelogV2(data), None
685 return IndexChangelogV2(data), None
681
686
682
687
683 class IndexObject2(IndexObject):
688 class IndexObject2(IndexObject):
684 index_format = revlog_constants.INDEX_ENTRY_V2
689 index_format = revlog_constants.INDEX_ENTRY_V2
685
690
686 def replace_sidedata_info(
691 def replace_sidedata_info(
687 self,
692 self,
688 rev,
693 rev,
689 sidedata_offset,
694 sidedata_offset,
690 sidedata_length,
695 sidedata_length,
691 offset_flags,
696 offset_flags,
692 compression_mode,
697 compression_mode,
693 ):
698 ):
694 """
699 """
695 Replace an existing index entry's sidedata offset and length with new
700 Replace an existing index entry's sidedata offset and length with new
696 ones.
701 ones.
697 This cannot be used outside of the context of sidedata rewriting,
702 This cannot be used outside of the context of sidedata rewriting,
698 inside the transaction that creates the revision `rev`.
703 inside the transaction that creates the revision `rev`.
699 """
704 """
700 if rev < 0:
705 if rev < 0:
701 raise KeyError
706 raise KeyError
702 self._check_index(rev)
707 self._check_index(rev)
703 if rev < self._lgt:
708 if rev < self._lgt:
704 msg = b"cannot rewrite entries outside of this transaction"
709 msg = b"cannot rewrite entries outside of this transaction"
705 raise KeyError(msg)
710 raise KeyError(msg)
706 else:
711 else:
707 entry = list(self[rev])
712 entry = list(self[rev])
708 entry[0] = offset_flags
713 entry[0] = offset_flags
709 entry[8] = sidedata_offset
714 entry[8] = sidedata_offset
710 entry[9] = sidedata_length
715 entry[9] = sidedata_length
711 entry[11] = compression_mode
716 entry[11] = compression_mode
712 entry = tuple(entry)
717 entry = tuple(entry)
713 new = self._pack_entry(rev, entry)
718 new = self._pack_entry(rev, entry)
714 self._extra[rev - self._lgt] = new
719 self._extra[rev - self._lgt] = new
715
720
716 def _unpack_entry(self, rev, data):
721 def _unpack_entry(self, rev, data):
717 data = self.index_format.unpack(data)
722 data = self.index_format.unpack(data)
718 entry = data[:10]
723 entry = data[:10]
719 data_comp = data[10] & 3
724 data_comp = data[10] & 3
720 sidedata_comp = (data[10] & (3 << 2)) >> 2
725 sidedata_comp = (data[10] & (3 << 2)) >> 2
721 return entry + (data_comp, sidedata_comp)
726 return entry + (data_comp, sidedata_comp)
722
727
723 def _pack_entry(self, rev, entry):
728 def _pack_entry(self, rev, entry):
724 data = entry[:10]
729 data = entry[:10]
725 data_comp = entry[10] & 3
730 data_comp = entry[10] & 3
726 sidedata_comp = (entry[11] & 3) << 2
731 sidedata_comp = (entry[11] & 3) << 2
727 data += (data_comp | sidedata_comp,)
732 data += (data_comp | sidedata_comp,)
728
733
729 return self.index_format.pack(*data)
734 return self.index_format.pack(*data)
730
735
731 def entry_binary(self, rev):
736 def entry_binary(self, rev):
732 """return the raw binary string representing a revision"""
737 """return the raw binary string representing a revision"""
733 entry = self[rev]
738 entry = self[rev]
734 return self._pack_entry(rev, entry)
739 return self._pack_entry(rev, entry)
735
740
736 def pack_header(self, header):
741 def pack_header(self, header):
737 """pack header information as binary"""
742 """pack header information as binary"""
738 msg = 'version header should go in the docket, not the index: %d'
743 msg = 'version header should go in the docket, not the index: %d'
739 msg %= header
744 msg %= header
740 raise error.ProgrammingError(msg)
745 raise error.ProgrammingError(msg)
741
746
742
747
743 class IndexChangelogV2(IndexObject2):
748 class IndexChangelogV2(IndexObject2):
744 index_format = revlog_constants.INDEX_ENTRY_CL_V2
749 index_format = revlog_constants.INDEX_ENTRY_CL_V2
745
750
746 def _unpack_entry(self, rev, data, r=True):
751 def _unpack_entry(self, rev, data, r=True):
747 items = self.index_format.unpack(data)
752 items = self.index_format.unpack(data)
748 entry = items[:3] + (rev, rev) + items[3:8]
753 entry = items[:3] + (rev, rev) + items[3:8]
749 data_comp = items[8] & 3
754 data_comp = items[8] & 3
750 sidedata_comp = (items[8] >> 2) & 3
755 sidedata_comp = (items[8] >> 2) & 3
751 return entry + (data_comp, sidedata_comp)
756 return entry + (data_comp, sidedata_comp)
752
757
753 def _pack_entry(self, rev, entry):
758 def _pack_entry(self, rev, entry):
754 assert entry[3] == rev, entry[3]
759 assert entry[3] == rev, entry[3]
755 assert entry[4] == rev, entry[4]
760 assert entry[4] == rev, entry[4]
756 data = entry[:3] + entry[5:10]
761 data = entry[:3] + entry[5:10]
757 data_comp = entry[10] & 3
762 data_comp = entry[10] & 3
758 sidedata_comp = (entry[11] & 3) << 2
763 sidedata_comp = (entry[11] & 3) << 2
759 data += (data_comp | sidedata_comp,)
764 data += (data_comp | sidedata_comp,)
760 return self.index_format.pack(*data)
765 return self.index_format.pack(*data)
761
766
762
767
763 def parse_index_devel_nodemap(data, inline):
768 def parse_index_devel_nodemap(data, inline):
764 """like parse_index2, but alway return a PersistentNodeMapIndexObject"""
769 """like parse_index2, but alway return a PersistentNodeMapIndexObject"""
765 return PersistentNodeMapIndexObject(data), None
770 return PersistentNodeMapIndexObject(data), None
766
771
767
772
768 def parse_dirstate(dmap, copymap, st):
773 def parse_dirstate(dmap, copymap, st):
769 parents = [st[:20], st[20:40]]
774 parents = [st[:20], st[20:40]]
770 # dereference fields so they will be local in loop
775 # dereference fields so they will be local in loop
771 format = b">cllll"
776 format = b">cllll"
772 e_size = struct.calcsize(format)
777 e_size = struct.calcsize(format)
773 pos1 = 40
778 pos1 = 40
774 l = len(st)
779 l = len(st)
775
780
776 # the inner loop
781 # the inner loop
777 while pos1 < l:
782 while pos1 < l:
778 pos2 = pos1 + e_size
783 pos2 = pos1 + e_size
779 e = _unpack(b">cllll", st[pos1:pos2]) # a literal here is faster
784 e = _unpack(b">cllll", st[pos1:pos2]) # a literal here is faster
780 pos1 = pos2 + e[4]
785 pos1 = pos2 + e[4]
781 f = st[pos2:pos1]
786 f = st[pos2:pos1]
782 if b'\0' in f:
787 if b'\0' in f:
783 f, c = f.split(b'\0')
788 f, c = f.split(b'\0')
784 copymap[f] = c
789 copymap[f] = c
785 dmap[f] = DirstateItem.from_v1_data(*e[:4])
790 dmap[f] = DirstateItem.from_v1_data(*e[:4])
786 return parents
791 return parents
787
792
788
793
789 def pack_dirstate(dmap, copymap, pl, now):
794 def pack_dirstate(dmap, copymap, pl, now):
790 now = int(now)
795 now = int(now)
791 cs = stringio()
796 cs = stringio()
792 write = cs.write
797 write = cs.write
793 write(b"".join(pl))
798 write(b"".join(pl))
794 for f, e in pycompat.iteritems(dmap):
799 for f, e in pycompat.iteritems(dmap):
795 if e.need_delay(now):
800 if e.need_delay(now):
796 # The file was last modified "simultaneously" with the current
801 # The file was last modified "simultaneously" with the current
797 # write to dirstate (i.e. within the same second for file-
802 # write to dirstate (i.e. within the same second for file-
798 # systems with a granularity of 1 sec). This commonly happens
803 # systems with a granularity of 1 sec). This commonly happens
799 # for at least a couple of files on 'update'.
804 # for at least a couple of files on 'update'.
800 # The user could change the file without changing its size
805 # The user could change the file without changing its size
801 # within the same second. Invalidate the file's mtime in
806 # within the same second. Invalidate the file's mtime in
802 # dirstate, forcing future 'status' calls to compare the
807 # dirstate, forcing future 'status' calls to compare the
803 # contents of the file if the size is the same. This prevents
808 # contents of the file if the size is the same. This prevents
804 # mistakenly treating such files as clean.
809 # mistakenly treating such files as clean.
805 e.set_possibly_dirty()
810 e.set_possibly_dirty()
806
811
807 if f in copymap:
812 if f in copymap:
808 f = b"%s\0%s" % (f, copymap[f])
813 f = b"%s\0%s" % (f, copymap[f])
809 e = _pack(
814 e = _pack(
810 b">cllll",
815 b">cllll",
811 e.v1_state(),
816 e.v1_state(),
812 e.v1_mode(),
817 e.v1_mode(),
813 e.v1_size(),
818 e.v1_size(),
814 e.v1_mtime(),
819 e.v1_mtime(),
815 len(f),
820 len(f),
816 )
821 )
817 write(e)
822 write(e)
818 write(f)
823 write(f)
819 return cs.getvalue()
824 return cs.getvalue()
@@ -1,394 +1,400
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 {
209 self.flags.intersects(
210 Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_TRACKED,
211 )
212 }
213
208 pub fn state(&self) -> EntryState {
214 pub fn state(&self) -> EntryState {
209 if self.removed() {
215 if self.removed() {
210 EntryState::Removed
216 EntryState::Removed
211 } else if self.merged() {
217 } else if self.merged() {
212 EntryState::Merged
218 EntryState::Merged
213 } else if self.added() {
219 } else if self.added() {
214 EntryState::Added
220 EntryState::Added
215 } else {
221 } else {
216 EntryState::Normal
222 EntryState::Normal
217 }
223 }
218 }
224 }
219
225
220 pub fn mode(&self) -> i32 {
226 pub fn mode(&self) -> i32 {
221 self.mode
227 self.mode
222 }
228 }
223
229
224 pub fn size(&self) -> i32 {
230 pub fn size(&self) -> i32 {
225 if self.removed() && self.flags.contains(Flags::MERGED) {
231 if self.removed() && self.flags.contains(Flags::MERGED) {
226 SIZE_NON_NORMAL
232 SIZE_NON_NORMAL
227 } else if self.removed() && self.flags.contains(Flags::CLEAN_P2) {
233 } else if self.removed() && self.flags.contains(Flags::CLEAN_P2) {
228 SIZE_FROM_OTHER_PARENT
234 SIZE_FROM_OTHER_PARENT
229 } else if self.removed() {
235 } else if self.removed() {
230 0
236 0
231 } else if self.merged() {
237 } else if self.merged() {
232 SIZE_FROM_OTHER_PARENT
238 SIZE_FROM_OTHER_PARENT
233 } else if self.added() {
239 } else if self.added() {
234 SIZE_NON_NORMAL
240 SIZE_NON_NORMAL
235 } else if self.from_p2() {
241 } else if self.from_p2() {
236 SIZE_FROM_OTHER_PARENT
242 SIZE_FROM_OTHER_PARENT
237 } else if self.flags.contains(Flags::POSSIBLY_DIRTY) {
243 } else if self.flags.contains(Flags::POSSIBLY_DIRTY) {
238 self.size // TODO: SIZE_NON_NORMAL ?
244 self.size // TODO: SIZE_NON_NORMAL ?
239 } else {
245 } else {
240 self.size
246 self.size
241 }
247 }
242 }
248 }
243
249
244 pub fn mtime(&self) -> i32 {
250 pub fn mtime(&self) -> i32 {
245 if self.removed() {
251 if self.removed() {
246 0
252 0
247 } else if self.flags.contains(Flags::POSSIBLY_DIRTY) {
253 } else if self.flags.contains(Flags::POSSIBLY_DIRTY) {
248 MTIME_UNSET
254 MTIME_UNSET
249 } else if self.merged() {
255 } else if self.merged() {
250 MTIME_UNSET
256 MTIME_UNSET
251 } else if self.added() {
257 } else if self.added() {
252 MTIME_UNSET
258 MTIME_UNSET
253 } else if self.from_p2() {
259 } else if self.from_p2() {
254 MTIME_UNSET
260 MTIME_UNSET
255 } else {
261 } else {
256 self.mtime
262 self.mtime
257 }
263 }
258 }
264 }
259
265
260 pub fn set_possibly_dirty(&mut self) {
266 pub fn set_possibly_dirty(&mut self) {
261 self.flags.insert(Flags::POSSIBLY_DIRTY)
267 self.flags.insert(Flags::POSSIBLY_DIRTY)
262 }
268 }
263
269
264 pub fn set_clean(&mut self, mode: i32, size: i32, mtime: i32) {
270 pub fn set_clean(&mut self, mode: i32, size: i32, mtime: i32) {
265 self.flags.insert(Flags::WDIR_TRACKED | Flags::P1_TRACKED);
271 self.flags.insert(Flags::WDIR_TRACKED | Flags::P1_TRACKED);
266 self.flags.remove(
272 self.flags.remove(
267 Flags::P2_TRACKED // This might be wrong
273 Flags::P2_TRACKED // This might be wrong
268 | Flags::MERGED
274 | Flags::MERGED
269 | Flags::CLEAN_P2
275 | Flags::CLEAN_P2
270 | Flags::POSSIBLY_DIRTY,
276 | Flags::POSSIBLY_DIRTY,
271 );
277 );
272 self.mode = mode;
278 self.mode = mode;
273 self.size = size;
279 self.size = size;
274 self.mtime = mtime;
280 self.mtime = mtime;
275 }
281 }
276
282
277 pub fn set_tracked(&mut self) {
283 pub fn set_tracked(&mut self) {
278 self.flags
284 self.flags
279 .insert(Flags::WDIR_TRACKED | Flags::POSSIBLY_DIRTY);
285 .insert(Flags::WDIR_TRACKED | Flags::POSSIBLY_DIRTY);
280 // size = None on the python size turn into size = NON_NORMAL when
286 // size = None on the python size turn into size = NON_NORMAL when
281 // accessed. So the next line is currently required, but a some future
287 // accessed. So the next line is currently required, but a some future
282 // clean up would be welcome.
288 // clean up would be welcome.
283 self.size = SIZE_NON_NORMAL;
289 self.size = SIZE_NON_NORMAL;
284 }
290 }
285
291
286 pub fn set_untracked(&mut self) {
292 pub fn set_untracked(&mut self) {
287 self.flags.remove(Flags::WDIR_TRACKED);
293 self.flags.remove(Flags::WDIR_TRACKED);
288 self.mode = 0;
294 self.mode = 0;
289 self.size = 0;
295 self.size = 0;
290 self.mtime = 0;
296 self.mtime = 0;
291 }
297 }
292
298
293 /// Returns `(state, mode, size, mtime)` for the puprose of serialization
299 /// Returns `(state, mode, size, mtime)` for the puprose of serialization
294 /// in the dirstate-v1 format.
300 /// in the dirstate-v1 format.
295 ///
301 ///
296 /// This includes marker values such as `mtime == -1`. In the future we may
302 /// This includes marker values such as `mtime == -1`. In the future we may
297 /// want to not represent these cases that way in memory, but serialization
303 /// want to not represent these cases that way in memory, but serialization
298 /// will need to keep the same format.
304 /// will need to keep the same format.
299 pub fn v1_data(&self) -> (u8, i32, i32, i32) {
305 pub fn v1_data(&self) -> (u8, i32, i32, i32) {
300 (self.state().into(), self.mode(), self.size(), self.mtime())
306 (self.state().into(), self.mode(), self.size(), self.mtime())
301 }
307 }
302
308
303 pub(crate) fn is_from_other_parent(&self) -> bool {
309 pub(crate) fn is_from_other_parent(&self) -> bool {
304 self.state() == EntryState::Normal
310 self.state() == EntryState::Normal
305 && self.size() == SIZE_FROM_OTHER_PARENT
311 && self.size() == SIZE_FROM_OTHER_PARENT
306 }
312 }
307
313
308 // TODO: other platforms
314 // TODO: other platforms
309 #[cfg(unix)]
315 #[cfg(unix)]
310 pub fn mode_changed(
316 pub fn mode_changed(
311 &self,
317 &self,
312 filesystem_metadata: &std::fs::Metadata,
318 filesystem_metadata: &std::fs::Metadata,
313 ) -> bool {
319 ) -> bool {
314 use std::os::unix::fs::MetadataExt;
320 use std::os::unix::fs::MetadataExt;
315 const EXEC_BIT_MASK: u32 = 0o100;
321 const EXEC_BIT_MASK: u32 = 0o100;
316 let dirstate_exec_bit = (self.mode() as u32) & EXEC_BIT_MASK;
322 let dirstate_exec_bit = (self.mode() as u32) & EXEC_BIT_MASK;
317 let fs_exec_bit = filesystem_metadata.mode() & EXEC_BIT_MASK;
323 let fs_exec_bit = filesystem_metadata.mode() & EXEC_BIT_MASK;
318 dirstate_exec_bit != fs_exec_bit
324 dirstate_exec_bit != fs_exec_bit
319 }
325 }
320
326
321 /// Returns a `(state, mode, size, mtime)` tuple as for
327 /// Returns a `(state, mode, size, mtime)` tuple as for
322 /// `DirstateMapMethods::debug_iter`.
328 /// `DirstateMapMethods::debug_iter`.
323 pub fn debug_tuple(&self) -> (u8, i32, i32, i32) {
329 pub fn debug_tuple(&self) -> (u8, i32, i32, i32) {
324 let state = if self.flags.contains(Flags::ENTRYLESS_TREE_NODE) {
330 let state = if self.flags.contains(Flags::ENTRYLESS_TREE_NODE) {
325 b' '
331 b' '
326 } else {
332 } else {
327 self.state().into()
333 self.state().into()
328 };
334 };
329 (state, self.mode(), self.size(), self.mtime())
335 (state, self.mode(), self.size(), self.mtime())
330 }
336 }
331
337
332 pub fn mtime_is_ambiguous(&self, now: i32) -> bool {
338 pub fn mtime_is_ambiguous(&self, now: i32) -> bool {
333 self.state() == EntryState::Normal && self.mtime() == now
339 self.state() == EntryState::Normal && self.mtime() == now
334 }
340 }
335
341
336 pub fn clear_ambiguous_mtime(&mut self, now: i32) -> bool {
342 pub fn clear_ambiguous_mtime(&mut self, now: i32) -> bool {
337 let ambiguous = self.mtime_is_ambiguous(now);
343 let ambiguous = self.mtime_is_ambiguous(now);
338 if ambiguous {
344 if ambiguous {
339 // The file was last modified "simultaneously" with the current
345 // The file was last modified "simultaneously" with the current
340 // write to dirstate (i.e. within the same second for file-
346 // write to dirstate (i.e. within the same second for file-
341 // systems with a granularity of 1 sec). This commonly happens
347 // systems with a granularity of 1 sec). This commonly happens
342 // for at least a couple of files on 'update'.
348 // for at least a couple of files on 'update'.
343 // The user could change the file without changing its size
349 // The user could change the file without changing its size
344 // within the same second. Invalidate the file's mtime in
350 // within the same second. Invalidate the file's mtime in
345 // dirstate, forcing future 'status' calls to compare the
351 // dirstate, forcing future 'status' calls to compare the
346 // contents of the file if the size is the same. This prevents
352 // contents of the file if the size is the same. This prevents
347 // mistakenly treating such files as clean.
353 // mistakenly treating such files as clean.
348 self.clear_mtime()
354 self.clear_mtime()
349 }
355 }
350 ambiguous
356 ambiguous
351 }
357 }
352
358
353 pub fn clear_mtime(&mut self) {
359 pub fn clear_mtime(&mut self) {
354 self.mtime = -1;
360 self.mtime = -1;
355 }
361 }
356 }
362 }
357
363
358 impl EntryState {
364 impl EntryState {
359 pub fn is_tracked(self) -> bool {
365 pub fn is_tracked(self) -> bool {
360 use EntryState::*;
366 use EntryState::*;
361 match self {
367 match self {
362 Normal | Added | Merged => true,
368 Normal | Added | Merged => true,
363 Removed => false,
369 Removed => false,
364 }
370 }
365 }
371 }
366 }
372 }
367
373
368 impl TryFrom<u8> for EntryState {
374 impl TryFrom<u8> for EntryState {
369 type Error = HgError;
375 type Error = HgError;
370
376
371 fn try_from(value: u8) -> Result<Self, Self::Error> {
377 fn try_from(value: u8) -> Result<Self, Self::Error> {
372 match value {
378 match value {
373 b'n' => Ok(EntryState::Normal),
379 b'n' => Ok(EntryState::Normal),
374 b'a' => Ok(EntryState::Added),
380 b'a' => Ok(EntryState::Added),
375 b'r' => Ok(EntryState::Removed),
381 b'r' => Ok(EntryState::Removed),
376 b'm' => Ok(EntryState::Merged),
382 b'm' => Ok(EntryState::Merged),
377 _ => Err(HgError::CorruptedRepository(format!(
383 _ => Err(HgError::CorruptedRepository(format!(
378 "Incorrect dirstate entry state {}",
384 "Incorrect dirstate entry state {}",
379 value
385 value
380 ))),
386 ))),
381 }
387 }
382 }
388 }
383 }
389 }
384
390
385 impl Into<u8> for EntryState {
391 impl Into<u8> for EntryState {
386 fn into(self) -> u8 {
392 fn into(self) -> u8 {
387 match self {
393 match self {
388 EntryState::Normal => b'n',
394 EntryState::Normal => b'n',
389 EntryState::Added => b'a',
395 EntryState::Added => b'a',
390 EntryState::Removed => b'r',
396 EntryState::Removed => b'r',
391 EntryState::Merged => b'm',
397 EntryState::Merged => b'm',
392 }
398 }
393 }
399 }
394 }
400 }
@@ -1,208 +1,213
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
94 def any_tracked(&self) -> PyResult<bool> {
95 Ok(self.entry(py).get().any_tracked())
96 }
97
93 def v1_state(&self) -> PyResult<PyBytes> {
98 def v1_state(&self) -> PyResult<PyBytes> {
94 let (state, _mode, _size, _mtime) = self.entry(py).get().v1_data();
99 let (state, _mode, _size, _mtime) = self.entry(py).get().v1_data();
95 let state_byte: u8 = state.into();
100 let state_byte: u8 = state.into();
96 Ok(PyBytes::new(py, &[state_byte]))
101 Ok(PyBytes::new(py, &[state_byte]))
97 }
102 }
98
103
99 def v1_mode(&self) -> PyResult<i32> {
104 def v1_mode(&self) -> PyResult<i32> {
100 let (_state, mode, _size, _mtime) = self.entry(py).get().v1_data();
105 let (_state, mode, _size, _mtime) = self.entry(py).get().v1_data();
101 Ok(mode)
106 Ok(mode)
102 }
107 }
103
108
104 def v1_size(&self) -> PyResult<i32> {
109 def v1_size(&self) -> PyResult<i32> {
105 let (_state, _mode, size, _mtime) = self.entry(py).get().v1_data();
110 let (_state, _mode, size, _mtime) = self.entry(py).get().v1_data();
106 Ok(size)
111 Ok(size)
107 }
112 }
108
113
109 def v1_mtime(&self) -> PyResult<i32> {
114 def v1_mtime(&self) -> PyResult<i32> {
110 let (_state, _mode, _size, mtime) = self.entry(py).get().v1_data();
115 let (_state, _mode, _size, mtime) = self.entry(py).get().v1_data();
111 Ok(mtime)
116 Ok(mtime)
112 }
117 }
113
118
114 def need_delay(&self, now: i32) -> PyResult<bool> {
119 def need_delay(&self, now: i32) -> PyResult<bool> {
115 Ok(self.entry(py).get().mtime_is_ambiguous(now))
120 Ok(self.entry(py).get().mtime_is_ambiguous(now))
116 }
121 }
117
122
118 @classmethod
123 @classmethod
119 def from_v1_data(
124 def from_v1_data(
120 _cls,
125 _cls,
121 state: PyBytes,
126 state: PyBytes,
122 mode: i32,
127 mode: i32,
123 size: i32,
128 size: i32,
124 mtime: i32,
129 mtime: i32,
125 ) -> PyResult<Self> {
130 ) -> PyResult<Self> {
126 let state = <[u8; 1]>::try_from(state.data(py))
131 let state = <[u8; 1]>::try_from(state.data(py))
127 .ok()
132 .ok()
128 .and_then(|state| EntryState::try_from(state[0]).ok())
133 .and_then(|state| EntryState::try_from(state[0]).ok())
129 .ok_or_else(|| PyErr::new::<exc::ValueError, _>(py, "invalid state"))?;
134 .ok_or_else(|| PyErr::new::<exc::ValueError, _>(py, "invalid state"))?;
130 let entry = DirstateEntry::from_v1_data(state, mode, size, mtime);
135 let entry = DirstateEntry::from_v1_data(state, mode, size, mtime);
131 DirstateItem::create_instance(py, Cell::new(entry))
136 DirstateItem::create_instance(py, Cell::new(entry))
132 }
137 }
133
138
134 @classmethod
139 @classmethod
135 def new_added(_cls) -> PyResult<Self> {
140 def new_added(_cls) -> PyResult<Self> {
136 let entry = DirstateEntry::new_added();
141 let entry = DirstateEntry::new_added();
137 DirstateItem::create_instance(py, Cell::new(entry))
142 DirstateItem::create_instance(py, Cell::new(entry))
138 }
143 }
139
144
140 @classmethod
145 @classmethod
141 def new_merged(_cls) -> PyResult<Self> {
146 def new_merged(_cls) -> PyResult<Self> {
142 let entry = DirstateEntry::new_merged();
147 let entry = DirstateEntry::new_merged();
143 DirstateItem::create_instance(py, Cell::new(entry))
148 DirstateItem::create_instance(py, Cell::new(entry))
144 }
149 }
145
150
146 @classmethod
151 @classmethod
147 def new_from_p2(_cls) -> PyResult<Self> {
152 def new_from_p2(_cls) -> PyResult<Self> {
148 let entry = DirstateEntry::new_from_p2();
153 let entry = DirstateEntry::new_from_p2();
149 DirstateItem::create_instance(py, Cell::new(entry))
154 DirstateItem::create_instance(py, Cell::new(entry))
150 }
155 }
151
156
152 @classmethod
157 @classmethod
153 def new_possibly_dirty(_cls) -> PyResult<Self> {
158 def new_possibly_dirty(_cls) -> PyResult<Self> {
154 let entry = DirstateEntry::new_possibly_dirty();
159 let entry = DirstateEntry::new_possibly_dirty();
155 DirstateItem::create_instance(py, Cell::new(entry))
160 DirstateItem::create_instance(py, Cell::new(entry))
156 }
161 }
157
162
158 @classmethod
163 @classmethod
159 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> {
160 let entry = DirstateEntry::new_normal(mode, size, mtime);
165 let entry = DirstateEntry::new_normal(mode, size, mtime);
161 DirstateItem::create_instance(py, Cell::new(entry))
166 DirstateItem::create_instance(py, Cell::new(entry))
162 }
167 }
163
168
164 def set_clean(
169 def set_clean(
165 &self,
170 &self,
166 mode: i32,
171 mode: i32,
167 size: i32,
172 size: i32,
168 mtime: i32,
173 mtime: i32,
169 ) -> PyResult<PyNone> {
174 ) -> PyResult<PyNone> {
170 self.update(py, |entry| entry.set_clean(mode, size, mtime));
175 self.update(py, |entry| entry.set_clean(mode, size, mtime));
171 Ok(PyNone)
176 Ok(PyNone)
172 }
177 }
173
178
174 def set_possibly_dirty(&self) -> PyResult<PyNone> {
179 def set_possibly_dirty(&self) -> PyResult<PyNone> {
175 self.update(py, |entry| entry.set_possibly_dirty());
180 self.update(py, |entry| entry.set_possibly_dirty());
176 Ok(PyNone)
181 Ok(PyNone)
177 }
182 }
178
183
179 def set_tracked(&self) -> PyResult<PyNone> {
184 def set_tracked(&self) -> PyResult<PyNone> {
180 self.update(py, |entry| entry.set_tracked());
185 self.update(py, |entry| entry.set_tracked());
181 Ok(PyNone)
186 Ok(PyNone)
182 }
187 }
183
188
184 def set_untracked(&self) -> PyResult<PyNone> {
189 def set_untracked(&self) -> PyResult<PyNone> {
185 self.update(py, |entry| entry.set_untracked());
190 self.update(py, |entry| entry.set_untracked());
186 Ok(PyNone)
191 Ok(PyNone)
187 }
192 }
188 });
193 });
189
194
190 impl DirstateItem {
195 impl DirstateItem {
191 pub fn new_as_pyobject(
196 pub fn new_as_pyobject(
192 py: Python<'_>,
197 py: Python<'_>,
193 entry: DirstateEntry,
198 entry: DirstateEntry,
194 ) -> PyResult<PyObject> {
199 ) -> PyResult<PyObject> {
195 Ok(DirstateItem::create_instance(py, Cell::new(entry))?.into_object())
200 Ok(DirstateItem::create_instance(py, Cell::new(entry))?.into_object())
196 }
201 }
197
202
198 pub fn get_entry(&self, py: Python<'_>) -> DirstateEntry {
203 pub fn get_entry(&self, py: Python<'_>) -> DirstateEntry {
199 self.entry(py).get()
204 self.entry(py).get()
200 }
205 }
201
206
202 // TODO: Use https://doc.rust-lang.org/std/cell/struct.Cell.html#method.update instead when it’s stable
207 // TODO: Use https://doc.rust-lang.org/std/cell/struct.Cell.html#method.update instead when it’s stable
203 pub fn update(&self, py: Python<'_>, f: impl FnOnce(&mut DirstateEntry)) {
208 pub fn update(&self, py: Python<'_>, f: impl FnOnce(&mut DirstateEntry)) {
204 let mut entry = self.entry(py).get();
209 let mut entry = self.entry(py).get();
205 f(&mut entry);
210 f(&mut entry);
206 self.entry(py).set(entry)
211 self.entry(py).set(entry)
207 }
212 }
208 }
213 }
General Comments 0
You need to be logged in to leave comments. Login now