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