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