##// END OF EJS Templates
dirstate-item: feed more information to `__init__`...
marmoute -
r48706:22c39f8a default
parent child Browse files
Show More
@@ -1,1004 +1,1083 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 inline dirstateItemObject *make_dirstate_item(char state, int mode,
47 static inline dirstateItemObject *make_dirstate_item(char state, int mode,
48 int size, int mtime)
48 int size, int mtime)
49 {
49 {
50 dirstateItemObject *t =
50 dirstateItemObject *t =
51 PyObject_New(dirstateItemObject, &dirstateItemType);
51 PyObject_New(dirstateItemObject, &dirstateItemType);
52 if (!t) {
52 if (!t) {
53 return NULL;
53 return NULL;
54 }
54 }
55 t->state = state;
55 t->state = state;
56 t->mode = mode;
56 t->mode = mode;
57 t->size = size;
57 t->size = size;
58 t->mtime = mtime;
58 t->mtime = mtime;
59 return t;
59 return t;
60 }
60 }
61
61
62 static PyObject *dirstate_item_new(PyTypeObject *subtype, PyObject *args,
62 static PyObject *dirstate_item_new(PyTypeObject *subtype, PyObject *args,
63 PyObject *kwds)
63 PyObject *kwds)
64 {
64 {
65 /* We do all the initialization here and not a tp_init function because
65 /* We do all the initialization here and not a tp_init function because
66 * dirstate_item is immutable. */
66 * dirstate_item is immutable. */
67 dirstateItemObject *t;
67 dirstateItemObject *t;
68 char state;
68 int wc_tracked;
69 int size, mode, mtime;
69 int p1_tracked;
70 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime)) {
70 int p2_tracked;
71 int merged;
72 int clean_p1;
73 int clean_p2;
74 int possibly_dirty;
75 PyObject *parentfiledata;
76 static char *keywords_name[] = {
77 "wc_tracked", "p1_tracked", "p2_tracked",
78 "merged", "clean_p1", "clean_p2",
79 "possibly_dirty", "parentfiledata", NULL,
80 };
81 wc_tracked = 0;
82 p1_tracked = 0;
83 p2_tracked = 0;
84 merged = 0;
85 clean_p1 = 0;
86 clean_p2 = 0;
87 possibly_dirty = 0;
88 parentfiledata = Py_None;
89 if (!PyArg_ParseTupleAndKeywords(args, kwds, "iiiiiiiO", keywords_name,
90 &wc_tracked, &p1_tracked, &p2_tracked,
91 &merged, &clean_p1, &clean_p2,
92 &possibly_dirty, &parentfiledata
93
94 )) {
71 return NULL;
95 return NULL;
72 }
96 }
73
97 if (merged && (clean_p1 || clean_p2)) {
98 PyErr_SetString(PyExc_RuntimeError,
99 "`merged` argument incompatible with "
100 "`clean_p1`/`clean_p2`");
101 return NULL;
102 }
74 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
103 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
75 if (!t) {
104 if (!t) {
76 return NULL;
105 return NULL;
77 }
106 }
78 t->state = state;
107 t->state = 'r';
79 t->mode = mode;
108 t->mode = 0;
80 t->size = size;
109 t->size = dirstate_v1_nonnormal;
81 t->mtime = mtime;
110 t->mtime = ambiguous_time;
82
111 if (!(p1_tracked || p2_tracked || wc_tracked)) {
112 /* Nothing special to do, file is untracked */
113 } else if (merged) {
114 t->state = 'm';
115 t->size = dirstate_v1_from_p2;
116 t->mtime = ambiguous_time;
117 } else if (!(p1_tracked || p2_tracked) && wc_tracked) {
118 t->state = 'a';
119 t->size = dirstate_v1_nonnormal;
120 t->mtime = ambiguous_time;
121 } else if ((p1_tracked || p2_tracked) && !wc_tracked) {
122 t->state = 'r';
123 t->size = 0;
124 t->mtime = 0;
125 } else if (clean_p2 && wc_tracked) {
126 t->state = 'n';
127 t->size = dirstate_v1_from_p2;
128 t->mtime = ambiguous_time;
129 } else if (!p1_tracked && p2_tracked && wc_tracked) {
130 t->state = 'n';
131 t->size = dirstate_v1_from_p2;
132 t->mtime = ambiguous_time;
133 } else if (possibly_dirty) {
134 t->state = 'n';
135 t->size = dirstate_v1_nonnormal;
136 t->mtime = ambiguous_time;
137 } else if (wc_tracked) {
138 /* this is a "normal" file */
139 if (parentfiledata == Py_None) {
140 PyErr_SetString(
141 PyExc_RuntimeError,
142 "failed to pass parentfiledata for a normal file");
143 return NULL;
144 }
145 if (!PyTuple_CheckExact(parentfiledata)) {
146 PyErr_SetString(
147 PyExc_TypeError,
148 "parentfiledata should be a Tuple or None");
149 return NULL;
150 }
151 t->state = 'n';
152 t->mode =
153 (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 0));
154 t->size =
155 (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 1));
156 t->mtime =
157 (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 2));
158 } else {
159 PyErr_SetString(PyExc_RuntimeError, "unreachable");
160 return NULL;
161 }
83 return (PyObject *)t;
162 return (PyObject *)t;
84 }
163 }
85
164
86 static void dirstate_item_dealloc(PyObject *o)
165 static void dirstate_item_dealloc(PyObject *o)
87 {
166 {
88 PyObject_Del(o);
167 PyObject_Del(o);
89 }
168 }
90
169
91 static Py_ssize_t dirstate_item_length(PyObject *o)
170 static Py_ssize_t dirstate_item_length(PyObject *o)
92 {
171 {
93 return 4;
172 return 4;
94 }
173 }
95
174
96 static PyObject *dirstate_item_item(PyObject *o, Py_ssize_t i)
175 static PyObject *dirstate_item_item(PyObject *o, Py_ssize_t i)
97 {
176 {
98 dirstateItemObject *t = (dirstateItemObject *)o;
177 dirstateItemObject *t = (dirstateItemObject *)o;
99 switch (i) {
178 switch (i) {
100 case 0:
179 case 0:
101 return PyBytes_FromStringAndSize(&t->state, 1);
180 return PyBytes_FromStringAndSize(&t->state, 1);
102 case 1:
181 case 1:
103 return PyInt_FromLong(t->mode);
182 return PyInt_FromLong(t->mode);
104 case 2:
183 case 2:
105 return PyInt_FromLong(t->size);
184 return PyInt_FromLong(t->size);
106 case 3:
185 case 3:
107 return PyInt_FromLong(t->mtime);
186 return PyInt_FromLong(t->mtime);
108 default:
187 default:
109 PyErr_SetString(PyExc_IndexError, "index out of range");
188 PyErr_SetString(PyExc_IndexError, "index out of range");
110 return NULL;
189 return NULL;
111 }
190 }
112 }
191 }
113
192
114 static PySequenceMethods dirstate_item_sq = {
193 static PySequenceMethods dirstate_item_sq = {
115 dirstate_item_length, /* sq_length */
194 dirstate_item_length, /* sq_length */
116 0, /* sq_concat */
195 0, /* sq_concat */
117 0, /* sq_repeat */
196 0, /* sq_repeat */
118 dirstate_item_item, /* sq_item */
197 dirstate_item_item, /* sq_item */
119 0, /* sq_ass_item */
198 0, /* sq_ass_item */
120 0, /* sq_contains */
199 0, /* sq_contains */
121 0, /* sq_inplace_concat */
200 0, /* sq_inplace_concat */
122 0 /* sq_inplace_repeat */
201 0 /* sq_inplace_repeat */
123 };
202 };
124
203
125 static PyObject *dirstate_item_v1_state(dirstateItemObject *self)
204 static PyObject *dirstate_item_v1_state(dirstateItemObject *self)
126 {
205 {
127 return PyBytes_FromStringAndSize(&self->state, 1);
206 return PyBytes_FromStringAndSize(&self->state, 1);
128 };
207 };
129
208
130 static PyObject *dirstate_item_v1_mode(dirstateItemObject *self)
209 static PyObject *dirstate_item_v1_mode(dirstateItemObject *self)
131 {
210 {
132 return PyInt_FromLong(self->mode);
211 return PyInt_FromLong(self->mode);
133 };
212 };
134
213
135 static PyObject *dirstate_item_v1_size(dirstateItemObject *self)
214 static PyObject *dirstate_item_v1_size(dirstateItemObject *self)
136 {
215 {
137 return PyInt_FromLong(self->size);
216 return PyInt_FromLong(self->size);
138 };
217 };
139
218
140 static PyObject *dirstate_item_v1_mtime(dirstateItemObject *self)
219 static PyObject *dirstate_item_v1_mtime(dirstateItemObject *self)
141 {
220 {
142 return PyInt_FromLong(self->mtime);
221 return PyInt_FromLong(self->mtime);
143 };
222 };
144
223
145 static PyObject *dirstate_item_need_delay(dirstateItemObject *self,
224 static PyObject *dirstate_item_need_delay(dirstateItemObject *self,
146 PyObject *value)
225 PyObject *value)
147 {
226 {
148 long now;
227 long now;
149 if (!pylong_to_long(value, &now)) {
228 if (!pylong_to_long(value, &now)) {
150 return NULL;
229 return NULL;
151 }
230 }
152 if (self->state == 'n' && self->mtime == now) {
231 if (self->state == 'n' && self->mtime == now) {
153 Py_RETURN_TRUE;
232 Py_RETURN_TRUE;
154 } else {
233 } else {
155 Py_RETURN_FALSE;
234 Py_RETURN_FALSE;
156 }
235 }
157 };
236 };
158
237
159 /* This will never change since it's bound to V1, unlike `make_dirstate_item`
238 /* This will never change since it's bound to V1, unlike `make_dirstate_item`
160 */
239 */
161 static inline dirstateItemObject *
240 static inline dirstateItemObject *
162 dirstate_item_from_v1_data(char state, int mode, int size, int mtime)
241 dirstate_item_from_v1_data(char state, int mode, int size, int mtime)
163 {
242 {
164 dirstateItemObject *t =
243 dirstateItemObject *t =
165 PyObject_New(dirstateItemObject, &dirstateItemType);
244 PyObject_New(dirstateItemObject, &dirstateItemType);
166 if (!t) {
245 if (!t) {
167 return NULL;
246 return NULL;
168 }
247 }
169 t->state = state;
248 t->state = state;
170 t->mode = mode;
249 t->mode = mode;
171 t->size = size;
250 t->size = size;
172 t->mtime = mtime;
251 t->mtime = mtime;
173 return t;
252 return t;
174 }
253 }
175
254
176 /* This will never change since it's bound to V1, unlike `dirstate_item_new` */
255 /* This will never change since it's bound to V1, unlike `dirstate_item_new` */
177 static PyObject *dirstate_item_from_v1_meth(PyTypeObject *subtype,
256 static PyObject *dirstate_item_from_v1_meth(PyTypeObject *subtype,
178 PyObject *args)
257 PyObject *args)
179 {
258 {
180 /* We do all the initialization here and not a tp_init function because
259 /* We do all the initialization here and not a tp_init function because
181 * dirstate_item is immutable. */
260 * dirstate_item is immutable. */
182 dirstateItemObject *t;
261 dirstateItemObject *t;
183 char state;
262 char state;
184 int size, mode, mtime;
263 int size, mode, mtime;
185 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime)) {
264 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime)) {
186 return NULL;
265 return NULL;
187 }
266 }
188
267
189 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
268 t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1);
190 if (!t) {
269 if (!t) {
191 return NULL;
270 return NULL;
192 }
271 }
193 t->state = state;
272 t->state = state;
194 t->mode = mode;
273 t->mode = mode;
195 t->size = size;
274 t->size = size;
196 t->mtime = mtime;
275 t->mtime = mtime;
197
276
198 return (PyObject *)t;
277 return (PyObject *)t;
199 };
278 };
200
279
201 /* This means the next status call will have to actually check its content
280 /* This means the next status call will have to actually check its content
202 to make sure it is correct. */
281 to make sure it is correct. */
203 static PyObject *dirstate_item_set_possibly_dirty(dirstateItemObject *self)
282 static PyObject *dirstate_item_set_possibly_dirty(dirstateItemObject *self)
204 {
283 {
205 self->mtime = ambiguous_time;
284 self->mtime = ambiguous_time;
206 Py_RETURN_NONE;
285 Py_RETURN_NONE;
207 }
286 }
208
287
209 static PyObject *dirstate_item_set_untracked(dirstateItemObject *self)
288 static PyObject *dirstate_item_set_untracked(dirstateItemObject *self)
210 {
289 {
211 if (self->state == 'm') {
290 if (self->state == 'm') {
212 self->size = dirstate_v1_nonnormal;
291 self->size = dirstate_v1_nonnormal;
213 } else if (self->state == 'n' && self->size == dirstate_v1_from_p2) {
292 } else if (self->state == 'n' && self->size == dirstate_v1_from_p2) {
214 self->size = dirstate_v1_from_p2;
293 self->size = dirstate_v1_from_p2;
215 } else {
294 } else {
216 self->size = 0;
295 self->size = 0;
217 }
296 }
218 self->state = 'r';
297 self->state = 'r';
219 self->mode = 0;
298 self->mode = 0;
220 self->mtime = 0;
299 self->mtime = 0;
221 Py_RETURN_NONE;
300 Py_RETURN_NONE;
222 }
301 }
223
302
224 static PyMethodDef dirstate_item_methods[] = {
303 static PyMethodDef dirstate_item_methods[] = {
225 {"v1_state", (PyCFunction)dirstate_item_v1_state, METH_NOARGS,
304 {"v1_state", (PyCFunction)dirstate_item_v1_state, METH_NOARGS,
226 "return a \"state\" suitable for v1 serialization"},
305 "return a \"state\" suitable for v1 serialization"},
227 {"v1_mode", (PyCFunction)dirstate_item_v1_mode, METH_NOARGS,
306 {"v1_mode", (PyCFunction)dirstate_item_v1_mode, METH_NOARGS,
228 "return a \"mode\" suitable for v1 serialization"},
307 "return a \"mode\" suitable for v1 serialization"},
229 {"v1_size", (PyCFunction)dirstate_item_v1_size, METH_NOARGS,
308 {"v1_size", (PyCFunction)dirstate_item_v1_size, METH_NOARGS,
230 "return a \"size\" suitable for v1 serialization"},
309 "return a \"size\" suitable for v1 serialization"},
231 {"v1_mtime", (PyCFunction)dirstate_item_v1_mtime, METH_NOARGS,
310 {"v1_mtime", (PyCFunction)dirstate_item_v1_mtime, METH_NOARGS,
232 "return a \"mtime\" suitable for v1 serialization"},
311 "return a \"mtime\" suitable for v1 serialization"},
233 {"need_delay", (PyCFunction)dirstate_item_need_delay, METH_O,
312 {"need_delay", (PyCFunction)dirstate_item_need_delay, METH_O,
234 "True if the stored mtime would be ambiguous with the current time"},
313 "True if the stored mtime would be ambiguous with the current time"},
235 {"from_v1_data", (PyCFunction)dirstate_item_from_v1_meth,
314 {"from_v1_data", (PyCFunction)dirstate_item_from_v1_meth,
236 METH_VARARGS | METH_CLASS, "build a new DirstateItem object from V1 data"},
315 METH_VARARGS | METH_CLASS, "build a new DirstateItem object from V1 data"},
237 {"set_possibly_dirty", (PyCFunction)dirstate_item_set_possibly_dirty,
316 {"set_possibly_dirty", (PyCFunction)dirstate_item_set_possibly_dirty,
238 METH_NOARGS, "mark a file as \"possibly dirty\""},
317 METH_NOARGS, "mark a file as \"possibly dirty\""},
239 {"set_untracked", (PyCFunction)dirstate_item_set_untracked, METH_NOARGS,
318 {"set_untracked", (PyCFunction)dirstate_item_set_untracked, METH_NOARGS,
240 "mark a file as \"untracked\""},
319 "mark a file as \"untracked\""},
241 {NULL} /* Sentinel */
320 {NULL} /* Sentinel */
242 };
321 };
243
322
244 static PyObject *dirstate_item_get_mode(dirstateItemObject *self)
323 static PyObject *dirstate_item_get_mode(dirstateItemObject *self)
245 {
324 {
246 return PyInt_FromLong(self->mode);
325 return PyInt_FromLong(self->mode);
247 };
326 };
248
327
249 static PyObject *dirstate_item_get_size(dirstateItemObject *self)
328 static PyObject *dirstate_item_get_size(dirstateItemObject *self)
250 {
329 {
251 return PyInt_FromLong(self->size);
330 return PyInt_FromLong(self->size);
252 };
331 };
253
332
254 static PyObject *dirstate_item_get_mtime(dirstateItemObject *self)
333 static PyObject *dirstate_item_get_mtime(dirstateItemObject *self)
255 {
334 {
256 return PyInt_FromLong(self->mtime);
335 return PyInt_FromLong(self->mtime);
257 };
336 };
258
337
259 static PyObject *dirstate_item_get_state(dirstateItemObject *self)
338 static PyObject *dirstate_item_get_state(dirstateItemObject *self)
260 {
339 {
261 return PyBytes_FromStringAndSize(&self->state, 1);
340 return PyBytes_FromStringAndSize(&self->state, 1);
262 };
341 };
263
342
264 static PyObject *dirstate_item_get_tracked(dirstateItemObject *self)
343 static PyObject *dirstate_item_get_tracked(dirstateItemObject *self)
265 {
344 {
266 if (self->state == 'a' || self->state == 'm' || self->state == 'n') {
345 if (self->state == 'a' || self->state == 'm' || self->state == 'n') {
267 Py_RETURN_TRUE;
346 Py_RETURN_TRUE;
268 } else {
347 } else {
269 Py_RETURN_FALSE;
348 Py_RETURN_FALSE;
270 }
349 }
271 };
350 };
272
351
273 static PyObject *dirstate_item_get_added(dirstateItemObject *self)
352 static PyObject *dirstate_item_get_added(dirstateItemObject *self)
274 {
353 {
275 if (self->state == 'a') {
354 if (self->state == 'a') {
276 Py_RETURN_TRUE;
355 Py_RETURN_TRUE;
277 } else {
356 } else {
278 Py_RETURN_FALSE;
357 Py_RETURN_FALSE;
279 }
358 }
280 };
359 };
281
360
282 static PyObject *dirstate_item_get_merged(dirstateItemObject *self)
361 static PyObject *dirstate_item_get_merged(dirstateItemObject *self)
283 {
362 {
284 if (self->state == 'm') {
363 if (self->state == 'm') {
285 Py_RETURN_TRUE;
364 Py_RETURN_TRUE;
286 } else {
365 } else {
287 Py_RETURN_FALSE;
366 Py_RETURN_FALSE;
288 }
367 }
289 };
368 };
290
369
291 static PyObject *dirstate_item_get_merged_removed(dirstateItemObject *self)
370 static PyObject *dirstate_item_get_merged_removed(dirstateItemObject *self)
292 {
371 {
293 if (self->state == 'r' && self->size == dirstate_v1_nonnormal) {
372 if (self->state == 'r' && self->size == dirstate_v1_nonnormal) {
294 Py_RETURN_TRUE;
373 Py_RETURN_TRUE;
295 } else {
374 } else {
296 Py_RETURN_FALSE;
375 Py_RETURN_FALSE;
297 }
376 }
298 };
377 };
299
378
300 static PyObject *dirstate_item_get_from_p2(dirstateItemObject *self)
379 static PyObject *dirstate_item_get_from_p2(dirstateItemObject *self)
301 {
380 {
302 if (self->state == 'n' && self->size == dirstate_v1_from_p2) {
381 if (self->state == 'n' && self->size == dirstate_v1_from_p2) {
303 Py_RETURN_TRUE;
382 Py_RETURN_TRUE;
304 } else {
383 } else {
305 Py_RETURN_FALSE;
384 Py_RETURN_FALSE;
306 }
385 }
307 };
386 };
308
387
309 static PyObject *dirstate_item_get_from_p2_removed(dirstateItemObject *self)
388 static PyObject *dirstate_item_get_from_p2_removed(dirstateItemObject *self)
310 {
389 {
311 if (self->state == 'r' && self->size == dirstate_v1_from_p2) {
390 if (self->state == 'r' && self->size == dirstate_v1_from_p2) {
312 Py_RETURN_TRUE;
391 Py_RETURN_TRUE;
313 } else {
392 } else {
314 Py_RETURN_FALSE;
393 Py_RETURN_FALSE;
315 }
394 }
316 };
395 };
317
396
318 static PyObject *dirstate_item_get_removed(dirstateItemObject *self)
397 static PyObject *dirstate_item_get_removed(dirstateItemObject *self)
319 {
398 {
320 if (self->state == 'r') {
399 if (self->state == 'r') {
321 Py_RETURN_TRUE;
400 Py_RETURN_TRUE;
322 } else {
401 } else {
323 Py_RETURN_FALSE;
402 Py_RETURN_FALSE;
324 }
403 }
325 };
404 };
326
405
327 static PyObject *dm_nonnormal(dirstateItemObject *self)
406 static PyObject *dm_nonnormal(dirstateItemObject *self)
328 {
407 {
329 if (self->state != 'n' || self->mtime == ambiguous_time) {
408 if (self->state != 'n' || self->mtime == ambiguous_time) {
330 Py_RETURN_TRUE;
409 Py_RETURN_TRUE;
331 } else {
410 } else {
332 Py_RETURN_FALSE;
411 Py_RETURN_FALSE;
333 }
412 }
334 };
413 };
335 static PyObject *dm_otherparent(dirstateItemObject *self)
414 static PyObject *dm_otherparent(dirstateItemObject *self)
336 {
415 {
337 if (self->size == dirstate_v1_from_p2) {
416 if (self->size == dirstate_v1_from_p2) {
338 Py_RETURN_TRUE;
417 Py_RETURN_TRUE;
339 } else {
418 } else {
340 Py_RETURN_FALSE;
419 Py_RETURN_FALSE;
341 }
420 }
342 };
421 };
343
422
344 static PyGetSetDef dirstate_item_getset[] = {
423 static PyGetSetDef dirstate_item_getset[] = {
345 {"mode", (getter)dirstate_item_get_mode, NULL, "mode", NULL},
424 {"mode", (getter)dirstate_item_get_mode, NULL, "mode", NULL},
346 {"size", (getter)dirstate_item_get_size, NULL, "size", NULL},
425 {"size", (getter)dirstate_item_get_size, NULL, "size", NULL},
347 {"mtime", (getter)dirstate_item_get_mtime, NULL, "mtime", NULL},
426 {"mtime", (getter)dirstate_item_get_mtime, NULL, "mtime", NULL},
348 {"state", (getter)dirstate_item_get_state, NULL, "state", NULL},
427 {"state", (getter)dirstate_item_get_state, NULL, "state", NULL},
349 {"tracked", (getter)dirstate_item_get_tracked, NULL, "tracked", NULL},
428 {"tracked", (getter)dirstate_item_get_tracked, NULL, "tracked", NULL},
350 {"added", (getter)dirstate_item_get_added, NULL, "added", NULL},
429 {"added", (getter)dirstate_item_get_added, NULL, "added", NULL},
351 {"merged_removed", (getter)dirstate_item_get_merged_removed, NULL,
430 {"merged_removed", (getter)dirstate_item_get_merged_removed, NULL,
352 "merged_removed", NULL},
431 "merged_removed", NULL},
353 {"merged", (getter)dirstate_item_get_merged, NULL, "merged", NULL},
432 {"merged", (getter)dirstate_item_get_merged, NULL, "merged", NULL},
354 {"from_p2_removed", (getter)dirstate_item_get_from_p2_removed, NULL,
433 {"from_p2_removed", (getter)dirstate_item_get_from_p2_removed, NULL,
355 "from_p2_removed", NULL},
434 "from_p2_removed", NULL},
356 {"from_p2", (getter)dirstate_item_get_from_p2, NULL, "from_p2", NULL},
435 {"from_p2", (getter)dirstate_item_get_from_p2, NULL, "from_p2", NULL},
357 {"removed", (getter)dirstate_item_get_removed, NULL, "removed", NULL},
436 {"removed", (getter)dirstate_item_get_removed, NULL, "removed", NULL},
358 {"dm_nonnormal", (getter)dm_nonnormal, NULL, "dm_nonnormal", NULL},
437 {"dm_nonnormal", (getter)dm_nonnormal, NULL, "dm_nonnormal", NULL},
359 {"dm_otherparent", (getter)dm_otherparent, NULL, "dm_otherparent", NULL},
438 {"dm_otherparent", (getter)dm_otherparent, NULL, "dm_otherparent", NULL},
360 {NULL} /* Sentinel */
439 {NULL} /* Sentinel */
361 };
440 };
362
441
363 PyTypeObject dirstateItemType = {
442 PyTypeObject dirstateItemType = {
364 PyVarObject_HEAD_INIT(NULL, 0) /* header */
443 PyVarObject_HEAD_INIT(NULL, 0) /* header */
365 "dirstate_tuple", /* tp_name */
444 "dirstate_tuple", /* tp_name */
366 sizeof(dirstateItemObject), /* tp_basicsize */
445 sizeof(dirstateItemObject), /* tp_basicsize */
367 0, /* tp_itemsize */
446 0, /* tp_itemsize */
368 (destructor)dirstate_item_dealloc, /* tp_dealloc */
447 (destructor)dirstate_item_dealloc, /* tp_dealloc */
369 0, /* tp_print */
448 0, /* tp_print */
370 0, /* tp_getattr */
449 0, /* tp_getattr */
371 0, /* tp_setattr */
450 0, /* tp_setattr */
372 0, /* tp_compare */
451 0, /* tp_compare */
373 0, /* tp_repr */
452 0, /* tp_repr */
374 0, /* tp_as_number */
453 0, /* tp_as_number */
375 &dirstate_item_sq, /* tp_as_sequence */
454 &dirstate_item_sq, /* tp_as_sequence */
376 0, /* tp_as_mapping */
455 0, /* tp_as_mapping */
377 0, /* tp_hash */
456 0, /* tp_hash */
378 0, /* tp_call */
457 0, /* tp_call */
379 0, /* tp_str */
458 0, /* tp_str */
380 0, /* tp_getattro */
459 0, /* tp_getattro */
381 0, /* tp_setattro */
460 0, /* tp_setattro */
382 0, /* tp_as_buffer */
461 0, /* tp_as_buffer */
383 Py_TPFLAGS_DEFAULT, /* tp_flags */
462 Py_TPFLAGS_DEFAULT, /* tp_flags */
384 "dirstate tuple", /* tp_doc */
463 "dirstate tuple", /* tp_doc */
385 0, /* tp_traverse */
464 0, /* tp_traverse */
386 0, /* tp_clear */
465 0, /* tp_clear */
387 0, /* tp_richcompare */
466 0, /* tp_richcompare */
388 0, /* tp_weaklistoffset */
467 0, /* tp_weaklistoffset */
389 0, /* tp_iter */
468 0, /* tp_iter */
390 0, /* tp_iternext */
469 0, /* tp_iternext */
391 dirstate_item_methods, /* tp_methods */
470 dirstate_item_methods, /* tp_methods */
392 0, /* tp_members */
471 0, /* tp_members */
393 dirstate_item_getset, /* tp_getset */
472 dirstate_item_getset, /* tp_getset */
394 0, /* tp_base */
473 0, /* tp_base */
395 0, /* tp_dict */
474 0, /* tp_dict */
396 0, /* tp_descr_get */
475 0, /* tp_descr_get */
397 0, /* tp_descr_set */
476 0, /* tp_descr_set */
398 0, /* tp_dictoffset */
477 0, /* tp_dictoffset */
399 0, /* tp_init */
478 0, /* tp_init */
400 0, /* tp_alloc */
479 0, /* tp_alloc */
401 dirstate_item_new, /* tp_new */
480 dirstate_item_new, /* tp_new */
402 };
481 };
403
482
404 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
483 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
405 {
484 {
406 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
485 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
407 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
486 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
408 char state, *cur, *str, *cpos;
487 char state, *cur, *str, *cpos;
409 int mode, size, mtime;
488 int mode, size, mtime;
410 unsigned int flen, pos = 40;
489 unsigned int flen, pos = 40;
411 Py_ssize_t len = 40;
490 Py_ssize_t len = 40;
412 Py_ssize_t readlen;
491 Py_ssize_t readlen;
413
492
414 if (!PyArg_ParseTuple(
493 if (!PyArg_ParseTuple(
415 args, PY23("O!O!s#:parse_dirstate", "O!O!y#:parse_dirstate"),
494 args, PY23("O!O!s#:parse_dirstate", "O!O!y#:parse_dirstate"),
416 &PyDict_Type, &dmap, &PyDict_Type, &cmap, &str, &readlen)) {
495 &PyDict_Type, &dmap, &PyDict_Type, &cmap, &str, &readlen)) {
417 goto quit;
496 goto quit;
418 }
497 }
419
498
420 len = readlen;
499 len = readlen;
421
500
422 /* read parents */
501 /* read parents */
423 if (len < 40) {
502 if (len < 40) {
424 PyErr_SetString(PyExc_ValueError,
503 PyErr_SetString(PyExc_ValueError,
425 "too little data for parents");
504 "too little data for parents");
426 goto quit;
505 goto quit;
427 }
506 }
428
507
429 parents = Py_BuildValue(PY23("s#s#", "y#y#"), str, (Py_ssize_t)20,
508 parents = Py_BuildValue(PY23("s#s#", "y#y#"), str, (Py_ssize_t)20,
430 str + 20, (Py_ssize_t)20);
509 str + 20, (Py_ssize_t)20);
431 if (!parents) {
510 if (!parents) {
432 goto quit;
511 goto quit;
433 }
512 }
434
513
435 /* read filenames */
514 /* read filenames */
436 while (pos >= 40 && pos < len) {
515 while (pos >= 40 && pos < len) {
437 if (pos + 17 > len) {
516 if (pos + 17 > len) {
438 PyErr_SetString(PyExc_ValueError,
517 PyErr_SetString(PyExc_ValueError,
439 "overflow in dirstate");
518 "overflow in dirstate");
440 goto quit;
519 goto quit;
441 }
520 }
442 cur = str + pos;
521 cur = str + pos;
443 /* unpack header */
522 /* unpack header */
444 state = *cur;
523 state = *cur;
445 mode = getbe32(cur + 1);
524 mode = getbe32(cur + 1);
446 size = getbe32(cur + 5);
525 size = getbe32(cur + 5);
447 mtime = getbe32(cur + 9);
526 mtime = getbe32(cur + 9);
448 flen = getbe32(cur + 13);
527 flen = getbe32(cur + 13);
449 pos += 17;
528 pos += 17;
450 cur += 17;
529 cur += 17;
451 if (flen > len - pos) {
530 if (flen > len - pos) {
452 PyErr_SetString(PyExc_ValueError,
531 PyErr_SetString(PyExc_ValueError,
453 "overflow in dirstate");
532 "overflow in dirstate");
454 goto quit;
533 goto quit;
455 }
534 }
456
535
457 entry = (PyObject *)dirstate_item_from_v1_data(state, mode,
536 entry = (PyObject *)dirstate_item_from_v1_data(state, mode,
458 size, mtime);
537 size, mtime);
459 cpos = memchr(cur, 0, flen);
538 cpos = memchr(cur, 0, flen);
460 if (cpos) {
539 if (cpos) {
461 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
540 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
462 cname = PyBytes_FromStringAndSize(
541 cname = PyBytes_FromStringAndSize(
463 cpos + 1, flen - (cpos - cur) - 1);
542 cpos + 1, flen - (cpos - cur) - 1);
464 if (!fname || !cname ||
543 if (!fname || !cname ||
465 PyDict_SetItem(cmap, fname, cname) == -1 ||
544 PyDict_SetItem(cmap, fname, cname) == -1 ||
466 PyDict_SetItem(dmap, fname, entry) == -1) {
545 PyDict_SetItem(dmap, fname, entry) == -1) {
467 goto quit;
546 goto quit;
468 }
547 }
469 Py_DECREF(cname);
548 Py_DECREF(cname);
470 } else {
549 } else {
471 fname = PyBytes_FromStringAndSize(cur, flen);
550 fname = PyBytes_FromStringAndSize(cur, flen);
472 if (!fname ||
551 if (!fname ||
473 PyDict_SetItem(dmap, fname, entry) == -1) {
552 PyDict_SetItem(dmap, fname, entry) == -1) {
474 goto quit;
553 goto quit;
475 }
554 }
476 }
555 }
477 Py_DECREF(fname);
556 Py_DECREF(fname);
478 Py_DECREF(entry);
557 Py_DECREF(entry);
479 fname = cname = entry = NULL;
558 fname = cname = entry = NULL;
480 pos += flen;
559 pos += flen;
481 }
560 }
482
561
483 ret = parents;
562 ret = parents;
484 Py_INCREF(ret);
563 Py_INCREF(ret);
485 quit:
564 quit:
486 Py_XDECREF(fname);
565 Py_XDECREF(fname);
487 Py_XDECREF(cname);
566 Py_XDECREF(cname);
488 Py_XDECREF(entry);
567 Py_XDECREF(entry);
489 Py_XDECREF(parents);
568 Py_XDECREF(parents);
490 return ret;
569 return ret;
491 }
570 }
492
571
493 /*
572 /*
494 * Build a set of non-normal and other parent entries from the dirstate dmap
573 * Build a set of non-normal and other parent entries from the dirstate dmap
495 */
574 */
496 static PyObject *nonnormalotherparententries(PyObject *self, PyObject *args)
575 static PyObject *nonnormalotherparententries(PyObject *self, PyObject *args)
497 {
576 {
498 PyObject *dmap, *fname, *v;
577 PyObject *dmap, *fname, *v;
499 PyObject *nonnset = NULL, *otherpset = NULL, *result = NULL;
578 PyObject *nonnset = NULL, *otherpset = NULL, *result = NULL;
500 Py_ssize_t pos;
579 Py_ssize_t pos;
501
580
502 if (!PyArg_ParseTuple(args, "O!:nonnormalentries", &PyDict_Type,
581 if (!PyArg_ParseTuple(args, "O!:nonnormalentries", &PyDict_Type,
503 &dmap)) {
582 &dmap)) {
504 goto bail;
583 goto bail;
505 }
584 }
506
585
507 nonnset = PySet_New(NULL);
586 nonnset = PySet_New(NULL);
508 if (nonnset == NULL) {
587 if (nonnset == NULL) {
509 goto bail;
588 goto bail;
510 }
589 }
511
590
512 otherpset = PySet_New(NULL);
591 otherpset = PySet_New(NULL);
513 if (otherpset == NULL) {
592 if (otherpset == NULL) {
514 goto bail;
593 goto bail;
515 }
594 }
516
595
517 pos = 0;
596 pos = 0;
518 while (PyDict_Next(dmap, &pos, &fname, &v)) {
597 while (PyDict_Next(dmap, &pos, &fname, &v)) {
519 dirstateItemObject *t;
598 dirstateItemObject *t;
520 if (!dirstate_tuple_check(v)) {
599 if (!dirstate_tuple_check(v)) {
521 PyErr_SetString(PyExc_TypeError,
600 PyErr_SetString(PyExc_TypeError,
522 "expected a dirstate tuple");
601 "expected a dirstate tuple");
523 goto bail;
602 goto bail;
524 }
603 }
525 t = (dirstateItemObject *)v;
604 t = (dirstateItemObject *)v;
526
605
527 if (t->state == 'n' && t->size == -2) {
606 if (t->state == 'n' && t->size == -2) {
528 if (PySet_Add(otherpset, fname) == -1) {
607 if (PySet_Add(otherpset, fname) == -1) {
529 goto bail;
608 goto bail;
530 }
609 }
531 }
610 }
532
611
533 if (t->state == 'n' && t->mtime != -1) {
612 if (t->state == 'n' && t->mtime != -1) {
534 continue;
613 continue;
535 }
614 }
536 if (PySet_Add(nonnset, fname) == -1) {
615 if (PySet_Add(nonnset, fname) == -1) {
537 goto bail;
616 goto bail;
538 }
617 }
539 }
618 }
540
619
541 result = Py_BuildValue("(OO)", nonnset, otherpset);
620 result = Py_BuildValue("(OO)", nonnset, otherpset);
542 if (result == NULL) {
621 if (result == NULL) {
543 goto bail;
622 goto bail;
544 }
623 }
545 Py_DECREF(nonnset);
624 Py_DECREF(nonnset);
546 Py_DECREF(otherpset);
625 Py_DECREF(otherpset);
547 return result;
626 return result;
548 bail:
627 bail:
549 Py_XDECREF(nonnset);
628 Py_XDECREF(nonnset);
550 Py_XDECREF(otherpset);
629 Py_XDECREF(otherpset);
551 Py_XDECREF(result);
630 Py_XDECREF(result);
552 return NULL;
631 return NULL;
553 }
632 }
554
633
555 /*
634 /*
556 * Efficiently pack a dirstate object into its on-disk format.
635 * Efficiently pack a dirstate object into its on-disk format.
557 */
636 */
558 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
637 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
559 {
638 {
560 PyObject *packobj = NULL;
639 PyObject *packobj = NULL;
561 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
640 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
562 Py_ssize_t nbytes, pos, l;
641 Py_ssize_t nbytes, pos, l;
563 PyObject *k, *v = NULL, *pn;
642 PyObject *k, *v = NULL, *pn;
564 char *p, *s;
643 char *p, *s;
565 int now;
644 int now;
566
645
567 if (!PyArg_ParseTuple(args, "O!O!O!i:pack_dirstate", &PyDict_Type, &map,
646 if (!PyArg_ParseTuple(args, "O!O!O!i:pack_dirstate", &PyDict_Type, &map,
568 &PyDict_Type, &copymap, &PyTuple_Type, &pl,
647 &PyDict_Type, &copymap, &PyTuple_Type, &pl,
569 &now)) {
648 &now)) {
570 return NULL;
649 return NULL;
571 }
650 }
572
651
573 if (PyTuple_Size(pl) != 2) {
652 if (PyTuple_Size(pl) != 2) {
574 PyErr_SetString(PyExc_TypeError, "expected 2-element tuple");
653 PyErr_SetString(PyExc_TypeError, "expected 2-element tuple");
575 return NULL;
654 return NULL;
576 }
655 }
577
656
578 /* Figure out how much we need to allocate. */
657 /* Figure out how much we need to allocate. */
579 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
658 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
580 PyObject *c;
659 PyObject *c;
581 if (!PyBytes_Check(k)) {
660 if (!PyBytes_Check(k)) {
582 PyErr_SetString(PyExc_TypeError, "expected string key");
661 PyErr_SetString(PyExc_TypeError, "expected string key");
583 goto bail;
662 goto bail;
584 }
663 }
585 nbytes += PyBytes_GET_SIZE(k) + 17;
664 nbytes += PyBytes_GET_SIZE(k) + 17;
586 c = PyDict_GetItem(copymap, k);
665 c = PyDict_GetItem(copymap, k);
587 if (c) {
666 if (c) {
588 if (!PyBytes_Check(c)) {
667 if (!PyBytes_Check(c)) {
589 PyErr_SetString(PyExc_TypeError,
668 PyErr_SetString(PyExc_TypeError,
590 "expected string key");
669 "expected string key");
591 goto bail;
670 goto bail;
592 }
671 }
593 nbytes += PyBytes_GET_SIZE(c) + 1;
672 nbytes += PyBytes_GET_SIZE(c) + 1;
594 }
673 }
595 }
674 }
596
675
597 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
676 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
598 if (packobj == NULL) {
677 if (packobj == NULL) {
599 goto bail;
678 goto bail;
600 }
679 }
601
680
602 p = PyBytes_AS_STRING(packobj);
681 p = PyBytes_AS_STRING(packobj);
603
682
604 pn = PyTuple_GET_ITEM(pl, 0);
683 pn = PyTuple_GET_ITEM(pl, 0);
605 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
684 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
606 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
685 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
607 goto bail;
686 goto bail;
608 }
687 }
609 memcpy(p, s, l);
688 memcpy(p, s, l);
610 p += 20;
689 p += 20;
611 pn = PyTuple_GET_ITEM(pl, 1);
690 pn = PyTuple_GET_ITEM(pl, 1);
612 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
691 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
613 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
692 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
614 goto bail;
693 goto bail;
615 }
694 }
616 memcpy(p, s, l);
695 memcpy(p, s, l);
617 p += 20;
696 p += 20;
618
697
619 for (pos = 0; PyDict_Next(map, &pos, &k, &v);) {
698 for (pos = 0; PyDict_Next(map, &pos, &k, &v);) {
620 dirstateItemObject *tuple;
699 dirstateItemObject *tuple;
621 char state;
700 char state;
622 int mode, size, mtime;
701 int mode, size, mtime;
623 Py_ssize_t len, l;
702 Py_ssize_t len, l;
624 PyObject *o;
703 PyObject *o;
625 char *t;
704 char *t;
626
705
627 if (!dirstate_tuple_check(v)) {
706 if (!dirstate_tuple_check(v)) {
628 PyErr_SetString(PyExc_TypeError,
707 PyErr_SetString(PyExc_TypeError,
629 "expected a dirstate tuple");
708 "expected a dirstate tuple");
630 goto bail;
709 goto bail;
631 }
710 }
632 tuple = (dirstateItemObject *)v;
711 tuple = (dirstateItemObject *)v;
633
712
634 state = tuple->state;
713 state = tuple->state;
635 mode = tuple->mode;
714 mode = tuple->mode;
636 size = tuple->size;
715 size = tuple->size;
637 mtime = tuple->mtime;
716 mtime = tuple->mtime;
638 if (state == 'n' && mtime == now) {
717 if (state == 'n' && mtime == now) {
639 /* See pure/parsers.py:pack_dirstate for why we do
718 /* See pure/parsers.py:pack_dirstate for why we do
640 * this. */
719 * this. */
641 mtime = -1;
720 mtime = -1;
642 mtime_unset = (PyObject *)make_dirstate_item(
721 mtime_unset = (PyObject *)make_dirstate_item(
643 state, mode, size, mtime);
722 state, mode, size, mtime);
644 if (!mtime_unset) {
723 if (!mtime_unset) {
645 goto bail;
724 goto bail;
646 }
725 }
647 if (PyDict_SetItem(map, k, mtime_unset) == -1) {
726 if (PyDict_SetItem(map, k, mtime_unset) == -1) {
648 goto bail;
727 goto bail;
649 }
728 }
650 Py_DECREF(mtime_unset);
729 Py_DECREF(mtime_unset);
651 mtime_unset = NULL;
730 mtime_unset = NULL;
652 }
731 }
653 *p++ = state;
732 *p++ = state;
654 putbe32((uint32_t)mode, p);
733 putbe32((uint32_t)mode, p);
655 putbe32((uint32_t)size, p + 4);
734 putbe32((uint32_t)size, p + 4);
656 putbe32((uint32_t)mtime, p + 8);
735 putbe32((uint32_t)mtime, p + 8);
657 t = p + 12;
736 t = p + 12;
658 p += 16;
737 p += 16;
659 len = PyBytes_GET_SIZE(k);
738 len = PyBytes_GET_SIZE(k);
660 memcpy(p, PyBytes_AS_STRING(k), len);
739 memcpy(p, PyBytes_AS_STRING(k), len);
661 p += len;
740 p += len;
662 o = PyDict_GetItem(copymap, k);
741 o = PyDict_GetItem(copymap, k);
663 if (o) {
742 if (o) {
664 *p++ = '\0';
743 *p++ = '\0';
665 l = PyBytes_GET_SIZE(o);
744 l = PyBytes_GET_SIZE(o);
666 memcpy(p, PyBytes_AS_STRING(o), l);
745 memcpy(p, PyBytes_AS_STRING(o), l);
667 p += l;
746 p += l;
668 len += l + 1;
747 len += l + 1;
669 }
748 }
670 putbe32((uint32_t)len, t);
749 putbe32((uint32_t)len, t);
671 }
750 }
672
751
673 pos = p - PyBytes_AS_STRING(packobj);
752 pos = p - PyBytes_AS_STRING(packobj);
674 if (pos != nbytes) {
753 if (pos != nbytes) {
675 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
754 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
676 (long)pos, (long)nbytes);
755 (long)pos, (long)nbytes);
677 goto bail;
756 goto bail;
678 }
757 }
679
758
680 return packobj;
759 return packobj;
681 bail:
760 bail:
682 Py_XDECREF(mtime_unset);
761 Py_XDECREF(mtime_unset);
683 Py_XDECREF(packobj);
762 Py_XDECREF(packobj);
684 Py_XDECREF(v);
763 Py_XDECREF(v);
685 return NULL;
764 return NULL;
686 }
765 }
687
766
688 #define BUMPED_FIX 1
767 #define BUMPED_FIX 1
689 #define USING_SHA_256 2
768 #define USING_SHA_256 2
690 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
769 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
691
770
692 static PyObject *readshas(const char *source, unsigned char num,
771 static PyObject *readshas(const char *source, unsigned char num,
693 Py_ssize_t hashwidth)
772 Py_ssize_t hashwidth)
694 {
773 {
695 int i;
774 int i;
696 PyObject *list = PyTuple_New(num);
775 PyObject *list = PyTuple_New(num);
697 if (list == NULL) {
776 if (list == NULL) {
698 return NULL;
777 return NULL;
699 }
778 }
700 for (i = 0; i < num; i++) {
779 for (i = 0; i < num; i++) {
701 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
780 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
702 if (hash == NULL) {
781 if (hash == NULL) {
703 Py_DECREF(list);
782 Py_DECREF(list);
704 return NULL;
783 return NULL;
705 }
784 }
706 PyTuple_SET_ITEM(list, i, hash);
785 PyTuple_SET_ITEM(list, i, hash);
707 source += hashwidth;
786 source += hashwidth;
708 }
787 }
709 return list;
788 return list;
710 }
789 }
711
790
712 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
791 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
713 uint32_t *msize)
792 uint32_t *msize)
714 {
793 {
715 const char *data = databegin;
794 const char *data = databegin;
716 const char *meta;
795 const char *meta;
717
796
718 double mtime;
797 double mtime;
719 int16_t tz;
798 int16_t tz;
720 uint16_t flags;
799 uint16_t flags;
721 unsigned char nsuccs, nparents, nmetadata;
800 unsigned char nsuccs, nparents, nmetadata;
722 Py_ssize_t hashwidth = 20;
801 Py_ssize_t hashwidth = 20;
723
802
724 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
803 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
725 PyObject *metadata = NULL, *ret = NULL;
804 PyObject *metadata = NULL, *ret = NULL;
726 int i;
805 int i;
727
806
728 if (data + FM1_HEADER_SIZE > dataend) {
807 if (data + FM1_HEADER_SIZE > dataend) {
729 goto overflow;
808 goto overflow;
730 }
809 }
731
810
732 *msize = getbe32(data);
811 *msize = getbe32(data);
733 data += 4;
812 data += 4;
734 mtime = getbefloat64(data);
813 mtime = getbefloat64(data);
735 data += 8;
814 data += 8;
736 tz = getbeint16(data);
815 tz = getbeint16(data);
737 data += 2;
816 data += 2;
738 flags = getbeuint16(data);
817 flags = getbeuint16(data);
739 data += 2;
818 data += 2;
740
819
741 if (flags & USING_SHA_256) {
820 if (flags & USING_SHA_256) {
742 hashwidth = 32;
821 hashwidth = 32;
743 }
822 }
744
823
745 nsuccs = (unsigned char)(*data++);
824 nsuccs = (unsigned char)(*data++);
746 nparents = (unsigned char)(*data++);
825 nparents = (unsigned char)(*data++);
747 nmetadata = (unsigned char)(*data++);
826 nmetadata = (unsigned char)(*data++);
748
827
749 if (databegin + *msize > dataend) {
828 if (databegin + *msize > dataend) {
750 goto overflow;
829 goto overflow;
751 }
830 }
752 dataend = databegin + *msize; /* narrow down to marker size */
831 dataend = databegin + *msize; /* narrow down to marker size */
753
832
754 if (data + hashwidth > dataend) {
833 if (data + hashwidth > dataend) {
755 goto overflow;
834 goto overflow;
756 }
835 }
757 prec = PyBytes_FromStringAndSize(data, hashwidth);
836 prec = PyBytes_FromStringAndSize(data, hashwidth);
758 data += hashwidth;
837 data += hashwidth;
759 if (prec == NULL) {
838 if (prec == NULL) {
760 goto bail;
839 goto bail;
761 }
840 }
762
841
763 if (data + nsuccs * hashwidth > dataend) {
842 if (data + nsuccs * hashwidth > dataend) {
764 goto overflow;
843 goto overflow;
765 }
844 }
766 succs = readshas(data, nsuccs, hashwidth);
845 succs = readshas(data, nsuccs, hashwidth);
767 if (succs == NULL) {
846 if (succs == NULL) {
768 goto bail;
847 goto bail;
769 }
848 }
770 data += nsuccs * hashwidth;
849 data += nsuccs * hashwidth;
771
850
772 if (nparents == 1 || nparents == 2) {
851 if (nparents == 1 || nparents == 2) {
773 if (data + nparents * hashwidth > dataend) {
852 if (data + nparents * hashwidth > dataend) {
774 goto overflow;
853 goto overflow;
775 }
854 }
776 parents = readshas(data, nparents, hashwidth);
855 parents = readshas(data, nparents, hashwidth);
777 if (parents == NULL) {
856 if (parents == NULL) {
778 goto bail;
857 goto bail;
779 }
858 }
780 data += nparents * hashwidth;
859 data += nparents * hashwidth;
781 } else {
860 } else {
782 parents = Py_None;
861 parents = Py_None;
783 Py_INCREF(parents);
862 Py_INCREF(parents);
784 }
863 }
785
864
786 if (data + 2 * nmetadata > dataend) {
865 if (data + 2 * nmetadata > dataend) {
787 goto overflow;
866 goto overflow;
788 }
867 }
789 meta = data + (2 * nmetadata);
868 meta = data + (2 * nmetadata);
790 metadata = PyTuple_New(nmetadata);
869 metadata = PyTuple_New(nmetadata);
791 if (metadata == NULL) {
870 if (metadata == NULL) {
792 goto bail;
871 goto bail;
793 }
872 }
794 for (i = 0; i < nmetadata; i++) {
873 for (i = 0; i < nmetadata; i++) {
795 PyObject *tmp, *left = NULL, *right = NULL;
874 PyObject *tmp, *left = NULL, *right = NULL;
796 Py_ssize_t leftsize = (unsigned char)(*data++);
875 Py_ssize_t leftsize = (unsigned char)(*data++);
797 Py_ssize_t rightsize = (unsigned char)(*data++);
876 Py_ssize_t rightsize = (unsigned char)(*data++);
798 if (meta + leftsize + rightsize > dataend) {
877 if (meta + leftsize + rightsize > dataend) {
799 goto overflow;
878 goto overflow;
800 }
879 }
801 left = PyBytes_FromStringAndSize(meta, leftsize);
880 left = PyBytes_FromStringAndSize(meta, leftsize);
802 meta += leftsize;
881 meta += leftsize;
803 right = PyBytes_FromStringAndSize(meta, rightsize);
882 right = PyBytes_FromStringAndSize(meta, rightsize);
804 meta += rightsize;
883 meta += rightsize;
805 tmp = PyTuple_New(2);
884 tmp = PyTuple_New(2);
806 if (!left || !right || !tmp) {
885 if (!left || !right || !tmp) {
807 Py_XDECREF(left);
886 Py_XDECREF(left);
808 Py_XDECREF(right);
887 Py_XDECREF(right);
809 Py_XDECREF(tmp);
888 Py_XDECREF(tmp);
810 goto bail;
889 goto bail;
811 }
890 }
812 PyTuple_SET_ITEM(tmp, 0, left);
891 PyTuple_SET_ITEM(tmp, 0, left);
813 PyTuple_SET_ITEM(tmp, 1, right);
892 PyTuple_SET_ITEM(tmp, 1, right);
814 PyTuple_SET_ITEM(metadata, i, tmp);
893 PyTuple_SET_ITEM(metadata, i, tmp);
815 }
894 }
816 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags, metadata, mtime,
895 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags, metadata, mtime,
817 (int)tz * 60, parents);
896 (int)tz * 60, parents);
818 goto bail; /* return successfully */
897 goto bail; /* return successfully */
819
898
820 overflow:
899 overflow:
821 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
900 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
822 bail:
901 bail:
823 Py_XDECREF(prec);
902 Py_XDECREF(prec);
824 Py_XDECREF(succs);
903 Py_XDECREF(succs);
825 Py_XDECREF(metadata);
904 Py_XDECREF(metadata);
826 Py_XDECREF(parents);
905 Py_XDECREF(parents);
827 return ret;
906 return ret;
828 }
907 }
829
908
830 static PyObject *fm1readmarkers(PyObject *self, PyObject *args)
909 static PyObject *fm1readmarkers(PyObject *self, PyObject *args)
831 {
910 {
832 const char *data, *dataend;
911 const char *data, *dataend;
833 Py_ssize_t datalen, offset, stop;
912 Py_ssize_t datalen, offset, stop;
834 PyObject *markers = NULL;
913 PyObject *markers = NULL;
835
914
836 if (!PyArg_ParseTuple(args, PY23("s#nn", "y#nn"), &data, &datalen,
915 if (!PyArg_ParseTuple(args, PY23("s#nn", "y#nn"), &data, &datalen,
837 &offset, &stop)) {
916 &offset, &stop)) {
838 return NULL;
917 return NULL;
839 }
918 }
840 if (offset < 0) {
919 if (offset < 0) {
841 PyErr_SetString(PyExc_ValueError,
920 PyErr_SetString(PyExc_ValueError,
842 "invalid negative offset in fm1readmarkers");
921 "invalid negative offset in fm1readmarkers");
843 return NULL;
922 return NULL;
844 }
923 }
845 if (stop > datalen) {
924 if (stop > datalen) {
846 PyErr_SetString(
925 PyErr_SetString(
847 PyExc_ValueError,
926 PyExc_ValueError,
848 "stop longer than data length in fm1readmarkers");
927 "stop longer than data length in fm1readmarkers");
849 return NULL;
928 return NULL;
850 }
929 }
851 dataend = data + datalen;
930 dataend = data + datalen;
852 data += offset;
931 data += offset;
853 markers = PyList_New(0);
932 markers = PyList_New(0);
854 if (!markers) {
933 if (!markers) {
855 return NULL;
934 return NULL;
856 }
935 }
857 while (offset < stop) {
936 while (offset < stop) {
858 uint32_t msize;
937 uint32_t msize;
859 int error;
938 int error;
860 PyObject *record = fm1readmarker(data, dataend, &msize);
939 PyObject *record = fm1readmarker(data, dataend, &msize);
861 if (!record) {
940 if (!record) {
862 goto bail;
941 goto bail;
863 }
942 }
864 error = PyList_Append(markers, record);
943 error = PyList_Append(markers, record);
865 Py_DECREF(record);
944 Py_DECREF(record);
866 if (error) {
945 if (error) {
867 goto bail;
946 goto bail;
868 }
947 }
869 data += msize;
948 data += msize;
870 offset += msize;
949 offset += msize;
871 }
950 }
872 return markers;
951 return markers;
873 bail:
952 bail:
874 Py_DECREF(markers);
953 Py_DECREF(markers);
875 return NULL;
954 return NULL;
876 }
955 }
877
956
878 static char parsers_doc[] = "Efficient content parsing.";
957 static char parsers_doc[] = "Efficient content parsing.";
879
958
880 PyObject *encodedir(PyObject *self, PyObject *args);
959 PyObject *encodedir(PyObject *self, PyObject *args);
881 PyObject *pathencode(PyObject *self, PyObject *args);
960 PyObject *pathencode(PyObject *self, PyObject *args);
882 PyObject *lowerencode(PyObject *self, PyObject *args);
961 PyObject *lowerencode(PyObject *self, PyObject *args);
883 PyObject *parse_index2(PyObject *self, PyObject *args, PyObject *kwargs);
962 PyObject *parse_index2(PyObject *self, PyObject *args, PyObject *kwargs);
884
963
885 static PyMethodDef methods[] = {
964 static PyMethodDef methods[] = {
886 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
965 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
887 {"nonnormalotherparententries", nonnormalotherparententries, METH_VARARGS,
966 {"nonnormalotherparententries", nonnormalotherparententries, METH_VARARGS,
888 "create a set containing non-normal and other parent entries of given "
967 "create a set containing non-normal and other parent entries of given "
889 "dirstate\n"},
968 "dirstate\n"},
890 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
969 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
891 {"parse_index2", (PyCFunction)parse_index2, METH_VARARGS | METH_KEYWORDS,
970 {"parse_index2", (PyCFunction)parse_index2, METH_VARARGS | METH_KEYWORDS,
892 "parse a revlog index\n"},
971 "parse a revlog index\n"},
893 {"isasciistr", isasciistr, METH_VARARGS, "check if an ASCII string\n"},
972 {"isasciistr", isasciistr, METH_VARARGS, "check if an ASCII string\n"},
894 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
973 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
895 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
974 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
896 {"dict_new_presized", dict_new_presized, METH_VARARGS,
975 {"dict_new_presized", dict_new_presized, METH_VARARGS,
897 "construct a dict with an expected size\n"},
976 "construct a dict with an expected size\n"},
898 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
977 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
899 "make file foldmap\n"},
978 "make file foldmap\n"},
900 {"jsonescapeu8fast", jsonescapeu8fast, METH_VARARGS,
979 {"jsonescapeu8fast", jsonescapeu8fast, METH_VARARGS,
901 "escape a UTF-8 byte string to JSON (fast path)\n"},
980 "escape a UTF-8 byte string to JSON (fast path)\n"},
902 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
981 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
903 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
982 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
904 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
983 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
905 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
984 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
906 "parse v1 obsolete markers\n"},
985 "parse v1 obsolete markers\n"},
907 {NULL, NULL}};
986 {NULL, NULL}};
908
987
909 void dirs_module_init(PyObject *mod);
988 void dirs_module_init(PyObject *mod);
910 void manifest_module_init(PyObject *mod);
989 void manifest_module_init(PyObject *mod);
911 void revlog_module_init(PyObject *mod);
990 void revlog_module_init(PyObject *mod);
912
991
913 static const int version = 20;
992 static const int version = 20;
914
993
915 static void module_init(PyObject *mod)
994 static void module_init(PyObject *mod)
916 {
995 {
917 PyObject *capsule = NULL;
996 PyObject *capsule = NULL;
918 PyModule_AddIntConstant(mod, "version", version);
997 PyModule_AddIntConstant(mod, "version", version);
919
998
920 /* This module constant has two purposes. First, it lets us unit test
999 /* This module constant has two purposes. First, it lets us unit test
921 * the ImportError raised without hard-coding any error text. This
1000 * the ImportError raised without hard-coding any error text. This
922 * means we can change the text in the future without breaking tests,
1001 * means we can change the text in the future without breaking tests,
923 * even across changesets without a recompile. Second, its presence
1002 * even across changesets without a recompile. Second, its presence
924 * can be used to determine whether the version-checking logic is
1003 * can be used to determine whether the version-checking logic is
925 * present, which also helps in testing across changesets without a
1004 * present, which also helps in testing across changesets without a
926 * recompile. Note that this means the pure-Python version of parsers
1005 * recompile. Note that this means the pure-Python version of parsers
927 * should not have this module constant. */
1006 * should not have this module constant. */
928 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
1007 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
929
1008
930 dirs_module_init(mod);
1009 dirs_module_init(mod);
931 manifest_module_init(mod);
1010 manifest_module_init(mod);
932 revlog_module_init(mod);
1011 revlog_module_init(mod);
933
1012
934 capsule = PyCapsule_New(
1013 capsule = PyCapsule_New(
935 make_dirstate_item,
1014 make_dirstate_item,
936 "mercurial.cext.parsers.make_dirstate_item_CAPI", NULL);
1015 "mercurial.cext.parsers.make_dirstate_item_CAPI", NULL);
937 if (capsule != NULL)
1016 if (capsule != NULL)
938 PyModule_AddObject(mod, "make_dirstate_item_CAPI", capsule);
1017 PyModule_AddObject(mod, "make_dirstate_item_CAPI", capsule);
939
1018
940 if (PyType_Ready(&dirstateItemType) < 0) {
1019 if (PyType_Ready(&dirstateItemType) < 0) {
941 return;
1020 return;
942 }
1021 }
943 Py_INCREF(&dirstateItemType);
1022 Py_INCREF(&dirstateItemType);
944 PyModule_AddObject(mod, "DirstateItem", (PyObject *)&dirstateItemType);
1023 PyModule_AddObject(mod, "DirstateItem", (PyObject *)&dirstateItemType);
945 }
1024 }
946
1025
947 static int check_python_version(void)
1026 static int check_python_version(void)
948 {
1027 {
949 PyObject *sys = PyImport_ImportModule("sys"), *ver;
1028 PyObject *sys = PyImport_ImportModule("sys"), *ver;
950 long hexversion;
1029 long hexversion;
951 if (!sys) {
1030 if (!sys) {
952 return -1;
1031 return -1;
953 }
1032 }
954 ver = PyObject_GetAttrString(sys, "hexversion");
1033 ver = PyObject_GetAttrString(sys, "hexversion");
955 Py_DECREF(sys);
1034 Py_DECREF(sys);
956 if (!ver) {
1035 if (!ver) {
957 return -1;
1036 return -1;
958 }
1037 }
959 hexversion = PyInt_AsLong(ver);
1038 hexversion = PyInt_AsLong(ver);
960 Py_DECREF(ver);
1039 Py_DECREF(ver);
961 /* sys.hexversion is a 32-bit number by default, so the -1 case
1040 /* sys.hexversion is a 32-bit number by default, so the -1 case
962 * should only occur in unusual circumstances (e.g. if sys.hexversion
1041 * should only occur in unusual circumstances (e.g. if sys.hexversion
963 * is manually set to an invalid value). */
1042 * is manually set to an invalid value). */
964 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
1043 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
965 PyErr_Format(PyExc_ImportError,
1044 PyErr_Format(PyExc_ImportError,
966 "%s: The Mercurial extension "
1045 "%s: The Mercurial extension "
967 "modules were compiled with Python " PY_VERSION
1046 "modules were compiled with Python " PY_VERSION
968 ", but "
1047 ", but "
969 "Mercurial is currently using Python with "
1048 "Mercurial is currently using Python with "
970 "sys.hexversion=%ld: "
1049 "sys.hexversion=%ld: "
971 "Python %s\n at: %s",
1050 "Python %s\n at: %s",
972 versionerrortext, hexversion, Py_GetVersion(),
1051 versionerrortext, hexversion, Py_GetVersion(),
973 Py_GetProgramFullPath());
1052 Py_GetProgramFullPath());
974 return -1;
1053 return -1;
975 }
1054 }
976 return 0;
1055 return 0;
977 }
1056 }
978
1057
979 #ifdef IS_PY3K
1058 #ifdef IS_PY3K
980 static struct PyModuleDef parsers_module = {PyModuleDef_HEAD_INIT, "parsers",
1059 static struct PyModuleDef parsers_module = {PyModuleDef_HEAD_INIT, "parsers",
981 parsers_doc, -1, methods};
1060 parsers_doc, -1, methods};
982
1061
983 PyMODINIT_FUNC PyInit_parsers(void)
1062 PyMODINIT_FUNC PyInit_parsers(void)
984 {
1063 {
985 PyObject *mod;
1064 PyObject *mod;
986
1065
987 if (check_python_version() == -1)
1066 if (check_python_version() == -1)
988 return NULL;
1067 return NULL;
989 mod = PyModule_Create(&parsers_module);
1068 mod = PyModule_Create(&parsers_module);
990 module_init(mod);
1069 module_init(mod);
991 return mod;
1070 return mod;
992 }
1071 }
993 #else
1072 #else
994 PyMODINIT_FUNC initparsers(void)
1073 PyMODINIT_FUNC initparsers(void)
995 {
1074 {
996 PyObject *mod;
1075 PyObject *mod;
997
1076
998 if (check_python_version() == -1) {
1077 if (check_python_version() == -1) {
999 return;
1078 return;
1000 }
1079 }
1001 mod = Py_InitModule3("parsers", methods, parsers_doc);
1080 mod = Py_InitModule3("parsers", methods, parsers_doc);
1002 module_init(mod);
1081 module_init(mod);
1003 }
1082 }
1004 #endif
1083 #endif
@@ -1,637 +1,688 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 _state = attr.ib()
59 _state = attr.ib()
60 _mode = attr.ib()
60 _mode = attr.ib()
61 _size = attr.ib()
61 _size = attr.ib()
62 _mtime = attr.ib()
62 _mtime = attr.ib()
63
63
64 def __init__(self, state, mode, size, mtime):
64 def __init__(
65 self._state = state
65 self,
66 self._mode = mode
66 wc_tracked=False,
67 self._size = size
67 p1_tracked=False,
68 self._mtime = mtime
68 p2_tracked=False,
69 merged=False,
70 clean_p1=False,
71 clean_p2=False,
72 possibly_dirty=False,
73 parentfiledata=None,
74 ):
75 if merged and (clean_p1 or clean_p2):
76 msg = b'`merged` argument incompatible with `clean_p1`/`clean_p2`'
77 raise error.ProgrammingError(msg)
78
79 self._state = None
80 self._mode = 0
81 self._size = NONNORMAL
82 self._mtime = AMBIGUOUS_TIME
83 if not (p1_tracked or p2_tracked or wc_tracked):
84 pass # the object has no state to record
85 elif merged:
86 self._state = b'm'
87 self._size = FROM_P2
88 self._mtime = AMBIGUOUS_TIME
89 elif not (p1_tracked or p2_tracked) and wc_tracked:
90 self._state = b'a'
91 self._size = NONNORMAL
92 self._mtime = AMBIGUOUS_TIME
93 elif (p1_tracked or p2_tracked) and not wc_tracked:
94 self._state = b'r'
95 self._size = 0
96 self._mtime = 0
97 elif clean_p2 and wc_tracked:
98 self._state = b'n'
99 self._size = FROM_P2
100 self._mtime = AMBIGUOUS_TIME
101 elif not p1_tracked and p2_tracked and wc_tracked:
102 self._state = b'n'
103 self._size = FROM_P2
104 self._mtime = AMBIGUOUS_TIME
105 elif possibly_dirty:
106 self._state = b'n'
107 self._size = NONNORMAL
108 self._mtime = AMBIGUOUS_TIME
109 elif wc_tracked:
110 # this is a "normal" file
111 if parentfiledata is None:
112 msg = b'failed to pass parentfiledata for a normal file'
113 raise error.ProgrammingError(msg)
114 self._state = b'n'
115 self._mode = parentfiledata[0]
116 self._size = parentfiledata[1]
117 self._mtime = parentfiledata[2]
118 else:
119 assert False, 'unreachable'
69
120
70 @classmethod
121 @classmethod
71 def from_v1_data(cls, state, mode, size, mtime):
122 def from_v1_data(cls, state, mode, size, mtime):
72 """Build a new DirstateItem object from V1 data
123 """Build a new DirstateItem object from V1 data
73
124
74 Since the dirstate-v1 format is frozen, the signature of this function
125 Since the dirstate-v1 format is frozen, the signature of this function
75 is not expected to change, unlike the __init__ one.
126 is not expected to change, unlike the __init__ one.
76 """
127 """
77 return cls(
128 instance = cls()
78 state=state,
129 instance._state = state
79 mode=mode,
130 instance._mode = mode
80 size=size,
131 instance._size = size
81 mtime=mtime,
132 instance._mtime = mtime
82 )
133 return instance
83
134
84 def set_possibly_dirty(self):
135 def set_possibly_dirty(self):
85 """Mark a file as "possibly dirty"
136 """Mark a file as "possibly dirty"
86
137
87 This means the next status call will have to actually check its content
138 This means the next status call will have to actually check its content
88 to make sure it is correct.
139 to make sure it is correct.
89 """
140 """
90 self._mtime = AMBIGUOUS_TIME
141 self._mtime = AMBIGUOUS_TIME
91
142
92 def set_untracked(self):
143 def set_untracked(self):
93 """mark a file as untracked in the working copy
144 """mark a file as untracked in the working copy
94
145
95 This will ultimately be called by command like `hg remove`.
146 This will ultimately be called by command like `hg remove`.
96 """
147 """
97 # backup the previous state (useful for merge)
148 # backup the previous state (useful for merge)
98 size = 0
149 size = 0
99 if self.merged: # merge
150 if self.merged: # merge
100 size = NONNORMAL
151 size = NONNORMAL
101 elif self.from_p2:
152 elif self.from_p2:
102 size = FROM_P2
153 size = FROM_P2
103 self._state = b'r'
154 self._state = b'r'
104 self._mode = 0
155 self._mode = 0
105 self._size = size
156 self._size = size
106 self._mtime = 0
157 self._mtime = 0
107
158
108 def __getitem__(self, idx):
159 def __getitem__(self, idx):
109 if idx == 0 or idx == -4:
160 if idx == 0 or idx == -4:
110 msg = b"do not use item[x], use item.state"
161 msg = b"do not use item[x], use item.state"
111 util.nouideprecwarn(msg, b'6.0', stacklevel=2)
162 util.nouideprecwarn(msg, b'6.0', stacklevel=2)
112 return self._state
163 return self._state
113 elif idx == 1 or idx == -3:
164 elif idx == 1 or idx == -3:
114 msg = b"do not use item[x], use item.mode"
165 msg = b"do not use item[x], use item.mode"
115 util.nouideprecwarn(msg, b'6.0', stacklevel=2)
166 util.nouideprecwarn(msg, b'6.0', stacklevel=2)
116 return self._mode
167 return self._mode
117 elif idx == 2 or idx == -2:
168 elif idx == 2 or idx == -2:
118 msg = b"do not use item[x], use item.size"
169 msg = b"do not use item[x], use item.size"
119 util.nouideprecwarn(msg, b'6.0', stacklevel=2)
170 util.nouideprecwarn(msg, b'6.0', stacklevel=2)
120 return self._size
171 return self._size
121 elif idx == 3 or idx == -1:
172 elif idx == 3 or idx == -1:
122 msg = b"do not use item[x], use item.mtime"
173 msg = b"do not use item[x], use item.mtime"
123 util.nouideprecwarn(msg, b'6.0', stacklevel=2)
174 util.nouideprecwarn(msg, b'6.0', stacklevel=2)
124 return self._mtime
175 return self._mtime
125 else:
176 else:
126 raise IndexError(idx)
177 raise IndexError(idx)
127
178
128 @property
179 @property
129 def mode(self):
180 def mode(self):
130 return self._mode
181 return self._mode
131
182
132 @property
183 @property
133 def size(self):
184 def size(self):
134 return self._size
185 return self._size
135
186
136 @property
187 @property
137 def mtime(self):
188 def mtime(self):
138 return self._mtime
189 return self._mtime
139
190
140 @property
191 @property
141 def state(self):
192 def state(self):
142 """
193 """
143 States are:
194 States are:
144 n normal
195 n normal
145 m needs merging
196 m needs merging
146 r marked for removal
197 r marked for removal
147 a marked for addition
198 a marked for addition
148
199
149 XXX This "state" is a bit obscure and mostly a direct expression of the
200 XXX This "state" is a bit obscure and mostly a direct expression of the
150 dirstatev1 format. It would make sense to ultimately deprecate it in
201 dirstatev1 format. It would make sense to ultimately deprecate it in
151 favor of the more "semantic" attributes.
202 favor of the more "semantic" attributes.
152 """
203 """
153 return self._state
204 return self._state
154
205
155 @property
206 @property
156 def tracked(self):
207 def tracked(self):
157 """True is the file is tracked in the working copy"""
208 """True is the file is tracked in the working copy"""
158 return self._state in b"nma"
209 return self._state in b"nma"
159
210
160 @property
211 @property
161 def added(self):
212 def added(self):
162 """True if the file has been added"""
213 """True if the file has been added"""
163 return self._state == b'a'
214 return self._state == b'a'
164
215
165 @property
216 @property
166 def merged(self):
217 def merged(self):
167 """True if the file has been merged
218 """True if the file has been merged
168
219
169 Should only be set if a merge is in progress in the dirstate
220 Should only be set if a merge is in progress in the dirstate
170 """
221 """
171 return self._state == b'm'
222 return self._state == b'm'
172
223
173 @property
224 @property
174 def from_p2(self):
225 def from_p2(self):
175 """True if the file have been fetched from p2 during the current merge
226 """True if the file have been fetched from p2 during the current merge
176
227
177 This is only True is the file is currently tracked.
228 This is only True is the file is currently tracked.
178
229
179 Should only be set if a merge is in progress in the dirstate
230 Should only be set if a merge is in progress in the dirstate
180 """
231 """
181 return self._state == b'n' and self._size == FROM_P2
232 return self._state == b'n' and self._size == FROM_P2
182
233
183 @property
234 @property
184 def from_p2_removed(self):
235 def from_p2_removed(self):
185 """True if the file has been removed, but was "from_p2" initially
236 """True if the file has been removed, but was "from_p2" initially
186
237
187 This property seems like an abstraction leakage and should probably be
238 This property seems like an abstraction leakage and should probably be
188 dealt in this class (or maybe the dirstatemap) directly.
239 dealt in this class (or maybe the dirstatemap) directly.
189 """
240 """
190 return self._state == b'r' and self._size == FROM_P2
241 return self._state == b'r' and self._size == FROM_P2
191
242
192 @property
243 @property
193 def removed(self):
244 def removed(self):
194 """True if the file has been removed"""
245 """True if the file has been removed"""
195 return self._state == b'r'
246 return self._state == b'r'
196
247
197 @property
248 @property
198 def merged_removed(self):
249 def merged_removed(self):
199 """True if the file has been removed, but was "merged" initially
250 """True if the file has been removed, but was "merged" initially
200
251
201 This property seems like an abstraction leakage and should probably be
252 This property seems like an abstraction leakage and should probably be
202 dealt in this class (or maybe the dirstatemap) directly.
253 dealt in this class (or maybe the dirstatemap) directly.
203 """
254 """
204 return self._state == b'r' and self._size == NONNORMAL
255 return self._state == b'r' and self._size == NONNORMAL
205
256
206 @property
257 @property
207 def dm_nonnormal(self):
258 def dm_nonnormal(self):
208 """True is the entry is non-normal in the dirstatemap sense
259 """True is the entry is non-normal in the dirstatemap sense
209
260
210 There is no reason for any code, but the dirstatemap one to use this.
261 There is no reason for any code, but the dirstatemap one to use this.
211 """
262 """
212 return self.state != b'n' or self.mtime == AMBIGUOUS_TIME
263 return self.state != b'n' or self.mtime == AMBIGUOUS_TIME
213
264
214 @property
265 @property
215 def dm_otherparent(self):
266 def dm_otherparent(self):
216 """True is the entry is `otherparent` in the dirstatemap sense
267 """True is the entry is `otherparent` in the dirstatemap sense
217
268
218 There is no reason for any code, but the dirstatemap one to use this.
269 There is no reason for any code, but the dirstatemap one to use this.
219 """
270 """
220 return self._size == FROM_P2
271 return self._size == FROM_P2
221
272
222 def v1_state(self):
273 def v1_state(self):
223 """return a "state" suitable for v1 serialization"""
274 """return a "state" suitable for v1 serialization"""
224 return self._state
275 return self._state
225
276
226 def v1_mode(self):
277 def v1_mode(self):
227 """return a "mode" suitable for v1 serialization"""
278 """return a "mode" suitable for v1 serialization"""
228 return self._mode
279 return self._mode
229
280
230 def v1_size(self):
281 def v1_size(self):
231 """return a "size" suitable for v1 serialization"""
282 """return a "size" suitable for v1 serialization"""
232 return self._size
283 return self._size
233
284
234 def v1_mtime(self):
285 def v1_mtime(self):
235 """return a "mtime" suitable for v1 serialization"""
286 """return a "mtime" suitable for v1 serialization"""
236 return self._mtime
287 return self._mtime
237
288
238 def need_delay(self, now):
289 def need_delay(self, now):
239 """True if the stored mtime would be ambiguous with the current time"""
290 """True if the stored mtime would be ambiguous with the current time"""
240 return self._state == b'n' and self._mtime == now
291 return self._state == b'n' and self._mtime == now
241
292
242
293
243 def gettype(q):
294 def gettype(q):
244 return int(q & 0xFFFF)
295 return int(q & 0xFFFF)
245
296
246
297
247 class BaseIndexObject(object):
298 class BaseIndexObject(object):
248 # Can I be passed to an algorithme implemented in Rust ?
299 # Can I be passed to an algorithme implemented in Rust ?
249 rust_ext_compat = 0
300 rust_ext_compat = 0
250 # Format of an index entry according to Python's `struct` language
301 # Format of an index entry according to Python's `struct` language
251 index_format = revlog_constants.INDEX_ENTRY_V1
302 index_format = revlog_constants.INDEX_ENTRY_V1
252 # Size of a C unsigned long long int, platform independent
303 # Size of a C unsigned long long int, platform independent
253 big_int_size = struct.calcsize(b'>Q')
304 big_int_size = struct.calcsize(b'>Q')
254 # Size of a C long int, platform independent
305 # Size of a C long int, platform independent
255 int_size = struct.calcsize(b'>i')
306 int_size = struct.calcsize(b'>i')
256 # An empty index entry, used as a default value to be overridden, or nullrev
307 # An empty index entry, used as a default value to be overridden, or nullrev
257 null_item = (
308 null_item = (
258 0,
309 0,
259 0,
310 0,
260 0,
311 0,
261 -1,
312 -1,
262 -1,
313 -1,
263 -1,
314 -1,
264 -1,
315 -1,
265 sha1nodeconstants.nullid,
316 sha1nodeconstants.nullid,
266 0,
317 0,
267 0,
318 0,
268 revlog_constants.COMP_MODE_INLINE,
319 revlog_constants.COMP_MODE_INLINE,
269 revlog_constants.COMP_MODE_INLINE,
320 revlog_constants.COMP_MODE_INLINE,
270 )
321 )
271
322
272 @util.propertycache
323 @util.propertycache
273 def entry_size(self):
324 def entry_size(self):
274 return self.index_format.size
325 return self.index_format.size
275
326
276 @property
327 @property
277 def nodemap(self):
328 def nodemap(self):
278 msg = b"index.nodemap is deprecated, use index.[has_node|rev|get_rev]"
329 msg = b"index.nodemap is deprecated, use index.[has_node|rev|get_rev]"
279 util.nouideprecwarn(msg, b'5.3', stacklevel=2)
330 util.nouideprecwarn(msg, b'5.3', stacklevel=2)
280 return self._nodemap
331 return self._nodemap
281
332
282 @util.propertycache
333 @util.propertycache
283 def _nodemap(self):
334 def _nodemap(self):
284 nodemap = nodemaputil.NodeMap({sha1nodeconstants.nullid: nullrev})
335 nodemap = nodemaputil.NodeMap({sha1nodeconstants.nullid: nullrev})
285 for r in range(0, len(self)):
336 for r in range(0, len(self)):
286 n = self[r][7]
337 n = self[r][7]
287 nodemap[n] = r
338 nodemap[n] = r
288 return nodemap
339 return nodemap
289
340
290 def has_node(self, node):
341 def has_node(self, node):
291 """return True if the node exist in the index"""
342 """return True if the node exist in the index"""
292 return node in self._nodemap
343 return node in self._nodemap
293
344
294 def rev(self, node):
345 def rev(self, node):
295 """return a revision for a node
346 """return a revision for a node
296
347
297 If the node is unknown, raise a RevlogError"""
348 If the node is unknown, raise a RevlogError"""
298 return self._nodemap[node]
349 return self._nodemap[node]
299
350
300 def get_rev(self, node):
351 def get_rev(self, node):
301 """return a revision for a node
352 """return a revision for a node
302
353
303 If the node is unknown, return None"""
354 If the node is unknown, return None"""
304 return self._nodemap.get(node)
355 return self._nodemap.get(node)
305
356
306 def _stripnodes(self, start):
357 def _stripnodes(self, start):
307 if '_nodemap' in vars(self):
358 if '_nodemap' in vars(self):
308 for r in range(start, len(self)):
359 for r in range(start, len(self)):
309 n = self[r][7]
360 n = self[r][7]
310 del self._nodemap[n]
361 del self._nodemap[n]
311
362
312 def clearcaches(self):
363 def clearcaches(self):
313 self.__dict__.pop('_nodemap', None)
364 self.__dict__.pop('_nodemap', None)
314
365
315 def __len__(self):
366 def __len__(self):
316 return self._lgt + len(self._extra)
367 return self._lgt + len(self._extra)
317
368
318 def append(self, tup):
369 def append(self, tup):
319 if '_nodemap' in vars(self):
370 if '_nodemap' in vars(self):
320 self._nodemap[tup[7]] = len(self)
371 self._nodemap[tup[7]] = len(self)
321 data = self._pack_entry(len(self), tup)
372 data = self._pack_entry(len(self), tup)
322 self._extra.append(data)
373 self._extra.append(data)
323
374
324 def _pack_entry(self, rev, entry):
375 def _pack_entry(self, rev, entry):
325 assert entry[8] == 0
376 assert entry[8] == 0
326 assert entry[9] == 0
377 assert entry[9] == 0
327 return self.index_format.pack(*entry[:8])
378 return self.index_format.pack(*entry[:8])
328
379
329 def _check_index(self, i):
380 def _check_index(self, i):
330 if not isinstance(i, int):
381 if not isinstance(i, int):
331 raise TypeError(b"expecting int indexes")
382 raise TypeError(b"expecting int indexes")
332 if i < 0 or i >= len(self):
383 if i < 0 or i >= len(self):
333 raise IndexError
384 raise IndexError
334
385
335 def __getitem__(self, i):
386 def __getitem__(self, i):
336 if i == -1:
387 if i == -1:
337 return self.null_item
388 return self.null_item
338 self._check_index(i)
389 self._check_index(i)
339 if i >= self._lgt:
390 if i >= self._lgt:
340 data = self._extra[i - self._lgt]
391 data = self._extra[i - self._lgt]
341 else:
392 else:
342 index = self._calculate_index(i)
393 index = self._calculate_index(i)
343 data = self._data[index : index + self.entry_size]
394 data = self._data[index : index + self.entry_size]
344 r = self._unpack_entry(i, data)
395 r = self._unpack_entry(i, data)
345 if self._lgt and i == 0:
396 if self._lgt and i == 0:
346 offset = revlogutils.offset_type(0, gettype(r[0]))
397 offset = revlogutils.offset_type(0, gettype(r[0]))
347 r = (offset,) + r[1:]
398 r = (offset,) + r[1:]
348 return r
399 return r
349
400
350 def _unpack_entry(self, rev, data):
401 def _unpack_entry(self, rev, data):
351 r = self.index_format.unpack(data)
402 r = self.index_format.unpack(data)
352 r = r + (
403 r = r + (
353 0,
404 0,
354 0,
405 0,
355 revlog_constants.COMP_MODE_INLINE,
406 revlog_constants.COMP_MODE_INLINE,
356 revlog_constants.COMP_MODE_INLINE,
407 revlog_constants.COMP_MODE_INLINE,
357 )
408 )
358 return r
409 return r
359
410
360 def pack_header(self, header):
411 def pack_header(self, header):
361 """pack header information as binary"""
412 """pack header information as binary"""
362 v_fmt = revlog_constants.INDEX_HEADER
413 v_fmt = revlog_constants.INDEX_HEADER
363 return v_fmt.pack(header)
414 return v_fmt.pack(header)
364
415
365 def entry_binary(self, rev):
416 def entry_binary(self, rev):
366 """return the raw binary string representing a revision"""
417 """return the raw binary string representing a revision"""
367 entry = self[rev]
418 entry = self[rev]
368 p = revlog_constants.INDEX_ENTRY_V1.pack(*entry[:8])
419 p = revlog_constants.INDEX_ENTRY_V1.pack(*entry[:8])
369 if rev == 0:
420 if rev == 0:
370 p = p[revlog_constants.INDEX_HEADER.size :]
421 p = p[revlog_constants.INDEX_HEADER.size :]
371 return p
422 return p
372
423
373
424
374 class IndexObject(BaseIndexObject):
425 class IndexObject(BaseIndexObject):
375 def __init__(self, data):
426 def __init__(self, data):
376 assert len(data) % self.entry_size == 0, (
427 assert len(data) % self.entry_size == 0, (
377 len(data),
428 len(data),
378 self.entry_size,
429 self.entry_size,
379 len(data) % self.entry_size,
430 len(data) % self.entry_size,
380 )
431 )
381 self._data = data
432 self._data = data
382 self._lgt = len(data) // self.entry_size
433 self._lgt = len(data) // self.entry_size
383 self._extra = []
434 self._extra = []
384
435
385 def _calculate_index(self, i):
436 def _calculate_index(self, i):
386 return i * self.entry_size
437 return i * self.entry_size
387
438
388 def __delitem__(self, i):
439 def __delitem__(self, i):
389 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
440 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
390 raise ValueError(b"deleting slices only supports a:-1 with step 1")
441 raise ValueError(b"deleting slices only supports a:-1 with step 1")
391 i = i.start
442 i = i.start
392 self._check_index(i)
443 self._check_index(i)
393 self._stripnodes(i)
444 self._stripnodes(i)
394 if i < self._lgt:
445 if i < self._lgt:
395 self._data = self._data[: i * self.entry_size]
446 self._data = self._data[: i * self.entry_size]
396 self._lgt = i
447 self._lgt = i
397 self._extra = []
448 self._extra = []
398 else:
449 else:
399 self._extra = self._extra[: i - self._lgt]
450 self._extra = self._extra[: i - self._lgt]
400
451
401
452
402 class PersistentNodeMapIndexObject(IndexObject):
453 class PersistentNodeMapIndexObject(IndexObject):
403 """a Debug oriented class to test persistent nodemap
454 """a Debug oriented class to test persistent nodemap
404
455
405 We need a simple python object to test API and higher level behavior. See
456 We need a simple python object to test API and higher level behavior. See
406 the Rust implementation for more serious usage. This should be used only
457 the Rust implementation for more serious usage. This should be used only
407 through the dedicated `devel.persistent-nodemap` config.
458 through the dedicated `devel.persistent-nodemap` config.
408 """
459 """
409
460
410 def nodemap_data_all(self):
461 def nodemap_data_all(self):
411 """Return bytes containing a full serialization of a nodemap
462 """Return bytes containing a full serialization of a nodemap
412
463
413 The nodemap should be valid for the full set of revisions in the
464 The nodemap should be valid for the full set of revisions in the
414 index."""
465 index."""
415 return nodemaputil.persistent_data(self)
466 return nodemaputil.persistent_data(self)
416
467
417 def nodemap_data_incremental(self):
468 def nodemap_data_incremental(self):
418 """Return bytes containing a incremental update to persistent nodemap
469 """Return bytes containing a incremental update to persistent nodemap
419
470
420 This containst the data for an append-only update of the data provided
471 This containst the data for an append-only update of the data provided
421 in the last call to `update_nodemap_data`.
472 in the last call to `update_nodemap_data`.
422 """
473 """
423 if self._nm_root is None:
474 if self._nm_root is None:
424 return None
475 return None
425 docket = self._nm_docket
476 docket = self._nm_docket
426 changed, data = nodemaputil.update_persistent_data(
477 changed, data = nodemaputil.update_persistent_data(
427 self, self._nm_root, self._nm_max_idx, self._nm_docket.tip_rev
478 self, self._nm_root, self._nm_max_idx, self._nm_docket.tip_rev
428 )
479 )
429
480
430 self._nm_root = self._nm_max_idx = self._nm_docket = None
481 self._nm_root = self._nm_max_idx = self._nm_docket = None
431 return docket, changed, data
482 return docket, changed, data
432
483
433 def update_nodemap_data(self, docket, nm_data):
484 def update_nodemap_data(self, docket, nm_data):
434 """provide full block of persisted binary data for a nodemap
485 """provide full block of persisted binary data for a nodemap
435
486
436 The data are expected to come from disk. See `nodemap_data_all` for a
487 The data are expected to come from disk. See `nodemap_data_all` for a
437 produceur of such data."""
488 produceur of such data."""
438 if nm_data is not None:
489 if nm_data is not None:
439 self._nm_root, self._nm_max_idx = nodemaputil.parse_data(nm_data)
490 self._nm_root, self._nm_max_idx = nodemaputil.parse_data(nm_data)
440 if self._nm_root:
491 if self._nm_root:
441 self._nm_docket = docket
492 self._nm_docket = docket
442 else:
493 else:
443 self._nm_root = self._nm_max_idx = self._nm_docket = None
494 self._nm_root = self._nm_max_idx = self._nm_docket = None
444
495
445
496
446 class InlinedIndexObject(BaseIndexObject):
497 class InlinedIndexObject(BaseIndexObject):
447 def __init__(self, data, inline=0):
498 def __init__(self, data, inline=0):
448 self._data = data
499 self._data = data
449 self._lgt = self._inline_scan(None)
500 self._lgt = self._inline_scan(None)
450 self._inline_scan(self._lgt)
501 self._inline_scan(self._lgt)
451 self._extra = []
502 self._extra = []
452
503
453 def _inline_scan(self, lgt):
504 def _inline_scan(self, lgt):
454 off = 0
505 off = 0
455 if lgt is not None:
506 if lgt is not None:
456 self._offsets = [0] * lgt
507 self._offsets = [0] * lgt
457 count = 0
508 count = 0
458 while off <= len(self._data) - self.entry_size:
509 while off <= len(self._data) - self.entry_size:
459 start = off + self.big_int_size
510 start = off + self.big_int_size
460 (s,) = struct.unpack(
511 (s,) = struct.unpack(
461 b'>i',
512 b'>i',
462 self._data[start : start + self.int_size],
513 self._data[start : start + self.int_size],
463 )
514 )
464 if lgt is not None:
515 if lgt is not None:
465 self._offsets[count] = off
516 self._offsets[count] = off
466 count += 1
517 count += 1
467 off += self.entry_size + s
518 off += self.entry_size + s
468 if off != len(self._data):
519 if off != len(self._data):
469 raise ValueError(b"corrupted data")
520 raise ValueError(b"corrupted data")
470 return count
521 return count
471
522
472 def __delitem__(self, i):
523 def __delitem__(self, i):
473 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
524 if not isinstance(i, slice) or not i.stop == -1 or i.step is not None:
474 raise ValueError(b"deleting slices only supports a:-1 with step 1")
525 raise ValueError(b"deleting slices only supports a:-1 with step 1")
475 i = i.start
526 i = i.start
476 self._check_index(i)
527 self._check_index(i)
477 self._stripnodes(i)
528 self._stripnodes(i)
478 if i < self._lgt:
529 if i < self._lgt:
479 self._offsets = self._offsets[:i]
530 self._offsets = self._offsets[:i]
480 self._lgt = i
531 self._lgt = i
481 self._extra = []
532 self._extra = []
482 else:
533 else:
483 self._extra = self._extra[: i - self._lgt]
534 self._extra = self._extra[: i - self._lgt]
484
535
485 def _calculate_index(self, i):
536 def _calculate_index(self, i):
486 return self._offsets[i]
537 return self._offsets[i]
487
538
488
539
489 def parse_index2(data, inline, revlogv2=False):
540 def parse_index2(data, inline, revlogv2=False):
490 if not inline:
541 if not inline:
491 cls = IndexObject2 if revlogv2 else IndexObject
542 cls = IndexObject2 if revlogv2 else IndexObject
492 return cls(data), None
543 return cls(data), None
493 cls = InlinedIndexObject
544 cls = InlinedIndexObject
494 return cls(data, inline), (0, data)
545 return cls(data, inline), (0, data)
495
546
496
547
497 def parse_index_cl_v2(data):
548 def parse_index_cl_v2(data):
498 return IndexChangelogV2(data), None
549 return IndexChangelogV2(data), None
499
550
500
551
501 class IndexObject2(IndexObject):
552 class IndexObject2(IndexObject):
502 index_format = revlog_constants.INDEX_ENTRY_V2
553 index_format = revlog_constants.INDEX_ENTRY_V2
503
554
504 def replace_sidedata_info(
555 def replace_sidedata_info(
505 self,
556 self,
506 rev,
557 rev,
507 sidedata_offset,
558 sidedata_offset,
508 sidedata_length,
559 sidedata_length,
509 offset_flags,
560 offset_flags,
510 compression_mode,
561 compression_mode,
511 ):
562 ):
512 """
563 """
513 Replace an existing index entry's sidedata offset and length with new
564 Replace an existing index entry's sidedata offset and length with new
514 ones.
565 ones.
515 This cannot be used outside of the context of sidedata rewriting,
566 This cannot be used outside of the context of sidedata rewriting,
516 inside the transaction that creates the revision `rev`.
567 inside the transaction that creates the revision `rev`.
517 """
568 """
518 if rev < 0:
569 if rev < 0:
519 raise KeyError
570 raise KeyError
520 self._check_index(rev)
571 self._check_index(rev)
521 if rev < self._lgt:
572 if rev < self._lgt:
522 msg = b"cannot rewrite entries outside of this transaction"
573 msg = b"cannot rewrite entries outside of this transaction"
523 raise KeyError(msg)
574 raise KeyError(msg)
524 else:
575 else:
525 entry = list(self[rev])
576 entry = list(self[rev])
526 entry[0] = offset_flags
577 entry[0] = offset_flags
527 entry[8] = sidedata_offset
578 entry[8] = sidedata_offset
528 entry[9] = sidedata_length
579 entry[9] = sidedata_length
529 entry[11] = compression_mode
580 entry[11] = compression_mode
530 entry = tuple(entry)
581 entry = tuple(entry)
531 new = self._pack_entry(rev, entry)
582 new = self._pack_entry(rev, entry)
532 self._extra[rev - self._lgt] = new
583 self._extra[rev - self._lgt] = new
533
584
534 def _unpack_entry(self, rev, data):
585 def _unpack_entry(self, rev, data):
535 data = self.index_format.unpack(data)
586 data = self.index_format.unpack(data)
536 entry = data[:10]
587 entry = data[:10]
537 data_comp = data[10] & 3
588 data_comp = data[10] & 3
538 sidedata_comp = (data[10] & (3 << 2)) >> 2
589 sidedata_comp = (data[10] & (3 << 2)) >> 2
539 return entry + (data_comp, sidedata_comp)
590 return entry + (data_comp, sidedata_comp)
540
591
541 def _pack_entry(self, rev, entry):
592 def _pack_entry(self, rev, entry):
542 data = entry[:10]
593 data = entry[:10]
543 data_comp = entry[10] & 3
594 data_comp = entry[10] & 3
544 sidedata_comp = (entry[11] & 3) << 2
595 sidedata_comp = (entry[11] & 3) << 2
545 data += (data_comp | sidedata_comp,)
596 data += (data_comp | sidedata_comp,)
546
597
547 return self.index_format.pack(*data)
598 return self.index_format.pack(*data)
548
599
549 def entry_binary(self, rev):
600 def entry_binary(self, rev):
550 """return the raw binary string representing a revision"""
601 """return the raw binary string representing a revision"""
551 entry = self[rev]
602 entry = self[rev]
552 return self._pack_entry(rev, entry)
603 return self._pack_entry(rev, entry)
553
604
554 def pack_header(self, header):
605 def pack_header(self, header):
555 """pack header information as binary"""
606 """pack header information as binary"""
556 msg = 'version header should go in the docket, not the index: %d'
607 msg = 'version header should go in the docket, not the index: %d'
557 msg %= header
608 msg %= header
558 raise error.ProgrammingError(msg)
609 raise error.ProgrammingError(msg)
559
610
560
611
561 class IndexChangelogV2(IndexObject2):
612 class IndexChangelogV2(IndexObject2):
562 index_format = revlog_constants.INDEX_ENTRY_CL_V2
613 index_format = revlog_constants.INDEX_ENTRY_CL_V2
563
614
564 def _unpack_entry(self, rev, data, r=True):
615 def _unpack_entry(self, rev, data, r=True):
565 items = self.index_format.unpack(data)
616 items = self.index_format.unpack(data)
566 entry = items[:3] + (rev, rev) + items[3:8]
617 entry = items[:3] + (rev, rev) + items[3:8]
567 data_comp = items[8] & 3
618 data_comp = items[8] & 3
568 sidedata_comp = (items[8] >> 2) & 3
619 sidedata_comp = (items[8] >> 2) & 3
569 return entry + (data_comp, sidedata_comp)
620 return entry + (data_comp, sidedata_comp)
570
621
571 def _pack_entry(self, rev, entry):
622 def _pack_entry(self, rev, entry):
572 assert entry[3] == rev, entry[3]
623 assert entry[3] == rev, entry[3]
573 assert entry[4] == rev, entry[4]
624 assert entry[4] == rev, entry[4]
574 data = entry[:3] + entry[5:10]
625 data = entry[:3] + entry[5:10]
575 data_comp = entry[10] & 3
626 data_comp = entry[10] & 3
576 sidedata_comp = (entry[11] & 3) << 2
627 sidedata_comp = (entry[11] & 3) << 2
577 data += (data_comp | sidedata_comp,)
628 data += (data_comp | sidedata_comp,)
578 return self.index_format.pack(*data)
629 return self.index_format.pack(*data)
579
630
580
631
581 def parse_index_devel_nodemap(data, inline):
632 def parse_index_devel_nodemap(data, inline):
582 """like parse_index2, but alway return a PersistentNodeMapIndexObject"""
633 """like parse_index2, but alway return a PersistentNodeMapIndexObject"""
583 return PersistentNodeMapIndexObject(data), None
634 return PersistentNodeMapIndexObject(data), None
584
635
585
636
586 def parse_dirstate(dmap, copymap, st):
637 def parse_dirstate(dmap, copymap, st):
587 parents = [st[:20], st[20:40]]
638 parents = [st[:20], st[20:40]]
588 # dereference fields so they will be local in loop
639 # dereference fields so they will be local in loop
589 format = b">cllll"
640 format = b">cllll"
590 e_size = struct.calcsize(format)
641 e_size = struct.calcsize(format)
591 pos1 = 40
642 pos1 = 40
592 l = len(st)
643 l = len(st)
593
644
594 # the inner loop
645 # the inner loop
595 while pos1 < l:
646 while pos1 < l:
596 pos2 = pos1 + e_size
647 pos2 = pos1 + e_size
597 e = _unpack(b">cllll", st[pos1:pos2]) # a literal here is faster
648 e = _unpack(b">cllll", st[pos1:pos2]) # a literal here is faster
598 pos1 = pos2 + e[4]
649 pos1 = pos2 + e[4]
599 f = st[pos2:pos1]
650 f = st[pos2:pos1]
600 if b'\0' in f:
651 if b'\0' in f:
601 f, c = f.split(b'\0')
652 f, c = f.split(b'\0')
602 copymap[f] = c
653 copymap[f] = c
603 dmap[f] = DirstateItem.from_v1_data(*e[:4])
654 dmap[f] = DirstateItem.from_v1_data(*e[:4])
604 return parents
655 return parents
605
656
606
657
607 def pack_dirstate(dmap, copymap, pl, now):
658 def pack_dirstate(dmap, copymap, pl, now):
608 now = int(now)
659 now = int(now)
609 cs = stringio()
660 cs = stringio()
610 write = cs.write
661 write = cs.write
611 write(b"".join(pl))
662 write(b"".join(pl))
612 for f, e in pycompat.iteritems(dmap):
663 for f, e in pycompat.iteritems(dmap):
613 if e.need_delay(now):
664 if e.need_delay(now):
614 # The file was last modified "simultaneously" with the current
665 # The file was last modified "simultaneously" with the current
615 # write to dirstate (i.e. within the same second for file-
666 # write to dirstate (i.e. within the same second for file-
616 # systems with a granularity of 1 sec). This commonly happens
667 # systems with a granularity of 1 sec). This commonly happens
617 # for at least a couple of files on 'update'.
668 # for at least a couple of files on 'update'.
618 # The user could change the file without changing its size
669 # The user could change the file without changing its size
619 # within the same second. Invalidate the file's mtime in
670 # within the same second. Invalidate the file's mtime in
620 # dirstate, forcing future 'status' calls to compare the
671 # dirstate, forcing future 'status' calls to compare the
621 # contents of the file if the size is the same. This prevents
672 # contents of the file if the size is the same. This prevents
622 # mistakenly treating such files as clean.
673 # mistakenly treating such files as clean.
623 e.set_possibly_dirty()
674 e.set_possibly_dirty()
624
675
625 if f in copymap:
676 if f in copymap:
626 f = b"%s\0%s" % (f, copymap[f])
677 f = b"%s\0%s" % (f, copymap[f])
627 e = _pack(
678 e = _pack(
628 b">cllll",
679 b">cllll",
629 e.v1_state(),
680 e.v1_state(),
630 e.v1_mode(),
681 e.v1_mode(),
631 e.v1_size(),
682 e.v1_size(),
632 e.v1_mtime(),
683 e.v1_mtime(),
633 len(f),
684 len(f),
634 )
685 )
635 write(e)
686 write(e)
636 write(f)
687 write(f)
637 return cs.getvalue()
688 return cs.getvalue()
General Comments 0
You need to be logged in to leave comments. Login now