##// END OF EJS Templates
phases: sparsify phaseroots and phasesets...
Joerg Sonnenberger -
r45691:61e74644 default
parent child Browse files
Show More
@@ -1,762 +1,762 b''
1 /*
1 /*
2 parsers.c - efficient content parsing
2 parsers.c - efficient content parsing
3
3
4 Copyright 2008 Matt Mackall <mpm@selenic.com> and others
4 Copyright 2008 Matt Mackall <mpm@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 PyObject *dict_new_presized(PyObject *self, PyObject *args)
32 static PyObject *dict_new_presized(PyObject *self, PyObject *args)
33 {
33 {
34 Py_ssize_t expected_size;
34 Py_ssize_t expected_size;
35
35
36 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size)) {
36 if (!PyArg_ParseTuple(args, "n:make_presized_dict", &expected_size)) {
37 return NULL;
37 return NULL;
38 }
38 }
39
39
40 return _dict_new_presized(expected_size);
40 return _dict_new_presized(expected_size);
41 }
41 }
42
42
43 static inline dirstateTupleObject *make_dirstate_tuple(char state, int mode,
43 static inline dirstateTupleObject *make_dirstate_tuple(char state, int mode,
44 int size, int mtime)
44 int size, int mtime)
45 {
45 {
46 dirstateTupleObject *t =
46 dirstateTupleObject *t =
47 PyObject_New(dirstateTupleObject, &dirstateTupleType);
47 PyObject_New(dirstateTupleObject, &dirstateTupleType);
48 if (!t) {
48 if (!t) {
49 return NULL;
49 return NULL;
50 }
50 }
51 t->state = state;
51 t->state = state;
52 t->mode = mode;
52 t->mode = mode;
53 t->size = size;
53 t->size = size;
54 t->mtime = mtime;
54 t->mtime = mtime;
55 return t;
55 return t;
56 }
56 }
57
57
58 static PyObject *dirstate_tuple_new(PyTypeObject *subtype, PyObject *args,
58 static PyObject *dirstate_tuple_new(PyTypeObject *subtype, PyObject *args,
59 PyObject *kwds)
59 PyObject *kwds)
60 {
60 {
61 /* We do all the initialization here and not a tp_init function because
61 /* We do all the initialization here and not a tp_init function because
62 * dirstate_tuple is immutable. */
62 * dirstate_tuple is immutable. */
63 dirstateTupleObject *t;
63 dirstateTupleObject *t;
64 char state;
64 char state;
65 int size, mode, mtime;
65 int size, mode, mtime;
66 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime)) {
66 if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime)) {
67 return NULL;
67 return NULL;
68 }
68 }
69
69
70 t = (dirstateTupleObject *)subtype->tp_alloc(subtype, 1);
70 t = (dirstateTupleObject *)subtype->tp_alloc(subtype, 1);
71 if (!t) {
71 if (!t) {
72 return NULL;
72 return NULL;
73 }
73 }
74 t->state = state;
74 t->state = state;
75 t->mode = mode;
75 t->mode = mode;
76 t->size = size;
76 t->size = size;
77 t->mtime = mtime;
77 t->mtime = mtime;
78
78
79 return (PyObject *)t;
79 return (PyObject *)t;
80 }
80 }
81
81
82 static void dirstate_tuple_dealloc(PyObject *o)
82 static void dirstate_tuple_dealloc(PyObject *o)
83 {
83 {
84 PyObject_Del(o);
84 PyObject_Del(o);
85 }
85 }
86
86
87 static Py_ssize_t dirstate_tuple_length(PyObject *o)
87 static Py_ssize_t dirstate_tuple_length(PyObject *o)
88 {
88 {
89 return 4;
89 return 4;
90 }
90 }
91
91
92 static PyObject *dirstate_tuple_item(PyObject *o, Py_ssize_t i)
92 static PyObject *dirstate_tuple_item(PyObject *o, Py_ssize_t i)
93 {
93 {
94 dirstateTupleObject *t = (dirstateTupleObject *)o;
94 dirstateTupleObject *t = (dirstateTupleObject *)o;
95 switch (i) {
95 switch (i) {
96 case 0:
96 case 0:
97 return PyBytes_FromStringAndSize(&t->state, 1);
97 return PyBytes_FromStringAndSize(&t->state, 1);
98 case 1:
98 case 1:
99 return PyInt_FromLong(t->mode);
99 return PyInt_FromLong(t->mode);
100 case 2:
100 case 2:
101 return PyInt_FromLong(t->size);
101 return PyInt_FromLong(t->size);
102 case 3:
102 case 3:
103 return PyInt_FromLong(t->mtime);
103 return PyInt_FromLong(t->mtime);
104 default:
104 default:
105 PyErr_SetString(PyExc_IndexError, "index out of range");
105 PyErr_SetString(PyExc_IndexError, "index out of range");
106 return NULL;
106 return NULL;
107 }
107 }
108 }
108 }
109
109
110 static PySequenceMethods dirstate_tuple_sq = {
110 static PySequenceMethods dirstate_tuple_sq = {
111 dirstate_tuple_length, /* sq_length */
111 dirstate_tuple_length, /* sq_length */
112 0, /* sq_concat */
112 0, /* sq_concat */
113 0, /* sq_repeat */
113 0, /* sq_repeat */
114 dirstate_tuple_item, /* sq_item */
114 dirstate_tuple_item, /* sq_item */
115 0, /* sq_ass_item */
115 0, /* sq_ass_item */
116 0, /* sq_contains */
116 0, /* sq_contains */
117 0, /* sq_inplace_concat */
117 0, /* sq_inplace_concat */
118 0 /* sq_inplace_repeat */
118 0 /* sq_inplace_repeat */
119 };
119 };
120
120
121 PyTypeObject dirstateTupleType = {
121 PyTypeObject dirstateTupleType = {
122 PyVarObject_HEAD_INIT(NULL, 0) /* header */
122 PyVarObject_HEAD_INIT(NULL, 0) /* header */
123 "dirstate_tuple", /* tp_name */
123 "dirstate_tuple", /* tp_name */
124 sizeof(dirstateTupleObject), /* tp_basicsize */
124 sizeof(dirstateTupleObject), /* tp_basicsize */
125 0, /* tp_itemsize */
125 0, /* tp_itemsize */
126 (destructor)dirstate_tuple_dealloc, /* tp_dealloc */
126 (destructor)dirstate_tuple_dealloc, /* tp_dealloc */
127 0, /* tp_print */
127 0, /* tp_print */
128 0, /* tp_getattr */
128 0, /* tp_getattr */
129 0, /* tp_setattr */
129 0, /* tp_setattr */
130 0, /* tp_compare */
130 0, /* tp_compare */
131 0, /* tp_repr */
131 0, /* tp_repr */
132 0, /* tp_as_number */
132 0, /* tp_as_number */
133 &dirstate_tuple_sq, /* tp_as_sequence */
133 &dirstate_tuple_sq, /* tp_as_sequence */
134 0, /* tp_as_mapping */
134 0, /* tp_as_mapping */
135 0, /* tp_hash */
135 0, /* tp_hash */
136 0, /* tp_call */
136 0, /* tp_call */
137 0, /* tp_str */
137 0, /* tp_str */
138 0, /* tp_getattro */
138 0, /* tp_getattro */
139 0, /* tp_setattro */
139 0, /* tp_setattro */
140 0, /* tp_as_buffer */
140 0, /* tp_as_buffer */
141 Py_TPFLAGS_DEFAULT, /* tp_flags */
141 Py_TPFLAGS_DEFAULT, /* tp_flags */
142 "dirstate tuple", /* tp_doc */
142 "dirstate tuple", /* tp_doc */
143 0, /* tp_traverse */
143 0, /* tp_traverse */
144 0, /* tp_clear */
144 0, /* tp_clear */
145 0, /* tp_richcompare */
145 0, /* tp_richcompare */
146 0, /* tp_weaklistoffset */
146 0, /* tp_weaklistoffset */
147 0, /* tp_iter */
147 0, /* tp_iter */
148 0, /* tp_iternext */
148 0, /* tp_iternext */
149 0, /* tp_methods */
149 0, /* tp_methods */
150 0, /* tp_members */
150 0, /* tp_members */
151 0, /* tp_getset */
151 0, /* tp_getset */
152 0, /* tp_base */
152 0, /* tp_base */
153 0, /* tp_dict */
153 0, /* tp_dict */
154 0, /* tp_descr_get */
154 0, /* tp_descr_get */
155 0, /* tp_descr_set */
155 0, /* tp_descr_set */
156 0, /* tp_dictoffset */
156 0, /* tp_dictoffset */
157 0, /* tp_init */
157 0, /* tp_init */
158 0, /* tp_alloc */
158 0, /* tp_alloc */
159 dirstate_tuple_new, /* tp_new */
159 dirstate_tuple_new, /* tp_new */
160 };
160 };
161
161
162 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
162 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
163 {
163 {
164 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
164 PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
165 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
165 PyObject *fname = NULL, *cname = NULL, *entry = NULL;
166 char state, *cur, *str, *cpos;
166 char state, *cur, *str, *cpos;
167 int mode, size, mtime;
167 int mode, size, mtime;
168 unsigned int flen, pos = 40;
168 unsigned int flen, pos = 40;
169 Py_ssize_t len = 40;
169 Py_ssize_t len = 40;
170 Py_ssize_t readlen;
170 Py_ssize_t readlen;
171
171
172 if (!PyArg_ParseTuple(
172 if (!PyArg_ParseTuple(
173 args, PY23("O!O!s#:parse_dirstate", "O!O!y#:parse_dirstate"),
173 args, PY23("O!O!s#:parse_dirstate", "O!O!y#:parse_dirstate"),
174 &PyDict_Type, &dmap, &PyDict_Type, &cmap, &str, &readlen)) {
174 &PyDict_Type, &dmap, &PyDict_Type, &cmap, &str, &readlen)) {
175 goto quit;
175 goto quit;
176 }
176 }
177
177
178 len = readlen;
178 len = readlen;
179
179
180 /* read parents */
180 /* read parents */
181 if (len < 40) {
181 if (len < 40) {
182 PyErr_SetString(PyExc_ValueError,
182 PyErr_SetString(PyExc_ValueError,
183 "too little data for parents");
183 "too little data for parents");
184 goto quit;
184 goto quit;
185 }
185 }
186
186
187 parents = Py_BuildValue(PY23("s#s#", "y#y#"), str, (Py_ssize_t)20,
187 parents = Py_BuildValue(PY23("s#s#", "y#y#"), str, (Py_ssize_t)20,
188 str + 20, (Py_ssize_t)20);
188 str + 20, (Py_ssize_t)20);
189 if (!parents) {
189 if (!parents) {
190 goto quit;
190 goto quit;
191 }
191 }
192
192
193 /* read filenames */
193 /* read filenames */
194 while (pos >= 40 && pos < len) {
194 while (pos >= 40 && pos < len) {
195 if (pos + 17 > len) {
195 if (pos + 17 > len) {
196 PyErr_SetString(PyExc_ValueError,
196 PyErr_SetString(PyExc_ValueError,
197 "overflow in dirstate");
197 "overflow in dirstate");
198 goto quit;
198 goto quit;
199 }
199 }
200 cur = str + pos;
200 cur = str + pos;
201 /* unpack header */
201 /* unpack header */
202 state = *cur;
202 state = *cur;
203 mode = getbe32(cur + 1);
203 mode = getbe32(cur + 1);
204 size = getbe32(cur + 5);
204 size = getbe32(cur + 5);
205 mtime = getbe32(cur + 9);
205 mtime = getbe32(cur + 9);
206 flen = getbe32(cur + 13);
206 flen = getbe32(cur + 13);
207 pos += 17;
207 pos += 17;
208 cur += 17;
208 cur += 17;
209 if (flen > len - pos) {
209 if (flen > len - pos) {
210 PyErr_SetString(PyExc_ValueError,
210 PyErr_SetString(PyExc_ValueError,
211 "overflow in dirstate");
211 "overflow in dirstate");
212 goto quit;
212 goto quit;
213 }
213 }
214
214
215 entry =
215 entry =
216 (PyObject *)make_dirstate_tuple(state, mode, size, mtime);
216 (PyObject *)make_dirstate_tuple(state, mode, size, mtime);
217 cpos = memchr(cur, 0, flen);
217 cpos = memchr(cur, 0, flen);
218 if (cpos) {
218 if (cpos) {
219 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
219 fname = PyBytes_FromStringAndSize(cur, cpos - cur);
220 cname = PyBytes_FromStringAndSize(
220 cname = PyBytes_FromStringAndSize(
221 cpos + 1, flen - (cpos - cur) - 1);
221 cpos + 1, flen - (cpos - cur) - 1);
222 if (!fname || !cname ||
222 if (!fname || !cname ||
223 PyDict_SetItem(cmap, fname, cname) == -1 ||
223 PyDict_SetItem(cmap, fname, cname) == -1 ||
224 PyDict_SetItem(dmap, fname, entry) == -1) {
224 PyDict_SetItem(dmap, fname, entry) == -1) {
225 goto quit;
225 goto quit;
226 }
226 }
227 Py_DECREF(cname);
227 Py_DECREF(cname);
228 } else {
228 } else {
229 fname = PyBytes_FromStringAndSize(cur, flen);
229 fname = PyBytes_FromStringAndSize(cur, flen);
230 if (!fname ||
230 if (!fname ||
231 PyDict_SetItem(dmap, fname, entry) == -1) {
231 PyDict_SetItem(dmap, fname, entry) == -1) {
232 goto quit;
232 goto quit;
233 }
233 }
234 }
234 }
235 Py_DECREF(fname);
235 Py_DECREF(fname);
236 Py_DECREF(entry);
236 Py_DECREF(entry);
237 fname = cname = entry = NULL;
237 fname = cname = entry = NULL;
238 pos += flen;
238 pos += flen;
239 }
239 }
240
240
241 ret = parents;
241 ret = parents;
242 Py_INCREF(ret);
242 Py_INCREF(ret);
243 quit:
243 quit:
244 Py_XDECREF(fname);
244 Py_XDECREF(fname);
245 Py_XDECREF(cname);
245 Py_XDECREF(cname);
246 Py_XDECREF(entry);
246 Py_XDECREF(entry);
247 Py_XDECREF(parents);
247 Py_XDECREF(parents);
248 return ret;
248 return ret;
249 }
249 }
250
250
251 /*
251 /*
252 * Build a set of non-normal and other parent entries from the dirstate dmap
252 * Build a set of non-normal and other parent entries from the dirstate dmap
253 */
253 */
254 static PyObject *nonnormalotherparententries(PyObject *self, PyObject *args)
254 static PyObject *nonnormalotherparententries(PyObject *self, PyObject *args)
255 {
255 {
256 PyObject *dmap, *fname, *v;
256 PyObject *dmap, *fname, *v;
257 PyObject *nonnset = NULL, *otherpset = NULL, *result = NULL;
257 PyObject *nonnset = NULL, *otherpset = NULL, *result = NULL;
258 Py_ssize_t pos;
258 Py_ssize_t pos;
259
259
260 if (!PyArg_ParseTuple(args, "O!:nonnormalentries", &PyDict_Type,
260 if (!PyArg_ParseTuple(args, "O!:nonnormalentries", &PyDict_Type,
261 &dmap)) {
261 &dmap)) {
262 goto bail;
262 goto bail;
263 }
263 }
264
264
265 nonnset = PySet_New(NULL);
265 nonnset = PySet_New(NULL);
266 if (nonnset == NULL) {
266 if (nonnset == NULL) {
267 goto bail;
267 goto bail;
268 }
268 }
269
269
270 otherpset = PySet_New(NULL);
270 otherpset = PySet_New(NULL);
271 if (otherpset == NULL) {
271 if (otherpset == NULL) {
272 goto bail;
272 goto bail;
273 }
273 }
274
274
275 pos = 0;
275 pos = 0;
276 while (PyDict_Next(dmap, &pos, &fname, &v)) {
276 while (PyDict_Next(dmap, &pos, &fname, &v)) {
277 dirstateTupleObject *t;
277 dirstateTupleObject *t;
278 if (!dirstate_tuple_check(v)) {
278 if (!dirstate_tuple_check(v)) {
279 PyErr_SetString(PyExc_TypeError,
279 PyErr_SetString(PyExc_TypeError,
280 "expected a dirstate tuple");
280 "expected a dirstate tuple");
281 goto bail;
281 goto bail;
282 }
282 }
283 t = (dirstateTupleObject *)v;
283 t = (dirstateTupleObject *)v;
284
284
285 if (t->state == 'n' && t->size == -2) {
285 if (t->state == 'n' && t->size == -2) {
286 if (PySet_Add(otherpset, fname) == -1) {
286 if (PySet_Add(otherpset, fname) == -1) {
287 goto bail;
287 goto bail;
288 }
288 }
289 }
289 }
290
290
291 if (t->state == 'n' && t->mtime != -1) {
291 if (t->state == 'n' && t->mtime != -1) {
292 continue;
292 continue;
293 }
293 }
294 if (PySet_Add(nonnset, fname) == -1) {
294 if (PySet_Add(nonnset, fname) == -1) {
295 goto bail;
295 goto bail;
296 }
296 }
297 }
297 }
298
298
299 result = Py_BuildValue("(OO)", nonnset, otherpset);
299 result = Py_BuildValue("(OO)", nonnset, otherpset);
300 if (result == NULL) {
300 if (result == NULL) {
301 goto bail;
301 goto bail;
302 }
302 }
303 Py_DECREF(nonnset);
303 Py_DECREF(nonnset);
304 Py_DECREF(otherpset);
304 Py_DECREF(otherpset);
305 return result;
305 return result;
306 bail:
306 bail:
307 Py_XDECREF(nonnset);
307 Py_XDECREF(nonnset);
308 Py_XDECREF(otherpset);
308 Py_XDECREF(otherpset);
309 Py_XDECREF(result);
309 Py_XDECREF(result);
310 return NULL;
310 return NULL;
311 }
311 }
312
312
313 /*
313 /*
314 * Efficiently pack a dirstate object into its on-disk format.
314 * Efficiently pack a dirstate object into its on-disk format.
315 */
315 */
316 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
316 static PyObject *pack_dirstate(PyObject *self, PyObject *args)
317 {
317 {
318 PyObject *packobj = NULL;
318 PyObject *packobj = NULL;
319 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
319 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
320 Py_ssize_t nbytes, pos, l;
320 Py_ssize_t nbytes, pos, l;
321 PyObject *k, *v = NULL, *pn;
321 PyObject *k, *v = NULL, *pn;
322 char *p, *s;
322 char *p, *s;
323 int now;
323 int now;
324
324
325 if (!PyArg_ParseTuple(args, "O!O!O!i:pack_dirstate", &PyDict_Type, &map,
325 if (!PyArg_ParseTuple(args, "O!O!O!i:pack_dirstate", &PyDict_Type, &map,
326 &PyDict_Type, &copymap, &PyTuple_Type, &pl,
326 &PyDict_Type, &copymap, &PyTuple_Type, &pl,
327 &now)) {
327 &now)) {
328 return NULL;
328 return NULL;
329 }
329 }
330
330
331 if (PyTuple_Size(pl) != 2) {
331 if (PyTuple_Size(pl) != 2) {
332 PyErr_SetString(PyExc_TypeError, "expected 2-element tuple");
332 PyErr_SetString(PyExc_TypeError, "expected 2-element tuple");
333 return NULL;
333 return NULL;
334 }
334 }
335
335
336 /* Figure out how much we need to allocate. */
336 /* Figure out how much we need to allocate. */
337 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
337 for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) {
338 PyObject *c;
338 PyObject *c;
339 if (!PyBytes_Check(k)) {
339 if (!PyBytes_Check(k)) {
340 PyErr_SetString(PyExc_TypeError, "expected string key");
340 PyErr_SetString(PyExc_TypeError, "expected string key");
341 goto bail;
341 goto bail;
342 }
342 }
343 nbytes += PyBytes_GET_SIZE(k) + 17;
343 nbytes += PyBytes_GET_SIZE(k) + 17;
344 c = PyDict_GetItem(copymap, k);
344 c = PyDict_GetItem(copymap, k);
345 if (c) {
345 if (c) {
346 if (!PyBytes_Check(c)) {
346 if (!PyBytes_Check(c)) {
347 PyErr_SetString(PyExc_TypeError,
347 PyErr_SetString(PyExc_TypeError,
348 "expected string key");
348 "expected string key");
349 goto bail;
349 goto bail;
350 }
350 }
351 nbytes += PyBytes_GET_SIZE(c) + 1;
351 nbytes += PyBytes_GET_SIZE(c) + 1;
352 }
352 }
353 }
353 }
354
354
355 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
355 packobj = PyBytes_FromStringAndSize(NULL, nbytes);
356 if (packobj == NULL) {
356 if (packobj == NULL) {
357 goto bail;
357 goto bail;
358 }
358 }
359
359
360 p = PyBytes_AS_STRING(packobj);
360 p = PyBytes_AS_STRING(packobj);
361
361
362 pn = PyTuple_GET_ITEM(pl, 0);
362 pn = PyTuple_GET_ITEM(pl, 0);
363 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
363 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
364 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
364 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
365 goto bail;
365 goto bail;
366 }
366 }
367 memcpy(p, s, l);
367 memcpy(p, s, l);
368 p += 20;
368 p += 20;
369 pn = PyTuple_GET_ITEM(pl, 1);
369 pn = PyTuple_GET_ITEM(pl, 1);
370 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
370 if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) {
371 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
371 PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash");
372 goto bail;
372 goto bail;
373 }
373 }
374 memcpy(p, s, l);
374 memcpy(p, s, l);
375 p += 20;
375 p += 20;
376
376
377 for (pos = 0; PyDict_Next(map, &pos, &k, &v);) {
377 for (pos = 0; PyDict_Next(map, &pos, &k, &v);) {
378 dirstateTupleObject *tuple;
378 dirstateTupleObject *tuple;
379 char state;
379 char state;
380 int mode, size, mtime;
380 int mode, size, mtime;
381 Py_ssize_t len, l;
381 Py_ssize_t len, l;
382 PyObject *o;
382 PyObject *o;
383 char *t;
383 char *t;
384
384
385 if (!dirstate_tuple_check(v)) {
385 if (!dirstate_tuple_check(v)) {
386 PyErr_SetString(PyExc_TypeError,
386 PyErr_SetString(PyExc_TypeError,
387 "expected a dirstate tuple");
387 "expected a dirstate tuple");
388 goto bail;
388 goto bail;
389 }
389 }
390 tuple = (dirstateTupleObject *)v;
390 tuple = (dirstateTupleObject *)v;
391
391
392 state = tuple->state;
392 state = tuple->state;
393 mode = tuple->mode;
393 mode = tuple->mode;
394 size = tuple->size;
394 size = tuple->size;
395 mtime = tuple->mtime;
395 mtime = tuple->mtime;
396 if (state == 'n' && mtime == now) {
396 if (state == 'n' && mtime == now) {
397 /* See pure/parsers.py:pack_dirstate for why we do
397 /* See pure/parsers.py:pack_dirstate for why we do
398 * this. */
398 * this. */
399 mtime = -1;
399 mtime = -1;
400 mtime_unset = (PyObject *)make_dirstate_tuple(
400 mtime_unset = (PyObject *)make_dirstate_tuple(
401 state, mode, size, mtime);
401 state, mode, size, mtime);
402 if (!mtime_unset) {
402 if (!mtime_unset) {
403 goto bail;
403 goto bail;
404 }
404 }
405 if (PyDict_SetItem(map, k, mtime_unset) == -1) {
405 if (PyDict_SetItem(map, k, mtime_unset) == -1) {
406 goto bail;
406 goto bail;
407 }
407 }
408 Py_DECREF(mtime_unset);
408 Py_DECREF(mtime_unset);
409 mtime_unset = NULL;
409 mtime_unset = NULL;
410 }
410 }
411 *p++ = state;
411 *p++ = state;
412 putbe32((uint32_t)mode, p);
412 putbe32((uint32_t)mode, p);
413 putbe32((uint32_t)size, p + 4);
413 putbe32((uint32_t)size, p + 4);
414 putbe32((uint32_t)mtime, p + 8);
414 putbe32((uint32_t)mtime, p + 8);
415 t = p + 12;
415 t = p + 12;
416 p += 16;
416 p += 16;
417 len = PyBytes_GET_SIZE(k);
417 len = PyBytes_GET_SIZE(k);
418 memcpy(p, PyBytes_AS_STRING(k), len);
418 memcpy(p, PyBytes_AS_STRING(k), len);
419 p += len;
419 p += len;
420 o = PyDict_GetItem(copymap, k);
420 o = PyDict_GetItem(copymap, k);
421 if (o) {
421 if (o) {
422 *p++ = '\0';
422 *p++ = '\0';
423 l = PyBytes_GET_SIZE(o);
423 l = PyBytes_GET_SIZE(o);
424 memcpy(p, PyBytes_AS_STRING(o), l);
424 memcpy(p, PyBytes_AS_STRING(o), l);
425 p += l;
425 p += l;
426 len += l + 1;
426 len += l + 1;
427 }
427 }
428 putbe32((uint32_t)len, t);
428 putbe32((uint32_t)len, t);
429 }
429 }
430
430
431 pos = p - PyBytes_AS_STRING(packobj);
431 pos = p - PyBytes_AS_STRING(packobj);
432 if (pos != nbytes) {
432 if (pos != nbytes) {
433 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
433 PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld",
434 (long)pos, (long)nbytes);
434 (long)pos, (long)nbytes);
435 goto bail;
435 goto bail;
436 }
436 }
437
437
438 return packobj;
438 return packobj;
439 bail:
439 bail:
440 Py_XDECREF(mtime_unset);
440 Py_XDECREF(mtime_unset);
441 Py_XDECREF(packobj);
441 Py_XDECREF(packobj);
442 Py_XDECREF(v);
442 Py_XDECREF(v);
443 return NULL;
443 return NULL;
444 }
444 }
445
445
446 #define BUMPED_FIX 1
446 #define BUMPED_FIX 1
447 #define USING_SHA_256 2
447 #define USING_SHA_256 2
448 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
448 #define FM1_HEADER_SIZE (4 + 8 + 2 + 2 + 1 + 1 + 1)
449
449
450 static PyObject *readshas(const char *source, unsigned char num,
450 static PyObject *readshas(const char *source, unsigned char num,
451 Py_ssize_t hashwidth)
451 Py_ssize_t hashwidth)
452 {
452 {
453 int i;
453 int i;
454 PyObject *list = PyTuple_New(num);
454 PyObject *list = PyTuple_New(num);
455 if (list == NULL) {
455 if (list == NULL) {
456 return NULL;
456 return NULL;
457 }
457 }
458 for (i = 0; i < num; i++) {
458 for (i = 0; i < num; i++) {
459 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
459 PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth);
460 if (hash == NULL) {
460 if (hash == NULL) {
461 Py_DECREF(list);
461 Py_DECREF(list);
462 return NULL;
462 return NULL;
463 }
463 }
464 PyTuple_SET_ITEM(list, i, hash);
464 PyTuple_SET_ITEM(list, i, hash);
465 source += hashwidth;
465 source += hashwidth;
466 }
466 }
467 return list;
467 return list;
468 }
468 }
469
469
470 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
470 static PyObject *fm1readmarker(const char *databegin, const char *dataend,
471 uint32_t *msize)
471 uint32_t *msize)
472 {
472 {
473 const char *data = databegin;
473 const char *data = databegin;
474 const char *meta;
474 const char *meta;
475
475
476 double mtime;
476 double mtime;
477 int16_t tz;
477 int16_t tz;
478 uint16_t flags;
478 uint16_t flags;
479 unsigned char nsuccs, nparents, nmetadata;
479 unsigned char nsuccs, nparents, nmetadata;
480 Py_ssize_t hashwidth = 20;
480 Py_ssize_t hashwidth = 20;
481
481
482 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
482 PyObject *prec = NULL, *parents = NULL, *succs = NULL;
483 PyObject *metadata = NULL, *ret = NULL;
483 PyObject *metadata = NULL, *ret = NULL;
484 int i;
484 int i;
485
485
486 if (data + FM1_HEADER_SIZE > dataend) {
486 if (data + FM1_HEADER_SIZE > dataend) {
487 goto overflow;
487 goto overflow;
488 }
488 }
489
489
490 *msize = getbe32(data);
490 *msize = getbe32(data);
491 data += 4;
491 data += 4;
492 mtime = getbefloat64(data);
492 mtime = getbefloat64(data);
493 data += 8;
493 data += 8;
494 tz = getbeint16(data);
494 tz = getbeint16(data);
495 data += 2;
495 data += 2;
496 flags = getbeuint16(data);
496 flags = getbeuint16(data);
497 data += 2;
497 data += 2;
498
498
499 if (flags & USING_SHA_256) {
499 if (flags & USING_SHA_256) {
500 hashwidth = 32;
500 hashwidth = 32;
501 }
501 }
502
502
503 nsuccs = (unsigned char)(*data++);
503 nsuccs = (unsigned char)(*data++);
504 nparents = (unsigned char)(*data++);
504 nparents = (unsigned char)(*data++);
505 nmetadata = (unsigned char)(*data++);
505 nmetadata = (unsigned char)(*data++);
506
506
507 if (databegin + *msize > dataend) {
507 if (databegin + *msize > dataend) {
508 goto overflow;
508 goto overflow;
509 }
509 }
510 dataend = databegin + *msize; /* narrow down to marker size */
510 dataend = databegin + *msize; /* narrow down to marker size */
511
511
512 if (data + hashwidth > dataend) {
512 if (data + hashwidth > dataend) {
513 goto overflow;
513 goto overflow;
514 }
514 }
515 prec = PyBytes_FromStringAndSize(data, hashwidth);
515 prec = PyBytes_FromStringAndSize(data, hashwidth);
516 data += hashwidth;
516 data += hashwidth;
517 if (prec == NULL) {
517 if (prec == NULL) {
518 goto bail;
518 goto bail;
519 }
519 }
520
520
521 if (data + nsuccs * hashwidth > dataend) {
521 if (data + nsuccs * hashwidth > dataend) {
522 goto overflow;
522 goto overflow;
523 }
523 }
524 succs = readshas(data, nsuccs, hashwidth);
524 succs = readshas(data, nsuccs, hashwidth);
525 if (succs == NULL) {
525 if (succs == NULL) {
526 goto bail;
526 goto bail;
527 }
527 }
528 data += nsuccs * hashwidth;
528 data += nsuccs * hashwidth;
529
529
530 if (nparents == 1 || nparents == 2) {
530 if (nparents == 1 || nparents == 2) {
531 if (data + nparents * hashwidth > dataend) {
531 if (data + nparents * hashwidth > dataend) {
532 goto overflow;
532 goto overflow;
533 }
533 }
534 parents = readshas(data, nparents, hashwidth);
534 parents = readshas(data, nparents, hashwidth);
535 if (parents == NULL) {
535 if (parents == NULL) {
536 goto bail;
536 goto bail;
537 }
537 }
538 data += nparents * hashwidth;
538 data += nparents * hashwidth;
539 } else {
539 } else {
540 parents = Py_None;
540 parents = Py_None;
541 Py_INCREF(parents);
541 Py_INCREF(parents);
542 }
542 }
543
543
544 if (data + 2 * nmetadata > dataend) {
544 if (data + 2 * nmetadata > dataend) {
545 goto overflow;
545 goto overflow;
546 }
546 }
547 meta = data + (2 * nmetadata);
547 meta = data + (2 * nmetadata);
548 metadata = PyTuple_New(nmetadata);
548 metadata = PyTuple_New(nmetadata);
549 if (metadata == NULL) {
549 if (metadata == NULL) {
550 goto bail;
550 goto bail;
551 }
551 }
552 for (i = 0; i < nmetadata; i++) {
552 for (i = 0; i < nmetadata; i++) {
553 PyObject *tmp, *left = NULL, *right = NULL;
553 PyObject *tmp, *left = NULL, *right = NULL;
554 Py_ssize_t leftsize = (unsigned char)(*data++);
554 Py_ssize_t leftsize = (unsigned char)(*data++);
555 Py_ssize_t rightsize = (unsigned char)(*data++);
555 Py_ssize_t rightsize = (unsigned char)(*data++);
556 if (meta + leftsize + rightsize > dataend) {
556 if (meta + leftsize + rightsize > dataend) {
557 goto overflow;
557 goto overflow;
558 }
558 }
559 left = PyBytes_FromStringAndSize(meta, leftsize);
559 left = PyBytes_FromStringAndSize(meta, leftsize);
560 meta += leftsize;
560 meta += leftsize;
561 right = PyBytes_FromStringAndSize(meta, rightsize);
561 right = PyBytes_FromStringAndSize(meta, rightsize);
562 meta += rightsize;
562 meta += rightsize;
563 tmp = PyTuple_New(2);
563 tmp = PyTuple_New(2);
564 if (!left || !right || !tmp) {
564 if (!left || !right || !tmp) {
565 Py_XDECREF(left);
565 Py_XDECREF(left);
566 Py_XDECREF(right);
566 Py_XDECREF(right);
567 Py_XDECREF(tmp);
567 Py_XDECREF(tmp);
568 goto bail;
568 goto bail;
569 }
569 }
570 PyTuple_SET_ITEM(tmp, 0, left);
570 PyTuple_SET_ITEM(tmp, 0, left);
571 PyTuple_SET_ITEM(tmp, 1, right);
571 PyTuple_SET_ITEM(tmp, 1, right);
572 PyTuple_SET_ITEM(metadata, i, tmp);
572 PyTuple_SET_ITEM(metadata, i, tmp);
573 }
573 }
574 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags, metadata, mtime,
574 ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags, metadata, mtime,
575 (int)tz * 60, parents);
575 (int)tz * 60, parents);
576 goto bail; /* return successfully */
576 goto bail; /* return successfully */
577
577
578 overflow:
578 overflow:
579 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
579 PyErr_SetString(PyExc_ValueError, "overflow in obsstore");
580 bail:
580 bail:
581 Py_XDECREF(prec);
581 Py_XDECREF(prec);
582 Py_XDECREF(succs);
582 Py_XDECREF(succs);
583 Py_XDECREF(metadata);
583 Py_XDECREF(metadata);
584 Py_XDECREF(parents);
584 Py_XDECREF(parents);
585 return ret;
585 return ret;
586 }
586 }
587
587
588 static PyObject *fm1readmarkers(PyObject *self, PyObject *args)
588 static PyObject *fm1readmarkers(PyObject *self, PyObject *args)
589 {
589 {
590 const char *data, *dataend;
590 const char *data, *dataend;
591 Py_ssize_t datalen, offset, stop;
591 Py_ssize_t datalen, offset, stop;
592 PyObject *markers = NULL;
592 PyObject *markers = NULL;
593
593
594 if (!PyArg_ParseTuple(args, PY23("s#nn", "y#nn"), &data, &datalen,
594 if (!PyArg_ParseTuple(args, PY23("s#nn", "y#nn"), &data, &datalen,
595 &offset, &stop)) {
595 &offset, &stop)) {
596 return NULL;
596 return NULL;
597 }
597 }
598 if (offset < 0) {
598 if (offset < 0) {
599 PyErr_SetString(PyExc_ValueError,
599 PyErr_SetString(PyExc_ValueError,
600 "invalid negative offset in fm1readmarkers");
600 "invalid negative offset in fm1readmarkers");
601 return NULL;
601 return NULL;
602 }
602 }
603 if (stop > datalen) {
603 if (stop > datalen) {
604 PyErr_SetString(
604 PyErr_SetString(
605 PyExc_ValueError,
605 PyExc_ValueError,
606 "stop longer than data length in fm1readmarkers");
606 "stop longer than data length in fm1readmarkers");
607 return NULL;
607 return NULL;
608 }
608 }
609 dataend = data + datalen;
609 dataend = data + datalen;
610 data += offset;
610 data += offset;
611 markers = PyList_New(0);
611 markers = PyList_New(0);
612 if (!markers) {
612 if (!markers) {
613 return NULL;
613 return NULL;
614 }
614 }
615 while (offset < stop) {
615 while (offset < stop) {
616 uint32_t msize;
616 uint32_t msize;
617 int error;
617 int error;
618 PyObject *record = fm1readmarker(data, dataend, &msize);
618 PyObject *record = fm1readmarker(data, dataend, &msize);
619 if (!record) {
619 if (!record) {
620 goto bail;
620 goto bail;
621 }
621 }
622 error = PyList_Append(markers, record);
622 error = PyList_Append(markers, record);
623 Py_DECREF(record);
623 Py_DECREF(record);
624 if (error) {
624 if (error) {
625 goto bail;
625 goto bail;
626 }
626 }
627 data += msize;
627 data += msize;
628 offset += msize;
628 offset += msize;
629 }
629 }
630 return markers;
630 return markers;
631 bail:
631 bail:
632 Py_DECREF(markers);
632 Py_DECREF(markers);
633 return NULL;
633 return NULL;
634 }
634 }
635
635
636 static char parsers_doc[] = "Efficient content parsing.";
636 static char parsers_doc[] = "Efficient content parsing.";
637
637
638 PyObject *encodedir(PyObject *self, PyObject *args);
638 PyObject *encodedir(PyObject *self, PyObject *args);
639 PyObject *pathencode(PyObject *self, PyObject *args);
639 PyObject *pathencode(PyObject *self, PyObject *args);
640 PyObject *lowerencode(PyObject *self, PyObject *args);
640 PyObject *lowerencode(PyObject *self, PyObject *args);
641 PyObject *parse_index2(PyObject *self, PyObject *args);
641 PyObject *parse_index2(PyObject *self, PyObject *args);
642
642
643 static PyMethodDef methods[] = {
643 static PyMethodDef methods[] = {
644 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
644 {"pack_dirstate", pack_dirstate, METH_VARARGS, "pack a dirstate\n"},
645 {"nonnormalotherparententries", nonnormalotherparententries, METH_VARARGS,
645 {"nonnormalotherparententries", nonnormalotherparententries, METH_VARARGS,
646 "create a set containing non-normal and other parent entries of given "
646 "create a set containing non-normal and other parent entries of given "
647 "dirstate\n"},
647 "dirstate\n"},
648 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
648 {"parse_dirstate", parse_dirstate, METH_VARARGS, "parse a dirstate\n"},
649 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
649 {"parse_index2", parse_index2, METH_VARARGS, "parse a revlog index\n"},
650 {"isasciistr", isasciistr, METH_VARARGS, "check if an ASCII string\n"},
650 {"isasciistr", isasciistr, METH_VARARGS, "check if an ASCII string\n"},
651 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
651 {"asciilower", asciilower, METH_VARARGS, "lowercase an ASCII string\n"},
652 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
652 {"asciiupper", asciiupper, METH_VARARGS, "uppercase an ASCII string\n"},
653 {"dict_new_presized", dict_new_presized, METH_VARARGS,
653 {"dict_new_presized", dict_new_presized, METH_VARARGS,
654 "construct a dict with an expected size\n"},
654 "construct a dict with an expected size\n"},
655 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
655 {"make_file_foldmap", make_file_foldmap, METH_VARARGS,
656 "make file foldmap\n"},
656 "make file foldmap\n"},
657 {"jsonescapeu8fast", jsonescapeu8fast, METH_VARARGS,
657 {"jsonescapeu8fast", jsonescapeu8fast, METH_VARARGS,
658 "escape a UTF-8 byte string to JSON (fast path)\n"},
658 "escape a UTF-8 byte string to JSON (fast path)\n"},
659 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
659 {"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
660 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
660 {"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
661 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
661 {"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
662 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
662 {"fm1readmarkers", fm1readmarkers, METH_VARARGS,
663 "parse v1 obsolete markers\n"},
663 "parse v1 obsolete markers\n"},
664 {NULL, NULL}};
664 {NULL, NULL}};
665
665
666 void dirs_module_init(PyObject *mod);
666 void dirs_module_init(PyObject *mod);
667 void manifest_module_init(PyObject *mod);
667 void manifest_module_init(PyObject *mod);
668 void revlog_module_init(PyObject *mod);
668 void revlog_module_init(PyObject *mod);
669
669
670 static const int version = 16;
670 static const int version = 17;
671
671
672 static void module_init(PyObject *mod)
672 static void module_init(PyObject *mod)
673 {
673 {
674 PyObject *capsule = NULL;
674 PyObject *capsule = NULL;
675 PyModule_AddIntConstant(mod, "version", version);
675 PyModule_AddIntConstant(mod, "version", version);
676
676
677 /* This module constant has two purposes. First, it lets us unit test
677 /* This module constant has two purposes. First, it lets us unit test
678 * the ImportError raised without hard-coding any error text. This
678 * the ImportError raised without hard-coding any error text. This
679 * means we can change the text in the future without breaking tests,
679 * means we can change the text in the future without breaking tests,
680 * even across changesets without a recompile. Second, its presence
680 * even across changesets without a recompile. Second, its presence
681 * can be used to determine whether the version-checking logic is
681 * can be used to determine whether the version-checking logic is
682 * present, which also helps in testing across changesets without a
682 * present, which also helps in testing across changesets without a
683 * recompile. Note that this means the pure-Python version of parsers
683 * recompile. Note that this means the pure-Python version of parsers
684 * should not have this module constant. */
684 * should not have this module constant. */
685 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
685 PyModule_AddStringConstant(mod, "versionerrortext", versionerrortext);
686
686
687 dirs_module_init(mod);
687 dirs_module_init(mod);
688 manifest_module_init(mod);
688 manifest_module_init(mod);
689 revlog_module_init(mod);
689 revlog_module_init(mod);
690
690
691 capsule = PyCapsule_New(
691 capsule = PyCapsule_New(
692 make_dirstate_tuple,
692 make_dirstate_tuple,
693 "mercurial.cext.parsers.make_dirstate_tuple_CAPI", NULL);
693 "mercurial.cext.parsers.make_dirstate_tuple_CAPI", NULL);
694 if (capsule != NULL)
694 if (capsule != NULL)
695 PyModule_AddObject(mod, "make_dirstate_tuple_CAPI", capsule);
695 PyModule_AddObject(mod, "make_dirstate_tuple_CAPI", capsule);
696
696
697 if (PyType_Ready(&dirstateTupleType) < 0) {
697 if (PyType_Ready(&dirstateTupleType) < 0) {
698 return;
698 return;
699 }
699 }
700 Py_INCREF(&dirstateTupleType);
700 Py_INCREF(&dirstateTupleType);
701 PyModule_AddObject(mod, "dirstatetuple",
701 PyModule_AddObject(mod, "dirstatetuple",
702 (PyObject *)&dirstateTupleType);
702 (PyObject *)&dirstateTupleType);
703 }
703 }
704
704
705 static int check_python_version(void)
705 static int check_python_version(void)
706 {
706 {
707 PyObject *sys = PyImport_ImportModule("sys"), *ver;
707 PyObject *sys = PyImport_ImportModule("sys"), *ver;
708 long hexversion;
708 long hexversion;
709 if (!sys) {
709 if (!sys) {
710 return -1;
710 return -1;
711 }
711 }
712 ver = PyObject_GetAttrString(sys, "hexversion");
712 ver = PyObject_GetAttrString(sys, "hexversion");
713 Py_DECREF(sys);
713 Py_DECREF(sys);
714 if (!ver) {
714 if (!ver) {
715 return -1;
715 return -1;
716 }
716 }
717 hexversion = PyInt_AsLong(ver);
717 hexversion = PyInt_AsLong(ver);
718 Py_DECREF(ver);
718 Py_DECREF(ver);
719 /* sys.hexversion is a 32-bit number by default, so the -1 case
719 /* sys.hexversion is a 32-bit number by default, so the -1 case
720 * should only occur in unusual circumstances (e.g. if sys.hexversion
720 * should only occur in unusual circumstances (e.g. if sys.hexversion
721 * is manually set to an invalid value). */
721 * is manually set to an invalid value). */
722 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
722 if ((hexversion == -1) || (hexversion >> 16 != PY_VERSION_HEX >> 16)) {
723 PyErr_Format(PyExc_ImportError,
723 PyErr_Format(PyExc_ImportError,
724 "%s: The Mercurial extension "
724 "%s: The Mercurial extension "
725 "modules were compiled with Python " PY_VERSION
725 "modules were compiled with Python " PY_VERSION
726 ", but "
726 ", but "
727 "Mercurial is currently using Python with "
727 "Mercurial is currently using Python with "
728 "sys.hexversion=%ld: "
728 "sys.hexversion=%ld: "
729 "Python %s\n at: %s",
729 "Python %s\n at: %s",
730 versionerrortext, hexversion, Py_GetVersion(),
730 versionerrortext, hexversion, Py_GetVersion(),
731 Py_GetProgramFullPath());
731 Py_GetProgramFullPath());
732 return -1;
732 return -1;
733 }
733 }
734 return 0;
734 return 0;
735 }
735 }
736
736
737 #ifdef IS_PY3K
737 #ifdef IS_PY3K
738 static struct PyModuleDef parsers_module = {PyModuleDef_HEAD_INIT, "parsers",
738 static struct PyModuleDef parsers_module = {PyModuleDef_HEAD_INIT, "parsers",
739 parsers_doc, -1, methods};
739 parsers_doc, -1, methods};
740
740
741 PyMODINIT_FUNC PyInit_parsers(void)
741 PyMODINIT_FUNC PyInit_parsers(void)
742 {
742 {
743 PyObject *mod;
743 PyObject *mod;
744
744
745 if (check_python_version() == -1)
745 if (check_python_version() == -1)
746 return NULL;
746 return NULL;
747 mod = PyModule_Create(&parsers_module);
747 mod = PyModule_Create(&parsers_module);
748 module_init(mod);
748 module_init(mod);
749 return mod;
749 return mod;
750 }
750 }
751 #else
751 #else
752 PyMODINIT_FUNC initparsers(void)
752 PyMODINIT_FUNC initparsers(void)
753 {
753 {
754 PyObject *mod;
754 PyObject *mod;
755
755
756 if (check_python_version() == -1) {
756 if (check_python_version() == -1) {
757 return;
757 return;
758 }
758 }
759 mod = Py_InitModule3("parsers", methods, parsers_doc);
759 mod = Py_InitModule3("parsers", methods, parsers_doc);
760 module_init(mod);
760 module_init(mod);
761 }
761 }
762 #endif
762 #endif
@@ -1,2918 +1,2959 b''
1 /*
1 /*
2 parsers.c - efficient content parsing
2 parsers.c - efficient content parsing
3
3
4 Copyright 2008 Matt Mackall <mpm@selenic.com> and others
4 Copyright 2008 Matt Mackall <mpm@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 <assert.h>
12 #include <assert.h>
13 #include <ctype.h>
13 #include <ctype.h>
14 #include <limits.h>
14 #include <limits.h>
15 #include <stddef.h>
15 #include <stddef.h>
16 #include <stdlib.h>
16 #include <stdlib.h>
17 #include <string.h>
17 #include <string.h>
18
18
19 #include "bitmanipulation.h"
19 #include "bitmanipulation.h"
20 #include "charencode.h"
20 #include "charencode.h"
21 #include "revlog.h"
21 #include "revlog.h"
22 #include "util.h"
22 #include "util.h"
23
23
24 #ifdef IS_PY3K
24 #ifdef IS_PY3K
25 /* The mapping of Python types is meant to be temporary to get Python
25 /* The mapping of Python types is meant to be temporary to get Python
26 * 3 to compile. We should remove this once Python 3 support is fully
26 * 3 to compile. We should remove this once Python 3 support is fully
27 * supported and proper types are used in the extensions themselves. */
27 * supported and proper types are used in the extensions themselves. */
28 #define PyInt_Check PyLong_Check
28 #define PyInt_Check PyLong_Check
29 #define PyInt_FromLong PyLong_FromLong
29 #define PyInt_FromLong PyLong_FromLong
30 #define PyInt_FromSsize_t PyLong_FromSsize_t
30 #define PyInt_FromSsize_t PyLong_FromSsize_t
31 #define PyInt_AsLong PyLong_AsLong
31 #define PyInt_AsLong PyLong_AsLong
32 #endif
32 #endif
33
33
34 typedef struct indexObjectStruct indexObject;
34 typedef struct indexObjectStruct indexObject;
35
35
36 typedef struct {
36 typedef struct {
37 int children[16];
37 int children[16];
38 } nodetreenode;
38 } nodetreenode;
39
39
40 typedef struct {
40 typedef struct {
41 int abi_version;
41 int abi_version;
42 Py_ssize_t (*index_length)(const indexObject *);
42 Py_ssize_t (*index_length)(const indexObject *);
43 const char *(*index_node)(indexObject *, Py_ssize_t);
43 const char *(*index_node)(indexObject *, Py_ssize_t);
44 int (*index_parents)(PyObject *, int, int *);
44 int (*index_parents)(PyObject *, int, int *);
45 } Revlog_CAPI;
45 } Revlog_CAPI;
46
46
47 /*
47 /*
48 * A base-16 trie for fast node->rev mapping.
48 * A base-16 trie for fast node->rev mapping.
49 *
49 *
50 * Positive value is index of the next node in the trie
50 * Positive value is index of the next node in the trie
51 * Negative value is a leaf: -(rev + 2)
51 * Negative value is a leaf: -(rev + 2)
52 * Zero is empty
52 * Zero is empty
53 */
53 */
54 typedef struct {
54 typedef struct {
55 indexObject *index;
55 indexObject *index;
56 nodetreenode *nodes;
56 nodetreenode *nodes;
57 unsigned length; /* # nodes in use */
57 unsigned length; /* # nodes in use */
58 unsigned capacity; /* # nodes allocated */
58 unsigned capacity; /* # nodes allocated */
59 int depth; /* maximum depth of tree */
59 int depth; /* maximum depth of tree */
60 int splits; /* # splits performed */
60 int splits; /* # splits performed */
61 } nodetree;
61 } nodetree;
62
62
63 typedef struct {
63 typedef struct {
64 PyObject_HEAD /* ; */
64 PyObject_HEAD /* ; */
65 nodetree nt;
65 nodetree nt;
66 } nodetreeObject;
66 } nodetreeObject;
67
67
68 /*
68 /*
69 * This class has two behaviors.
69 * This class has two behaviors.
70 *
70 *
71 * When used in a list-like way (with integer keys), we decode an
71 * When used in a list-like way (with integer keys), we decode an
72 * entry in a RevlogNG index file on demand. We have limited support for
72 * entry in a RevlogNG index file on demand. We have limited support for
73 * integer-keyed insert and delete, only at elements right before the
73 * integer-keyed insert and delete, only at elements right before the
74 * end.
74 * end.
75 *
75 *
76 * With string keys, we lazily perform a reverse mapping from node to
76 * With string keys, we lazily perform a reverse mapping from node to
77 * rev, using a base-16 trie.
77 * rev, using a base-16 trie.
78 */
78 */
79 struct indexObjectStruct {
79 struct indexObjectStruct {
80 PyObject_HEAD
80 PyObject_HEAD
81 /* Type-specific fields go here. */
81 /* Type-specific fields go here. */
82 PyObject *data; /* raw bytes of index */
82 PyObject *data; /* raw bytes of index */
83 Py_buffer buf; /* buffer of data */
83 Py_buffer buf; /* buffer of data */
84 PyObject **cache; /* cached tuples */
84 PyObject **cache; /* cached tuples */
85 const char **offsets; /* populated on demand */
85 const char **offsets; /* populated on demand */
86 Py_ssize_t raw_length; /* original number of elements */
86 Py_ssize_t raw_length; /* original number of elements */
87 Py_ssize_t length; /* current number of elements */
87 Py_ssize_t length; /* current number of elements */
88 PyObject *added; /* populated on demand */
88 PyObject *added; /* populated on demand */
89 PyObject *headrevs; /* cache, invalidated on changes */
89 PyObject *headrevs; /* cache, invalidated on changes */
90 PyObject *filteredrevs; /* filtered revs set */
90 PyObject *filteredrevs; /* filtered revs set */
91 nodetree nt; /* base-16 trie */
91 nodetree nt; /* base-16 trie */
92 int ntinitialized; /* 0 or 1 */
92 int ntinitialized; /* 0 or 1 */
93 int ntrev; /* last rev scanned */
93 int ntrev; /* last rev scanned */
94 int ntlookups; /* # lookups */
94 int ntlookups; /* # lookups */
95 int ntmisses; /* # lookups that miss the cache */
95 int ntmisses; /* # lookups that miss the cache */
96 int inlined;
96 int inlined;
97 };
97 };
98
98
99 static Py_ssize_t index_length(const indexObject *self)
99 static Py_ssize_t index_length(const indexObject *self)
100 {
100 {
101 if (self->added == NULL)
101 if (self->added == NULL)
102 return self->length;
102 return self->length;
103 return self->length + PyList_GET_SIZE(self->added);
103 return self->length + PyList_GET_SIZE(self->added);
104 }
104 }
105
105
106 static PyObject *nullentry = NULL;
106 static PyObject *nullentry = NULL;
107 static const char nullid[20] = {0};
107 static const char nullid[20] = {0};
108 static const Py_ssize_t nullrev = -1;
108 static const Py_ssize_t nullrev = -1;
109
109
110 static Py_ssize_t inline_scan(indexObject *self, const char **offsets);
110 static Py_ssize_t inline_scan(indexObject *self, const char **offsets);
111
111
112 static int index_find_node(indexObject *self, const char *node,
113 Py_ssize_t nodelen);
114
112 #if LONG_MAX == 0x7fffffffL
115 #if LONG_MAX == 0x7fffffffL
113 static const char *const tuple_format = PY23("Kiiiiiis#", "Kiiiiiiy#");
116 static const char *const tuple_format = PY23("Kiiiiiis#", "Kiiiiiiy#");
114 #else
117 #else
115 static const char *const tuple_format = PY23("kiiiiiis#", "kiiiiiiy#");
118 static const char *const tuple_format = PY23("kiiiiiis#", "kiiiiiiy#");
116 #endif
119 #endif
117
120
118 /* A RevlogNG v1 index entry is 64 bytes long. */
121 /* A RevlogNG v1 index entry is 64 bytes long. */
119 static const long v1_hdrsize = 64;
122 static const long v1_hdrsize = 64;
120
123
121 static void raise_revlog_error(void)
124 static void raise_revlog_error(void)
122 {
125 {
123 PyObject *mod = NULL, *dict = NULL, *errclass = NULL;
126 PyObject *mod = NULL, *dict = NULL, *errclass = NULL;
124
127
125 mod = PyImport_ImportModule("mercurial.error");
128 mod = PyImport_ImportModule("mercurial.error");
126 if (mod == NULL) {
129 if (mod == NULL) {
127 goto cleanup;
130 goto cleanup;
128 }
131 }
129
132
130 dict = PyModule_GetDict(mod);
133 dict = PyModule_GetDict(mod);
131 if (dict == NULL) {
134 if (dict == NULL) {
132 goto cleanup;
135 goto cleanup;
133 }
136 }
134 Py_INCREF(dict);
137 Py_INCREF(dict);
135
138
136 errclass = PyDict_GetItemString(dict, "RevlogError");
139 errclass = PyDict_GetItemString(dict, "RevlogError");
137 if (errclass == NULL) {
140 if (errclass == NULL) {
138 PyErr_SetString(PyExc_SystemError,
141 PyErr_SetString(PyExc_SystemError,
139 "could not find RevlogError");
142 "could not find RevlogError");
140 goto cleanup;
143 goto cleanup;
141 }
144 }
142
145
143 /* value of exception is ignored by callers */
146 /* value of exception is ignored by callers */
144 PyErr_SetString(errclass, "RevlogError");
147 PyErr_SetString(errclass, "RevlogError");
145
148
146 cleanup:
149 cleanup:
147 Py_XDECREF(dict);
150 Py_XDECREF(dict);
148 Py_XDECREF(mod);
151 Py_XDECREF(mod);
149 }
152 }
150
153
151 /*
154 /*
152 * Return a pointer to the beginning of a RevlogNG record.
155 * Return a pointer to the beginning of a RevlogNG record.
153 */
156 */
154 static const char *index_deref(indexObject *self, Py_ssize_t pos)
157 static const char *index_deref(indexObject *self, Py_ssize_t pos)
155 {
158 {
156 if (self->inlined && pos > 0) {
159 if (self->inlined && pos > 0) {
157 if (self->offsets == NULL) {
160 if (self->offsets == NULL) {
158 Py_ssize_t ret;
161 Py_ssize_t ret;
159 self->offsets = PyMem_Malloc(self->raw_length *
162 self->offsets = PyMem_Malloc(self->raw_length *
160 sizeof(*self->offsets));
163 sizeof(*self->offsets));
161 if (self->offsets == NULL)
164 if (self->offsets == NULL)
162 return (const char *)PyErr_NoMemory();
165 return (const char *)PyErr_NoMemory();
163 ret = inline_scan(self, self->offsets);
166 ret = inline_scan(self, self->offsets);
164 if (ret == -1) {
167 if (ret == -1) {
165 return NULL;
168 return NULL;
166 };
169 };
167 }
170 }
168 return self->offsets[pos];
171 return self->offsets[pos];
169 }
172 }
170
173
171 return (const char *)(self->buf.buf) + pos * v1_hdrsize;
174 return (const char *)(self->buf.buf) + pos * v1_hdrsize;
172 }
175 }
173
176
174 /*
177 /*
175 * Get parents of the given rev.
178 * Get parents of the given rev.
176 *
179 *
177 * The specified rev must be valid and must not be nullrev. A returned
180 * The specified rev must be valid and must not be nullrev. A returned
178 * parent revision may be nullrev, but is guaranteed to be in valid range.
181 * parent revision may be nullrev, but is guaranteed to be in valid range.
179 */
182 */
180 static inline int index_get_parents(indexObject *self, Py_ssize_t rev, int *ps,
183 static inline int index_get_parents(indexObject *self, Py_ssize_t rev, int *ps,
181 int maxrev)
184 int maxrev)
182 {
185 {
183 if (rev >= self->length) {
186 if (rev >= self->length) {
184 long tmp;
187 long tmp;
185 PyObject *tuple =
188 PyObject *tuple =
186 PyList_GET_ITEM(self->added, rev - self->length);
189 PyList_GET_ITEM(self->added, rev - self->length);
187 if (!pylong_to_long(PyTuple_GET_ITEM(tuple, 5), &tmp)) {
190 if (!pylong_to_long(PyTuple_GET_ITEM(tuple, 5), &tmp)) {
188 return -1;
191 return -1;
189 }
192 }
190 ps[0] = (int)tmp;
193 ps[0] = (int)tmp;
191 if (!pylong_to_long(PyTuple_GET_ITEM(tuple, 6), &tmp)) {
194 if (!pylong_to_long(PyTuple_GET_ITEM(tuple, 6), &tmp)) {
192 return -1;
195 return -1;
193 }
196 }
194 ps[1] = (int)tmp;
197 ps[1] = (int)tmp;
195 } else {
198 } else {
196 const char *data = index_deref(self, rev);
199 const char *data = index_deref(self, rev);
197 ps[0] = getbe32(data + 24);
200 ps[0] = getbe32(data + 24);
198 ps[1] = getbe32(data + 28);
201 ps[1] = getbe32(data + 28);
199 }
202 }
200 /* If index file is corrupted, ps[] may point to invalid revisions. So
203 /* If index file is corrupted, ps[] may point to invalid revisions. So
201 * there is a risk of buffer overflow to trust them unconditionally. */
204 * there is a risk of buffer overflow to trust them unconditionally. */
202 if (ps[0] < -1 || ps[0] > maxrev || ps[1] < -1 || ps[1] > maxrev) {
205 if (ps[0] < -1 || ps[0] > maxrev || ps[1] < -1 || ps[1] > maxrev) {
203 PyErr_SetString(PyExc_ValueError, "parent out of range");
206 PyErr_SetString(PyExc_ValueError, "parent out of range");
204 return -1;
207 return -1;
205 }
208 }
206 return 0;
209 return 0;
207 }
210 }
208
211
209 /*
212 /*
210 * Get parents of the given rev.
213 * Get parents of the given rev.
211 *
214 *
212 * If the specified rev is out of range, IndexError will be raised. If the
215 * If the specified rev is out of range, IndexError will be raised. If the
213 * revlog entry is corrupted, ValueError may be raised.
216 * revlog entry is corrupted, ValueError may be raised.
214 *
217 *
215 * Returns 0 on success or -1 on failure.
218 * Returns 0 on success or -1 on failure.
216 */
219 */
217 static int HgRevlogIndex_GetParents(PyObject *op, int rev, int *ps)
220 static int HgRevlogIndex_GetParents(PyObject *op, int rev, int *ps)
218 {
221 {
219 int tiprev;
222 int tiprev;
220 if (!op || !HgRevlogIndex_Check(op) || !ps) {
223 if (!op || !HgRevlogIndex_Check(op) || !ps) {
221 PyErr_BadInternalCall();
224 PyErr_BadInternalCall();
222 return -1;
225 return -1;
223 }
226 }
224 tiprev = (int)index_length((indexObject *)op) - 1;
227 tiprev = (int)index_length((indexObject *)op) - 1;
225 if (rev < -1 || rev > tiprev) {
228 if (rev < -1 || rev > tiprev) {
226 PyErr_Format(PyExc_IndexError, "rev out of range: %d", rev);
229 PyErr_Format(PyExc_IndexError, "rev out of range: %d", rev);
227 return -1;
230 return -1;
228 } else if (rev == -1) {
231 } else if (rev == -1) {
229 ps[0] = ps[1] = -1;
232 ps[0] = ps[1] = -1;
230 return 0;
233 return 0;
231 } else {
234 } else {
232 return index_get_parents((indexObject *)op, rev, ps, tiprev);
235 return index_get_parents((indexObject *)op, rev, ps, tiprev);
233 }
236 }
234 }
237 }
235
238
236 static inline int64_t index_get_start(indexObject *self, Py_ssize_t rev)
239 static inline int64_t index_get_start(indexObject *self, Py_ssize_t rev)
237 {
240 {
238 uint64_t offset;
241 uint64_t offset;
239 if (rev == nullrev) {
242 if (rev == nullrev) {
240 return 0;
243 return 0;
241 }
244 }
242 if (rev >= self->length) {
245 if (rev >= self->length) {
243 PyObject *tuple;
246 PyObject *tuple;
244 PyObject *pylong;
247 PyObject *pylong;
245 PY_LONG_LONG tmp;
248 PY_LONG_LONG tmp;
246 tuple = PyList_GET_ITEM(self->added, rev - self->length);
249 tuple = PyList_GET_ITEM(self->added, rev - self->length);
247 pylong = PyTuple_GET_ITEM(tuple, 0);
250 pylong = PyTuple_GET_ITEM(tuple, 0);
248 tmp = PyLong_AsLongLong(pylong);
251 tmp = PyLong_AsLongLong(pylong);
249 if (tmp == -1 && PyErr_Occurred()) {
252 if (tmp == -1 && PyErr_Occurred()) {
250 return -1;
253 return -1;
251 }
254 }
252 if (tmp < 0) {
255 if (tmp < 0) {
253 PyErr_Format(PyExc_OverflowError,
256 PyErr_Format(PyExc_OverflowError,
254 "revlog entry size out of bound (%lld)",
257 "revlog entry size out of bound (%lld)",
255 (long long)tmp);
258 (long long)tmp);
256 return -1;
259 return -1;
257 }
260 }
258 offset = (uint64_t)tmp;
261 offset = (uint64_t)tmp;
259 } else {
262 } else {
260 const char *data = index_deref(self, rev);
263 const char *data = index_deref(self, rev);
261 offset = getbe32(data + 4);
264 offset = getbe32(data + 4);
262 if (rev == 0) {
265 if (rev == 0) {
263 /* mask out version number for the first entry */
266 /* mask out version number for the first entry */
264 offset &= 0xFFFF;
267 offset &= 0xFFFF;
265 } else {
268 } else {
266 uint32_t offset_high = getbe32(data);
269 uint32_t offset_high = getbe32(data);
267 offset |= ((uint64_t)offset_high) << 32;
270 offset |= ((uint64_t)offset_high) << 32;
268 }
271 }
269 }
272 }
270 return (int64_t)(offset >> 16);
273 return (int64_t)(offset >> 16);
271 }
274 }
272
275
273 static inline int index_get_length(indexObject *self, Py_ssize_t rev)
276 static inline int index_get_length(indexObject *self, Py_ssize_t rev)
274 {
277 {
275 if (rev == nullrev) {
278 if (rev == nullrev) {
276 return 0;
279 return 0;
277 }
280 }
278 if (rev >= self->length) {
281 if (rev >= self->length) {
279 PyObject *tuple;
282 PyObject *tuple;
280 PyObject *pylong;
283 PyObject *pylong;
281 long ret;
284 long ret;
282 tuple = PyList_GET_ITEM(self->added, rev - self->length);
285 tuple = PyList_GET_ITEM(self->added, rev - self->length);
283 pylong = PyTuple_GET_ITEM(tuple, 1);
286 pylong = PyTuple_GET_ITEM(tuple, 1);
284 ret = PyInt_AsLong(pylong);
287 ret = PyInt_AsLong(pylong);
285 if (ret == -1 && PyErr_Occurred()) {
288 if (ret == -1 && PyErr_Occurred()) {
286 return -1;
289 return -1;
287 }
290 }
288 if (ret < 0 || ret > (long)INT_MAX) {
291 if (ret < 0 || ret > (long)INT_MAX) {
289 PyErr_Format(PyExc_OverflowError,
292 PyErr_Format(PyExc_OverflowError,
290 "revlog entry size out of bound (%ld)",
293 "revlog entry size out of bound (%ld)",
291 ret);
294 ret);
292 return -1;
295 return -1;
293 }
296 }
294 return (int)ret;
297 return (int)ret;
295 } else {
298 } else {
296 const char *data = index_deref(self, rev);
299 const char *data = index_deref(self, rev);
297 int tmp = (int)getbe32(data + 8);
300 int tmp = (int)getbe32(data + 8);
298 if (tmp < 0) {
301 if (tmp < 0) {
299 PyErr_Format(PyExc_OverflowError,
302 PyErr_Format(PyExc_OverflowError,
300 "revlog entry size out of bound (%d)",
303 "revlog entry size out of bound (%d)",
301 tmp);
304 tmp);
302 return -1;
305 return -1;
303 }
306 }
304 return tmp;
307 return tmp;
305 }
308 }
306 }
309 }
307
310
308 /*
311 /*
309 * RevlogNG format (all in big endian, data may be inlined):
312 * RevlogNG format (all in big endian, data may be inlined):
310 * 6 bytes: offset
313 * 6 bytes: offset
311 * 2 bytes: flags
314 * 2 bytes: flags
312 * 4 bytes: compressed length
315 * 4 bytes: compressed length
313 * 4 bytes: uncompressed length
316 * 4 bytes: uncompressed length
314 * 4 bytes: base revision
317 * 4 bytes: base revision
315 * 4 bytes: link revision
318 * 4 bytes: link revision
316 * 4 bytes: parent 1 revision
319 * 4 bytes: parent 1 revision
317 * 4 bytes: parent 2 revision
320 * 4 bytes: parent 2 revision
318 * 32 bytes: nodeid (only 20 bytes used)
321 * 32 bytes: nodeid (only 20 bytes used)
319 */
322 */
320 static PyObject *index_get(indexObject *self, Py_ssize_t pos)
323 static PyObject *index_get(indexObject *self, Py_ssize_t pos)
321 {
324 {
322 uint64_t offset_flags;
325 uint64_t offset_flags;
323 int comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2;
326 int comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2;
324 const char *c_node_id;
327 const char *c_node_id;
325 const char *data;
328 const char *data;
326 Py_ssize_t length = index_length(self);
329 Py_ssize_t length = index_length(self);
327 PyObject *entry;
330 PyObject *entry;
328
331
329 if (pos == nullrev) {
332 if (pos == nullrev) {
330 Py_INCREF(nullentry);
333 Py_INCREF(nullentry);
331 return nullentry;
334 return nullentry;
332 }
335 }
333
336
334 if (pos < 0 || pos >= length) {
337 if (pos < 0 || pos >= length) {
335 PyErr_SetString(PyExc_IndexError, "revlog index out of range");
338 PyErr_SetString(PyExc_IndexError, "revlog index out of range");
336 return NULL;
339 return NULL;
337 }
340 }
338
341
339 if (pos >= self->length) {
342 if (pos >= self->length) {
340 PyObject *obj;
343 PyObject *obj;
341 obj = PyList_GET_ITEM(self->added, pos - self->length);
344 obj = PyList_GET_ITEM(self->added, pos - self->length);
342 Py_INCREF(obj);
345 Py_INCREF(obj);
343 return obj;
346 return obj;
344 }
347 }
345
348
346 if (self->cache) {
349 if (self->cache) {
347 if (self->cache[pos]) {
350 if (self->cache[pos]) {
348 Py_INCREF(self->cache[pos]);
351 Py_INCREF(self->cache[pos]);
349 return self->cache[pos];
352 return self->cache[pos];
350 }
353 }
351 } else {
354 } else {
352 self->cache = calloc(self->raw_length, sizeof(PyObject *));
355 self->cache = calloc(self->raw_length, sizeof(PyObject *));
353 if (self->cache == NULL)
356 if (self->cache == NULL)
354 return PyErr_NoMemory();
357 return PyErr_NoMemory();
355 }
358 }
356
359
357 data = index_deref(self, pos);
360 data = index_deref(self, pos);
358 if (data == NULL)
361 if (data == NULL)
359 return NULL;
362 return NULL;
360
363
361 offset_flags = getbe32(data + 4);
364 offset_flags = getbe32(data + 4);
362 if (pos == 0) /* mask out version number for the first entry */
365 if (pos == 0) /* mask out version number for the first entry */
363 offset_flags &= 0xFFFF;
366 offset_flags &= 0xFFFF;
364 else {
367 else {
365 uint32_t offset_high = getbe32(data);
368 uint32_t offset_high = getbe32(data);
366 offset_flags |= ((uint64_t)offset_high) << 32;
369 offset_flags |= ((uint64_t)offset_high) << 32;
367 }
370 }
368
371
369 comp_len = getbe32(data + 8);
372 comp_len = getbe32(data + 8);
370 uncomp_len = getbe32(data + 12);
373 uncomp_len = getbe32(data + 12);
371 base_rev = getbe32(data + 16);
374 base_rev = getbe32(data + 16);
372 link_rev = getbe32(data + 20);
375 link_rev = getbe32(data + 20);
373 parent_1 = getbe32(data + 24);
376 parent_1 = getbe32(data + 24);
374 parent_2 = getbe32(data + 28);
377 parent_2 = getbe32(data + 28);
375 c_node_id = data + 32;
378 c_node_id = data + 32;
376
379
377 entry = Py_BuildValue(tuple_format, offset_flags, comp_len, uncomp_len,
380 entry = Py_BuildValue(tuple_format, offset_flags, comp_len, uncomp_len,
378 base_rev, link_rev, parent_1, parent_2, c_node_id,
381 base_rev, link_rev, parent_1, parent_2, c_node_id,
379 (Py_ssize_t)20);
382 (Py_ssize_t)20);
380
383
381 if (entry) {
384 if (entry) {
382 PyObject_GC_UnTrack(entry);
385 PyObject_GC_UnTrack(entry);
383 Py_INCREF(entry);
386 Py_INCREF(entry);
384 }
387 }
385
388
386 self->cache[pos] = entry;
389 self->cache[pos] = entry;
387
390
388 return entry;
391 return entry;
389 }
392 }
390
393
391 /*
394 /*
392 * Return the 20-byte SHA of the node corresponding to the given rev.
395 * Return the 20-byte SHA of the node corresponding to the given rev.
393 */
396 */
394 static const char *index_node(indexObject *self, Py_ssize_t pos)
397 static const char *index_node(indexObject *self, Py_ssize_t pos)
395 {
398 {
396 Py_ssize_t length = index_length(self);
399 Py_ssize_t length = index_length(self);
397 const char *data;
400 const char *data;
398
401
399 if (pos == nullrev)
402 if (pos == nullrev)
400 return nullid;
403 return nullid;
401
404
402 if (pos >= length)
405 if (pos >= length)
403 return NULL;
406 return NULL;
404
407
405 if (pos >= self->length) {
408 if (pos >= self->length) {
406 PyObject *tuple, *str;
409 PyObject *tuple, *str;
407 tuple = PyList_GET_ITEM(self->added, pos - self->length);
410 tuple = PyList_GET_ITEM(self->added, pos - self->length);
408 str = PyTuple_GetItem(tuple, 7);
411 str = PyTuple_GetItem(tuple, 7);
409 return str ? PyBytes_AS_STRING(str) : NULL;
412 return str ? PyBytes_AS_STRING(str) : NULL;
410 }
413 }
411
414
412 data = index_deref(self, pos);
415 data = index_deref(self, pos);
413 return data ? data + 32 : NULL;
416 return data ? data + 32 : NULL;
414 }
417 }
415
418
416 /*
419 /*
417 * Return the 20-byte SHA of the node corresponding to the given rev. The
420 * Return the 20-byte SHA of the node corresponding to the given rev. The
418 * rev is assumed to be existing. If not, an exception is set.
421 * rev is assumed to be existing. If not, an exception is set.
419 */
422 */
420 static const char *index_node_existing(indexObject *self, Py_ssize_t pos)
423 static const char *index_node_existing(indexObject *self, Py_ssize_t pos)
421 {
424 {
422 const char *node = index_node(self, pos);
425 const char *node = index_node(self, pos);
423 if (node == NULL) {
426 if (node == NULL) {
424 PyErr_Format(PyExc_IndexError, "could not access rev %d",
427 PyErr_Format(PyExc_IndexError, "could not access rev %d",
425 (int)pos);
428 (int)pos);
426 }
429 }
427 return node;
430 return node;
428 }
431 }
429
432
430 static int nt_insert(nodetree *self, const char *node, int rev);
433 static int nt_insert(nodetree *self, const char *node, int rev);
431
434
432 static int node_check(PyObject *obj, char **node)
435 static int node_check(PyObject *obj, char **node)
433 {
436 {
434 Py_ssize_t nodelen;
437 Py_ssize_t nodelen;
435 if (PyBytes_AsStringAndSize(obj, node, &nodelen) == -1)
438 if (PyBytes_AsStringAndSize(obj, node, &nodelen) == -1)
436 return -1;
439 return -1;
437 if (nodelen == 20)
440 if (nodelen == 20)
438 return 0;
441 return 0;
439 PyErr_SetString(PyExc_ValueError, "20-byte hash required");
442 PyErr_SetString(PyExc_ValueError, "20-byte hash required");
440 return -1;
443 return -1;
441 }
444 }
442
445
443 static PyObject *index_append(indexObject *self, PyObject *obj)
446 static PyObject *index_append(indexObject *self, PyObject *obj)
444 {
447 {
445 char *node;
448 char *node;
446 Py_ssize_t len;
449 Py_ssize_t len;
447
450
448 if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 8) {
451 if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 8) {
449 PyErr_SetString(PyExc_TypeError, "8-tuple required");
452 PyErr_SetString(PyExc_TypeError, "8-tuple required");
450 return NULL;
453 return NULL;
451 }
454 }
452
455
453 if (node_check(PyTuple_GET_ITEM(obj, 7), &node) == -1)
456 if (node_check(PyTuple_GET_ITEM(obj, 7), &node) == -1)
454 return NULL;
457 return NULL;
455
458
456 len = index_length(self);
459 len = index_length(self);
457
460
458 if (self->added == NULL) {
461 if (self->added == NULL) {
459 self->added = PyList_New(0);
462 self->added = PyList_New(0);
460 if (self->added == NULL)
463 if (self->added == NULL)
461 return NULL;
464 return NULL;
462 }
465 }
463
466
464 if (PyList_Append(self->added, obj) == -1)
467 if (PyList_Append(self->added, obj) == -1)
465 return NULL;
468 return NULL;
466
469
467 if (self->ntinitialized)
470 if (self->ntinitialized)
468 nt_insert(&self->nt, node, (int)len);
471 nt_insert(&self->nt, node, (int)len);
469
472
470 Py_CLEAR(self->headrevs);
473 Py_CLEAR(self->headrevs);
471 Py_RETURN_NONE;
474 Py_RETURN_NONE;
472 }
475 }
473
476
474 static PyObject *index_stats(indexObject *self)
477 static PyObject *index_stats(indexObject *self)
475 {
478 {
476 PyObject *obj = PyDict_New();
479 PyObject *obj = PyDict_New();
477 PyObject *s = NULL;
480 PyObject *s = NULL;
478 PyObject *t = NULL;
481 PyObject *t = NULL;
479
482
480 if (obj == NULL)
483 if (obj == NULL)
481 return NULL;
484 return NULL;
482
485
483 #define istat(__n, __d) \
486 #define istat(__n, __d) \
484 do { \
487 do { \
485 s = PyBytes_FromString(__d); \
488 s = PyBytes_FromString(__d); \
486 t = PyInt_FromSsize_t(self->__n); \
489 t = PyInt_FromSsize_t(self->__n); \
487 if (!s || !t) \
490 if (!s || !t) \
488 goto bail; \
491 goto bail; \
489 if (PyDict_SetItem(obj, s, t) == -1) \
492 if (PyDict_SetItem(obj, s, t) == -1) \
490 goto bail; \
493 goto bail; \
491 Py_CLEAR(s); \
494 Py_CLEAR(s); \
492 Py_CLEAR(t); \
495 Py_CLEAR(t); \
493 } while (0)
496 } while (0)
494
497
495 if (self->added) {
498 if (self->added) {
496 Py_ssize_t len = PyList_GET_SIZE(self->added);
499 Py_ssize_t len = PyList_GET_SIZE(self->added);
497 s = PyBytes_FromString("index entries added");
500 s = PyBytes_FromString("index entries added");
498 t = PyInt_FromSsize_t(len);
501 t = PyInt_FromSsize_t(len);
499 if (!s || !t)
502 if (!s || !t)
500 goto bail;
503 goto bail;
501 if (PyDict_SetItem(obj, s, t) == -1)
504 if (PyDict_SetItem(obj, s, t) == -1)
502 goto bail;
505 goto bail;
503 Py_CLEAR(s);
506 Py_CLEAR(s);
504 Py_CLEAR(t);
507 Py_CLEAR(t);
505 }
508 }
506
509
507 if (self->raw_length != self->length)
510 if (self->raw_length != self->length)
508 istat(raw_length, "revs on disk");
511 istat(raw_length, "revs on disk");
509 istat(length, "revs in memory");
512 istat(length, "revs in memory");
510 istat(ntlookups, "node trie lookups");
513 istat(ntlookups, "node trie lookups");
511 istat(ntmisses, "node trie misses");
514 istat(ntmisses, "node trie misses");
512 istat(ntrev, "node trie last rev scanned");
515 istat(ntrev, "node trie last rev scanned");
513 if (self->ntinitialized) {
516 if (self->ntinitialized) {
514 istat(nt.capacity, "node trie capacity");
517 istat(nt.capacity, "node trie capacity");
515 istat(nt.depth, "node trie depth");
518 istat(nt.depth, "node trie depth");
516 istat(nt.length, "node trie count");
519 istat(nt.length, "node trie count");
517 istat(nt.splits, "node trie splits");
520 istat(nt.splits, "node trie splits");
518 }
521 }
519
522
520 #undef istat
523 #undef istat
521
524
522 return obj;
525 return obj;
523
526
524 bail:
527 bail:
525 Py_XDECREF(obj);
528 Py_XDECREF(obj);
526 Py_XDECREF(s);
529 Py_XDECREF(s);
527 Py_XDECREF(t);
530 Py_XDECREF(t);
528 return NULL;
531 return NULL;
529 }
532 }
530
533
531 /*
534 /*
532 * When we cache a list, we want to be sure the caller can't mutate
535 * When we cache a list, we want to be sure the caller can't mutate
533 * the cached copy.
536 * the cached copy.
534 */
537 */
535 static PyObject *list_copy(PyObject *list)
538 static PyObject *list_copy(PyObject *list)
536 {
539 {
537 Py_ssize_t len = PyList_GET_SIZE(list);
540 Py_ssize_t len = PyList_GET_SIZE(list);
538 PyObject *newlist = PyList_New(len);
541 PyObject *newlist = PyList_New(len);
539 Py_ssize_t i;
542 Py_ssize_t i;
540
543
541 if (newlist == NULL)
544 if (newlist == NULL)
542 return NULL;
545 return NULL;
543
546
544 for (i = 0; i < len; i++) {
547 for (i = 0; i < len; i++) {
545 PyObject *obj = PyList_GET_ITEM(list, i);
548 PyObject *obj = PyList_GET_ITEM(list, i);
546 Py_INCREF(obj);
549 Py_INCREF(obj);
547 PyList_SET_ITEM(newlist, i, obj);
550 PyList_SET_ITEM(newlist, i, obj);
548 }
551 }
549
552
550 return newlist;
553 return newlist;
551 }
554 }
552
555
553 static int check_filter(PyObject *filter, Py_ssize_t arg)
556 static int check_filter(PyObject *filter, Py_ssize_t arg)
554 {
557 {
555 if (filter) {
558 if (filter) {
556 PyObject *arglist, *result;
559 PyObject *arglist, *result;
557 int isfiltered;
560 int isfiltered;
558
561
559 arglist = Py_BuildValue("(n)", arg);
562 arglist = Py_BuildValue("(n)", arg);
560 if (!arglist) {
563 if (!arglist) {
561 return -1;
564 return -1;
562 }
565 }
563
566
564 result = PyEval_CallObject(filter, arglist);
567 result = PyEval_CallObject(filter, arglist);
565 Py_DECREF(arglist);
568 Py_DECREF(arglist);
566 if (!result) {
569 if (!result) {
567 return -1;
570 return -1;
568 }
571 }
569
572
570 /* PyObject_IsTrue returns 1 if true, 0 if false, -1 if error,
573 /* PyObject_IsTrue returns 1 if true, 0 if false, -1 if error,
571 * same as this function, so we can just return it directly.*/
574 * same as this function, so we can just return it directly.*/
572 isfiltered = PyObject_IsTrue(result);
575 isfiltered = PyObject_IsTrue(result);
573 Py_DECREF(result);
576 Py_DECREF(result);
574 return isfiltered;
577 return isfiltered;
575 } else {
578 } else {
576 return 0;
579 return 0;
577 }
580 }
578 }
581 }
579
582
580 static Py_ssize_t add_roots_get_min(indexObject *self, PyObject *list,
581 Py_ssize_t marker, char *phases)
582 {
583 PyObject *iter = NULL;
584 PyObject *iter_item = NULL;
585 Py_ssize_t min_idx = index_length(self) + 2;
586 long iter_item_long;
587
588 if (PyList_GET_SIZE(list) != 0) {
589 iter = PyObject_GetIter(list);
590 if (iter == NULL)
591 return -2;
592 while ((iter_item = PyIter_Next(iter))) {
593 if (!pylong_to_long(iter_item, &iter_item_long)) {
594 Py_DECREF(iter_item);
595 return -2;
596 }
597 Py_DECREF(iter_item);
598 if (iter_item_long < min_idx)
599 min_idx = iter_item_long;
600 phases[iter_item_long] = (char)marker;
601 }
602 Py_DECREF(iter);
603 }
604
605 return min_idx;
606 }
607
608 static inline void set_phase_from_parents(char *phases, int parent_1,
583 static inline void set_phase_from_parents(char *phases, int parent_1,
609 int parent_2, Py_ssize_t i)
584 int parent_2, Py_ssize_t i)
610 {
585 {
611 if (parent_1 >= 0 && phases[parent_1] > phases[i])
586 if (parent_1 >= 0 && phases[parent_1] > phases[i])
612 phases[i] = phases[parent_1];
587 phases[i] = phases[parent_1];
613 if (parent_2 >= 0 && phases[parent_2] > phases[i])
588 if (parent_2 >= 0 && phases[parent_2] > phases[i])
614 phases[i] = phases[parent_2];
589 phases[i] = phases[parent_2];
615 }
590 }
616
591
617 static PyObject *reachableroots2(indexObject *self, PyObject *args)
592 static PyObject *reachableroots2(indexObject *self, PyObject *args)
618 {
593 {
619
594
620 /* Input */
595 /* Input */
621 long minroot;
596 long minroot;
622 PyObject *includepatharg = NULL;
597 PyObject *includepatharg = NULL;
623 int includepath = 0;
598 int includepath = 0;
624 /* heads and roots are lists */
599 /* heads and roots are lists */
625 PyObject *heads = NULL;
600 PyObject *heads = NULL;
626 PyObject *roots = NULL;
601 PyObject *roots = NULL;
627 PyObject *reachable = NULL;
602 PyObject *reachable = NULL;
628
603
629 PyObject *val;
604 PyObject *val;
630 Py_ssize_t len = index_length(self);
605 Py_ssize_t len = index_length(self);
631 long revnum;
606 long revnum;
632 Py_ssize_t k;
607 Py_ssize_t k;
633 Py_ssize_t i;
608 Py_ssize_t i;
634 Py_ssize_t l;
609 Py_ssize_t l;
635 int r;
610 int r;
636 int parents[2];
611 int parents[2];
637
612
638 /* Internal data structure:
613 /* Internal data structure:
639 * tovisit: array of length len+1 (all revs + nullrev), filled upto
614 * tovisit: array of length len+1 (all revs + nullrev), filled upto
640 * lentovisit
615 * lentovisit
641 *
616 *
642 * revstates: array of length len+1 (all revs + nullrev) */
617 * revstates: array of length len+1 (all revs + nullrev) */
643 int *tovisit = NULL;
618 int *tovisit = NULL;
644 long lentovisit = 0;
619 long lentovisit = 0;
645 enum { RS_SEEN = 1, RS_ROOT = 2, RS_REACHABLE = 4 };
620 enum { RS_SEEN = 1, RS_ROOT = 2, RS_REACHABLE = 4 };
646 char *revstates = NULL;
621 char *revstates = NULL;
647
622
648 /* Get arguments */
623 /* Get arguments */
649 if (!PyArg_ParseTuple(args, "lO!O!O!", &minroot, &PyList_Type, &heads,
624 if (!PyArg_ParseTuple(args, "lO!O!O!", &minroot, &PyList_Type, &heads,
650 &PyList_Type, &roots, &PyBool_Type,
625 &PyList_Type, &roots, &PyBool_Type,
651 &includepatharg))
626 &includepatharg))
652 goto bail;
627 goto bail;
653
628
654 if (includepatharg == Py_True)
629 if (includepatharg == Py_True)
655 includepath = 1;
630 includepath = 1;
656
631
657 /* Initialize return set */
632 /* Initialize return set */
658 reachable = PyList_New(0);
633 reachable = PyList_New(0);
659 if (reachable == NULL)
634 if (reachable == NULL)
660 goto bail;
635 goto bail;
661
636
662 /* Initialize internal datastructures */
637 /* Initialize internal datastructures */
663 tovisit = (int *)malloc((len + 1) * sizeof(int));
638 tovisit = (int *)malloc((len + 1) * sizeof(int));
664 if (tovisit == NULL) {
639 if (tovisit == NULL) {
665 PyErr_NoMemory();
640 PyErr_NoMemory();
666 goto bail;
641 goto bail;
667 }
642 }
668
643
669 revstates = (char *)calloc(len + 1, 1);
644 revstates = (char *)calloc(len + 1, 1);
670 if (revstates == NULL) {
645 if (revstates == NULL) {
671 PyErr_NoMemory();
646 PyErr_NoMemory();
672 goto bail;
647 goto bail;
673 }
648 }
674
649
675 l = PyList_GET_SIZE(roots);
650 l = PyList_GET_SIZE(roots);
676 for (i = 0; i < l; i++) {
651 for (i = 0; i < l; i++) {
677 revnum = PyInt_AsLong(PyList_GET_ITEM(roots, i));
652 revnum = PyInt_AsLong(PyList_GET_ITEM(roots, i));
678 if (revnum == -1 && PyErr_Occurred())
653 if (revnum == -1 && PyErr_Occurred())
679 goto bail;
654 goto bail;
680 /* If root is out of range, e.g. wdir(), it must be unreachable
655 /* If root is out of range, e.g. wdir(), it must be unreachable
681 * from heads. So we can just ignore it. */
656 * from heads. So we can just ignore it. */
682 if (revnum + 1 < 0 || revnum + 1 >= len + 1)
657 if (revnum + 1 < 0 || revnum + 1 >= len + 1)
683 continue;
658 continue;
684 revstates[revnum + 1] |= RS_ROOT;
659 revstates[revnum + 1] |= RS_ROOT;
685 }
660 }
686
661
687 /* Populate tovisit with all the heads */
662 /* Populate tovisit with all the heads */
688 l = PyList_GET_SIZE(heads);
663 l = PyList_GET_SIZE(heads);
689 for (i = 0; i < l; i++) {
664 for (i = 0; i < l; i++) {
690 revnum = PyInt_AsLong(PyList_GET_ITEM(heads, i));
665 revnum = PyInt_AsLong(PyList_GET_ITEM(heads, i));
691 if (revnum == -1 && PyErr_Occurred())
666 if (revnum == -1 && PyErr_Occurred())
692 goto bail;
667 goto bail;
693 if (revnum + 1 < 0 || revnum + 1 >= len + 1) {
668 if (revnum + 1 < 0 || revnum + 1 >= len + 1) {
694 PyErr_SetString(PyExc_IndexError, "head out of range");
669 PyErr_SetString(PyExc_IndexError, "head out of range");
695 goto bail;
670 goto bail;
696 }
671 }
697 if (!(revstates[revnum + 1] & RS_SEEN)) {
672 if (!(revstates[revnum + 1] & RS_SEEN)) {
698 tovisit[lentovisit++] = (int)revnum;
673 tovisit[lentovisit++] = (int)revnum;
699 revstates[revnum + 1] |= RS_SEEN;
674 revstates[revnum + 1] |= RS_SEEN;
700 }
675 }
701 }
676 }
702
677
703 /* Visit the tovisit list and find the reachable roots */
678 /* Visit the tovisit list and find the reachable roots */
704 k = 0;
679 k = 0;
705 while (k < lentovisit) {
680 while (k < lentovisit) {
706 /* Add the node to reachable if it is a root*/
681 /* Add the node to reachable if it is a root*/
707 revnum = tovisit[k++];
682 revnum = tovisit[k++];
708 if (revstates[revnum + 1] & RS_ROOT) {
683 if (revstates[revnum + 1] & RS_ROOT) {
709 revstates[revnum + 1] |= RS_REACHABLE;
684 revstates[revnum + 1] |= RS_REACHABLE;
710 val = PyInt_FromLong(revnum);
685 val = PyInt_FromLong(revnum);
711 if (val == NULL)
686 if (val == NULL)
712 goto bail;
687 goto bail;
713 r = PyList_Append(reachable, val);
688 r = PyList_Append(reachable, val);
714 Py_DECREF(val);
689 Py_DECREF(val);
715 if (r < 0)
690 if (r < 0)
716 goto bail;
691 goto bail;
717 if (includepath == 0)
692 if (includepath == 0)
718 continue;
693 continue;
719 }
694 }
720
695
721 /* Add its parents to the list of nodes to visit */
696 /* Add its parents to the list of nodes to visit */
722 if (revnum == nullrev)
697 if (revnum == nullrev)
723 continue;
698 continue;
724 r = index_get_parents(self, revnum, parents, (int)len - 1);
699 r = index_get_parents(self, revnum, parents, (int)len - 1);
725 if (r < 0)
700 if (r < 0)
726 goto bail;
701 goto bail;
727 for (i = 0; i < 2; i++) {
702 for (i = 0; i < 2; i++) {
728 if (!(revstates[parents[i] + 1] & RS_SEEN) &&
703 if (!(revstates[parents[i] + 1] & RS_SEEN) &&
729 parents[i] >= minroot) {
704 parents[i] >= minroot) {
730 tovisit[lentovisit++] = parents[i];
705 tovisit[lentovisit++] = parents[i];
731 revstates[parents[i] + 1] |= RS_SEEN;
706 revstates[parents[i] + 1] |= RS_SEEN;
732 }
707 }
733 }
708 }
734 }
709 }
735
710
736 /* Find all the nodes in between the roots we found and the heads
711 /* Find all the nodes in between the roots we found and the heads
737 * and add them to the reachable set */
712 * and add them to the reachable set */
738 if (includepath == 1) {
713 if (includepath == 1) {
739 long minidx = minroot;
714 long minidx = minroot;
740 if (minidx < 0)
715 if (minidx < 0)
741 minidx = 0;
716 minidx = 0;
742 for (i = minidx; i < len; i++) {
717 for (i = minidx; i < len; i++) {
743 if (!(revstates[i + 1] & RS_SEEN))
718 if (!(revstates[i + 1] & RS_SEEN))
744 continue;
719 continue;
745 r = index_get_parents(self, i, parents, (int)len - 1);
720 r = index_get_parents(self, i, parents, (int)len - 1);
746 /* Corrupted index file, error is set from
721 /* Corrupted index file, error is set from
747 * index_get_parents */
722 * index_get_parents */
748 if (r < 0)
723 if (r < 0)
749 goto bail;
724 goto bail;
750 if (((revstates[parents[0] + 1] |
725 if (((revstates[parents[0] + 1] |
751 revstates[parents[1] + 1]) &
726 revstates[parents[1] + 1]) &
752 RS_REACHABLE) &&
727 RS_REACHABLE) &&
753 !(revstates[i + 1] & RS_REACHABLE)) {
728 !(revstates[i + 1] & RS_REACHABLE)) {
754 revstates[i + 1] |= RS_REACHABLE;
729 revstates[i + 1] |= RS_REACHABLE;
755 val = PyInt_FromSsize_t(i);
730 val = PyInt_FromSsize_t(i);
756 if (val == NULL)
731 if (val == NULL)
757 goto bail;
732 goto bail;
758 r = PyList_Append(reachable, val);
733 r = PyList_Append(reachable, val);
759 Py_DECREF(val);
734 Py_DECREF(val);
760 if (r < 0)
735 if (r < 0)
761 goto bail;
736 goto bail;
762 }
737 }
763 }
738 }
764 }
739 }
765
740
766 free(revstates);
741 free(revstates);
767 free(tovisit);
742 free(tovisit);
768 return reachable;
743 return reachable;
769 bail:
744 bail:
770 Py_XDECREF(reachable);
745 Py_XDECREF(reachable);
771 free(revstates);
746 free(revstates);
772 free(tovisit);
747 free(tovisit);
773 return NULL;
748 return NULL;
774 }
749 }
775
750
751 static int add_roots_get_min(indexObject *self, PyObject *roots, char *phases,
752 char phase)
753 {
754 Py_ssize_t len = index_length(self);
755 PyObject *iter;
756 PyObject *item;
757 PyObject *iterator;
758 int rev, minrev = -1;
759 char *node;
760
761 if (!PySet_Check(roots))
762 return -2;
763 iterator = PyObject_GetIter(roots);
764 if (iterator == NULL)
765 return -2;
766 while ((item = PyIter_Next(iterator))) {
767 if (node_check(item, &node) == -1)
768 goto failed;
769 rev = index_find_node(self, node, 20);
770 /* null is implicitly public, so negative is invalid */
771 if (rev < 0 || rev >= len)
772 goto failed;
773 phases[rev] = phase;
774 if (minrev == -1 || minrev > rev)
775 minrev = rev;
776 Py_DECREF(item);
777 }
778 Py_DECREF(iterator);
779 return minrev;
780 failed:
781 Py_DECREF(iterator);
782 Py_DECREF(item);
783 return -2;
784 }
785
776 static PyObject *compute_phases_map_sets(indexObject *self, PyObject *args)
786 static PyObject *compute_phases_map_sets(indexObject *self, PyObject *args)
777 {
787 {
778 PyObject *roots = Py_None;
788 /* 0: public (untracked), 1: draft, 2: secret, 32: archive,
789 96: internal */
790 static const char trackedphases[] = {1, 2, 32, 96};
779 PyObject *ret = NULL;
791 PyObject *ret = NULL;
780 PyObject *phasessize = NULL;
792 PyObject *roots = Py_None;
793 PyObject *idx = NULL;
794 PyObject *pyphase = NULL;
795 PyObject *pyrev = NULL;
781 PyObject *phaseroots = NULL;
796 PyObject *phaseroots = NULL;
782 PyObject *phaseset = NULL;
797 PyObject *phasessize = NULL;
783 PyObject *phasessetlist = NULL;
798 PyObject *phasesets[4] = {NULL, NULL, NULL, NULL};
784 PyObject *rev = NULL;
785 Py_ssize_t len = index_length(self);
799 Py_ssize_t len = index_length(self);
786 Py_ssize_t numphase = 0;
800 const char *currentphase;
787 Py_ssize_t minrevallphases = 0;
788 Py_ssize_t minrevphase = 0;
789 Py_ssize_t i = 0;
790 char *phases = NULL;
801 char *phases = NULL;
791 long phase;
802 int minphaserev = -1, rev, i;
803 const int numphases = (int)(sizeof(phasesets) / sizeof(phasesets[0]));
792
804
793 if (!PyArg_ParseTuple(args, "O", &roots))
805 if (!PyArg_ParseTuple(args, "O", &roots))
794 goto done;
806 return NULL;
795 if (roots == NULL || !PyList_Check(roots)) {
807 if (roots == NULL || !PyDict_Check(roots)) {
796 PyErr_SetString(PyExc_TypeError, "roots must be a list");
808 PyErr_SetString(PyExc_TypeError, "roots must be a dictionary");
797 goto done;
809 return NULL;
798 }
810 }
799
811
800 phases = calloc(
812 phases = calloc(len, 1);
801 len, 1); /* phase per rev: {0: public, 1: draft, 2: secret} */
802 if (phases == NULL) {
813 if (phases == NULL) {
803 PyErr_NoMemory();
814 PyErr_NoMemory();
804 goto done;
815 return NULL;
805 }
816 }
806 /* Put the phase information of all the roots in phases */
817
807 numphase = PyList_GET_SIZE(roots) + 1;
818 for (i = 0; i < numphases; ++i) {
808 minrevallphases = len + 1;
819 pyphase = PyInt_FromLong(trackedphases[i]);
809 phasessetlist = PyList_New(numphase);
820 if (pyphase == NULL)
810 if (phasessetlist == NULL)
821 goto release;
811 goto done;
822 phaseroots = PyDict_GetItem(roots, pyphase);
823 Py_DECREF(pyphase);
824 if (phaseroots == NULL)
825 continue;
826 rev = add_roots_get_min(self, phaseroots, phases, trackedphases[i]);
827 phaseroots = NULL;
828 if (rev == -2)
829 goto release;
830 if (rev != -1 && (minphaserev == -1 || rev < minphaserev))
831 minphaserev = rev;
832 }
833
834 for (i = 0; i < numphases; ++i) {
835 phasesets[i] = PySet_New(NULL);
836 if (phasesets[i] == NULL)
837 goto release;
838 }
812
839
813 PyList_SET_ITEM(phasessetlist, 0, Py_None);
840 if (minphaserev == -1)
814 Py_INCREF(Py_None);
841 minphaserev = len;
815
842 for (rev = minphaserev; rev < len; ++rev) {
816 for (i = 0; i < numphase - 1; i++) {
843 int parents[2];
817 phaseroots = PyList_GET_ITEM(roots, i);
844 /*
818 phaseset = PySet_New(NULL);
845 * The parent lookup could be skipped for phaseroots, but
819 if (phaseset == NULL)
846 * phase --force would historically not recompute them
847 * correctly, leaving descendents with a lower phase around.
848 * As such, unconditionally recompute the phase.
849 */
850 if (index_get_parents(self, rev, parents, (int)len - 1) < 0)
820 goto release;
851 goto release;
821 PyList_SET_ITEM(phasessetlist, i + 1, phaseset);
852 set_phase_from_parents(phases, parents[0], parents[1], rev);
822 if (!PyList_Check(phaseroots)) {
853 switch (phases[rev]) {
823 PyErr_SetString(PyExc_TypeError,
854 case 0:
824 "roots item must be a list");
855 continue;
856 case 1:
857 pyphase = phasesets[0];
858 break;
859 case 2:
860 pyphase = phasesets[1];
861 break;
862 case 32:
863 pyphase = phasesets[2];
864 break;
865 case 96:
866 pyphase = phasesets[3];
867 break;
868 default:
825 goto release;
869 goto release;
826 }
870 }
827 minrevphase =
871 pyrev = PyInt_FromLong(rev);
828 add_roots_get_min(self, phaseroots, i + 1, phases);
872 if (pyrev == NULL)
829 if (minrevphase == -2) /* Error from add_roots_get_min */
830 goto release;
873 goto release;
831 minrevallphases = MIN(minrevallphases, minrevphase);
874 if (PySet_Add(pyphase, pyrev) == -1) {
875 Py_DECREF(pyrev);
876 goto release;
877 }
878 Py_DECREF(pyrev);
832 }
879 }
833 /* Propagate the phase information from the roots to the revs */
880 phaseroots = _dict_new_presized(numphases);
834 if (minrevallphases != -1) {
881 if (phaseroots == NULL)
835 int parents[2];
882 goto release;
836 for (i = minrevallphases; i < len; i++) {
883 for (int i = 0; i < numphases; ++i) {
837 if (index_get_parents(self, i, parents, (int)len - 1) <
884 pyphase = PyInt_FromLong(trackedphases[i]);
838 0)
885 if (pyphase == NULL)
839 goto release;
886 goto release;
840 set_phase_from_parents(phases, parents[0], parents[1],
887 if (PyDict_SetItem(phaseroots, pyphase, phasesets[i]) == -1) {
841 i);
888 Py_DECREF(pyphase);
889 goto release;
842 }
890 }
891 Py_DECREF(phasesets[i]);
892 phasesets[i] = NULL;
843 }
893 }
844 /* Transform phase list to a python list */
845 phasessize = PyInt_FromSsize_t(len);
894 phasessize = PyInt_FromSsize_t(len);
846 if (phasessize == NULL)
895 if (phasessize == NULL)
847 goto release;
896 goto release;
848 for (i = 0; i < len; i++) {
897
849 phase = phases[i];
898 ret = PyTuple_Pack(2, phasessize, phaseroots);
850 /* We only store the sets of phase for non public phase, the
899 Py_DECREF(phasessize);
851 * public phase is computed as a difference */
900 Py_DECREF(phaseroots);
852 if (phase != 0) {
901 return ret;
853 phaseset = PyList_GET_ITEM(phasessetlist, phase);
854 rev = PyInt_FromSsize_t(i);
855 if (rev == NULL)
856 goto release;
857 PySet_Add(phaseset, rev);
858 Py_XDECREF(rev);
859 }
860 }
861 ret = PyTuple_Pack(2, phasessize, phasessetlist);
862
902
863 release:
903 release:
864 Py_XDECREF(phasessize);
904 for (i = 0; i < numphases; ++i)
865 Py_XDECREF(phasessetlist);
905 Py_XDECREF(phasesets[i]);
866 done:
906 Py_XDECREF(phaseroots);
907
867 free(phases);
908 free(phases);
868 return ret;
909 return NULL;
869 }
910 }
870
911
871 static PyObject *index_headrevs(indexObject *self, PyObject *args)
912 static PyObject *index_headrevs(indexObject *self, PyObject *args)
872 {
913 {
873 Py_ssize_t i, j, len;
914 Py_ssize_t i, j, len;
874 char *nothead = NULL;
915 char *nothead = NULL;
875 PyObject *heads = NULL;
916 PyObject *heads = NULL;
876 PyObject *filter = NULL;
917 PyObject *filter = NULL;
877 PyObject *filteredrevs = Py_None;
918 PyObject *filteredrevs = Py_None;
878
919
879 if (!PyArg_ParseTuple(args, "|O", &filteredrevs)) {
920 if (!PyArg_ParseTuple(args, "|O", &filteredrevs)) {
880 return NULL;
921 return NULL;
881 }
922 }
882
923
883 if (self->headrevs && filteredrevs == self->filteredrevs)
924 if (self->headrevs && filteredrevs == self->filteredrevs)
884 return list_copy(self->headrevs);
925 return list_copy(self->headrevs);
885
926
886 Py_DECREF(self->filteredrevs);
927 Py_DECREF(self->filteredrevs);
887 self->filteredrevs = filteredrevs;
928 self->filteredrevs = filteredrevs;
888 Py_INCREF(filteredrevs);
929 Py_INCREF(filteredrevs);
889
930
890 if (filteredrevs != Py_None) {
931 if (filteredrevs != Py_None) {
891 filter = PyObject_GetAttrString(filteredrevs, "__contains__");
932 filter = PyObject_GetAttrString(filteredrevs, "__contains__");
892 if (!filter) {
933 if (!filter) {
893 PyErr_SetString(
934 PyErr_SetString(
894 PyExc_TypeError,
935 PyExc_TypeError,
895 "filteredrevs has no attribute __contains__");
936 "filteredrevs has no attribute __contains__");
896 goto bail;
937 goto bail;
897 }
938 }
898 }
939 }
899
940
900 len = index_length(self);
941 len = index_length(self);
901 heads = PyList_New(0);
942 heads = PyList_New(0);
902 if (heads == NULL)
943 if (heads == NULL)
903 goto bail;
944 goto bail;
904 if (len == 0) {
945 if (len == 0) {
905 PyObject *nullid = PyInt_FromLong(-1);
946 PyObject *nullid = PyInt_FromLong(-1);
906 if (nullid == NULL || PyList_Append(heads, nullid) == -1) {
947 if (nullid == NULL || PyList_Append(heads, nullid) == -1) {
907 Py_XDECREF(nullid);
948 Py_XDECREF(nullid);
908 goto bail;
949 goto bail;
909 }
950 }
910 goto done;
951 goto done;
911 }
952 }
912
953
913 nothead = calloc(len, 1);
954 nothead = calloc(len, 1);
914 if (nothead == NULL) {
955 if (nothead == NULL) {
915 PyErr_NoMemory();
956 PyErr_NoMemory();
916 goto bail;
957 goto bail;
917 }
958 }
918
959
919 for (i = len - 1; i >= 0; i--) {
960 for (i = len - 1; i >= 0; i--) {
920 int isfiltered;
961 int isfiltered;
921 int parents[2];
962 int parents[2];
922
963
923 /* If nothead[i] == 1, it means we've seen an unfiltered child
964 /* If nothead[i] == 1, it means we've seen an unfiltered child
924 * of this node already, and therefore this node is not
965 * of this node already, and therefore this node is not
925 * filtered. So we can skip the expensive check_filter step.
966 * filtered. So we can skip the expensive check_filter step.
926 */
967 */
927 if (nothead[i] != 1) {
968 if (nothead[i] != 1) {
928 isfiltered = check_filter(filter, i);
969 isfiltered = check_filter(filter, i);
929 if (isfiltered == -1) {
970 if (isfiltered == -1) {
930 PyErr_SetString(PyExc_TypeError,
971 PyErr_SetString(PyExc_TypeError,
931 "unable to check filter");
972 "unable to check filter");
932 goto bail;
973 goto bail;
933 }
974 }
934
975
935 if (isfiltered) {
976 if (isfiltered) {
936 nothead[i] = 1;
977 nothead[i] = 1;
937 continue;
978 continue;
938 }
979 }
939 }
980 }
940
981
941 if (index_get_parents(self, i, parents, (int)len - 1) < 0)
982 if (index_get_parents(self, i, parents, (int)len - 1) < 0)
942 goto bail;
983 goto bail;
943 for (j = 0; j < 2; j++) {
984 for (j = 0; j < 2; j++) {
944 if (parents[j] >= 0)
985 if (parents[j] >= 0)
945 nothead[parents[j]] = 1;
986 nothead[parents[j]] = 1;
946 }
987 }
947 }
988 }
948
989
949 for (i = 0; i < len; i++) {
990 for (i = 0; i < len; i++) {
950 PyObject *head;
991 PyObject *head;
951
992
952 if (nothead[i])
993 if (nothead[i])
953 continue;
994 continue;
954 head = PyInt_FromSsize_t(i);
995 head = PyInt_FromSsize_t(i);
955 if (head == NULL || PyList_Append(heads, head) == -1) {
996 if (head == NULL || PyList_Append(heads, head) == -1) {
956 Py_XDECREF(head);
997 Py_XDECREF(head);
957 goto bail;
998 goto bail;
958 }
999 }
959 }
1000 }
960
1001
961 done:
1002 done:
962 self->headrevs = heads;
1003 self->headrevs = heads;
963 Py_XDECREF(filter);
1004 Py_XDECREF(filter);
964 free(nothead);
1005 free(nothead);
965 return list_copy(self->headrevs);
1006 return list_copy(self->headrevs);
966 bail:
1007 bail:
967 Py_XDECREF(filter);
1008 Py_XDECREF(filter);
968 Py_XDECREF(heads);
1009 Py_XDECREF(heads);
969 free(nothead);
1010 free(nothead);
970 return NULL;
1011 return NULL;
971 }
1012 }
972
1013
973 /**
1014 /**
974 * Obtain the base revision index entry.
1015 * Obtain the base revision index entry.
975 *
1016 *
976 * Callers must ensure that rev >= 0 or illegal memory access may occur.
1017 * Callers must ensure that rev >= 0 or illegal memory access may occur.
977 */
1018 */
978 static inline int index_baserev(indexObject *self, int rev)
1019 static inline int index_baserev(indexObject *self, int rev)
979 {
1020 {
980 const char *data;
1021 const char *data;
981 int result;
1022 int result;
982
1023
983 if (rev >= self->length) {
1024 if (rev >= self->length) {
984 PyObject *tuple =
1025 PyObject *tuple =
985 PyList_GET_ITEM(self->added, rev - self->length);
1026 PyList_GET_ITEM(self->added, rev - self->length);
986 long ret;
1027 long ret;
987 if (!pylong_to_long(PyTuple_GET_ITEM(tuple, 3), &ret)) {
1028 if (!pylong_to_long(PyTuple_GET_ITEM(tuple, 3), &ret)) {
988 return -2;
1029 return -2;
989 }
1030 }
990 result = (int)ret;
1031 result = (int)ret;
991 } else {
1032 } else {
992 data = index_deref(self, rev);
1033 data = index_deref(self, rev);
993 if (data == NULL) {
1034 if (data == NULL) {
994 return -2;
1035 return -2;
995 }
1036 }
996
1037
997 result = getbe32(data + 16);
1038 result = getbe32(data + 16);
998 }
1039 }
999 if (result > rev) {
1040 if (result > rev) {
1000 PyErr_Format(
1041 PyErr_Format(
1001 PyExc_ValueError,
1042 PyExc_ValueError,
1002 "corrupted revlog, revision base above revision: %d, %d",
1043 "corrupted revlog, revision base above revision: %d, %d",
1003 rev, result);
1044 rev, result);
1004 return -2;
1045 return -2;
1005 }
1046 }
1006 if (result < -1) {
1047 if (result < -1) {
1007 PyErr_Format(
1048 PyErr_Format(
1008 PyExc_ValueError,
1049 PyExc_ValueError,
1009 "corrupted revlog, revision base out of range: %d, %d", rev,
1050 "corrupted revlog, revision base out of range: %d, %d", rev,
1010 result);
1051 result);
1011 return -2;
1052 return -2;
1012 }
1053 }
1013 return result;
1054 return result;
1014 }
1055 }
1015
1056
1016 /**
1057 /**
1017 * Find if a revision is a snapshot or not
1058 * Find if a revision is a snapshot or not
1018 *
1059 *
1019 * Only relevant for sparse-revlog case.
1060 * Only relevant for sparse-revlog case.
1020 * Callers must ensure that rev is in a valid range.
1061 * Callers must ensure that rev is in a valid range.
1021 */
1062 */
1022 static int index_issnapshotrev(indexObject *self, Py_ssize_t rev)
1063 static int index_issnapshotrev(indexObject *self, Py_ssize_t rev)
1023 {
1064 {
1024 int ps[2];
1065 int ps[2];
1025 Py_ssize_t base;
1066 Py_ssize_t base;
1026 while (rev >= 0) {
1067 while (rev >= 0) {
1027 base = (Py_ssize_t)index_baserev(self, rev);
1068 base = (Py_ssize_t)index_baserev(self, rev);
1028 if (base == rev) {
1069 if (base == rev) {
1029 base = -1;
1070 base = -1;
1030 }
1071 }
1031 if (base == -2) {
1072 if (base == -2) {
1032 assert(PyErr_Occurred());
1073 assert(PyErr_Occurred());
1033 return -1;
1074 return -1;
1034 }
1075 }
1035 if (base == -1) {
1076 if (base == -1) {
1036 return 1;
1077 return 1;
1037 }
1078 }
1038 if (index_get_parents(self, rev, ps, (int)rev) < 0) {
1079 if (index_get_parents(self, rev, ps, (int)rev) < 0) {
1039 assert(PyErr_Occurred());
1080 assert(PyErr_Occurred());
1040 return -1;
1081 return -1;
1041 };
1082 };
1042 if (base == ps[0] || base == ps[1]) {
1083 if (base == ps[0] || base == ps[1]) {
1043 return 0;
1084 return 0;
1044 }
1085 }
1045 rev = base;
1086 rev = base;
1046 }
1087 }
1047 return rev == -1;
1088 return rev == -1;
1048 }
1089 }
1049
1090
1050 static PyObject *index_issnapshot(indexObject *self, PyObject *value)
1091 static PyObject *index_issnapshot(indexObject *self, PyObject *value)
1051 {
1092 {
1052 long rev;
1093 long rev;
1053 int issnap;
1094 int issnap;
1054 Py_ssize_t length = index_length(self);
1095 Py_ssize_t length = index_length(self);
1055
1096
1056 if (!pylong_to_long(value, &rev)) {
1097 if (!pylong_to_long(value, &rev)) {
1057 return NULL;
1098 return NULL;
1058 }
1099 }
1059 if (rev < -1 || rev >= length) {
1100 if (rev < -1 || rev >= length) {
1060 PyErr_Format(PyExc_ValueError, "revlog index out of range: %ld",
1101 PyErr_Format(PyExc_ValueError, "revlog index out of range: %ld",
1061 rev);
1102 rev);
1062 return NULL;
1103 return NULL;
1063 };
1104 };
1064 issnap = index_issnapshotrev(self, (Py_ssize_t)rev);
1105 issnap = index_issnapshotrev(self, (Py_ssize_t)rev);
1065 if (issnap < 0) {
1106 if (issnap < 0) {
1066 return NULL;
1107 return NULL;
1067 };
1108 };
1068 return PyBool_FromLong((long)issnap);
1109 return PyBool_FromLong((long)issnap);
1069 }
1110 }
1070
1111
1071 static PyObject *index_findsnapshots(indexObject *self, PyObject *args)
1112 static PyObject *index_findsnapshots(indexObject *self, PyObject *args)
1072 {
1113 {
1073 Py_ssize_t start_rev;
1114 Py_ssize_t start_rev;
1074 PyObject *cache;
1115 PyObject *cache;
1075 Py_ssize_t base;
1116 Py_ssize_t base;
1076 Py_ssize_t rev;
1117 Py_ssize_t rev;
1077 PyObject *key = NULL;
1118 PyObject *key = NULL;
1078 PyObject *value = NULL;
1119 PyObject *value = NULL;
1079 const Py_ssize_t length = index_length(self);
1120 const Py_ssize_t length = index_length(self);
1080 if (!PyArg_ParseTuple(args, "O!n", &PyDict_Type, &cache, &start_rev)) {
1121 if (!PyArg_ParseTuple(args, "O!n", &PyDict_Type, &cache, &start_rev)) {
1081 return NULL;
1122 return NULL;
1082 }
1123 }
1083 for (rev = start_rev; rev < length; rev++) {
1124 for (rev = start_rev; rev < length; rev++) {
1084 int issnap;
1125 int issnap;
1085 PyObject *allvalues = NULL;
1126 PyObject *allvalues = NULL;
1086 issnap = index_issnapshotrev(self, rev);
1127 issnap = index_issnapshotrev(self, rev);
1087 if (issnap < 0) {
1128 if (issnap < 0) {
1088 goto bail;
1129 goto bail;
1089 }
1130 }
1090 if (issnap == 0) {
1131 if (issnap == 0) {
1091 continue;
1132 continue;
1092 }
1133 }
1093 base = (Py_ssize_t)index_baserev(self, rev);
1134 base = (Py_ssize_t)index_baserev(self, rev);
1094 if (base == rev) {
1135 if (base == rev) {
1095 base = -1;
1136 base = -1;
1096 }
1137 }
1097 if (base == -2) {
1138 if (base == -2) {
1098 assert(PyErr_Occurred());
1139 assert(PyErr_Occurred());
1099 goto bail;
1140 goto bail;
1100 }
1141 }
1101 key = PyInt_FromSsize_t(base);
1142 key = PyInt_FromSsize_t(base);
1102 allvalues = PyDict_GetItem(cache, key);
1143 allvalues = PyDict_GetItem(cache, key);
1103 if (allvalues == NULL && PyErr_Occurred()) {
1144 if (allvalues == NULL && PyErr_Occurred()) {
1104 goto bail;
1145 goto bail;
1105 }
1146 }
1106 if (allvalues == NULL) {
1147 if (allvalues == NULL) {
1107 int r;
1148 int r;
1108 allvalues = PyList_New(0);
1149 allvalues = PyList_New(0);
1109 if (!allvalues) {
1150 if (!allvalues) {
1110 goto bail;
1151 goto bail;
1111 }
1152 }
1112 r = PyDict_SetItem(cache, key, allvalues);
1153 r = PyDict_SetItem(cache, key, allvalues);
1113 Py_DECREF(allvalues);
1154 Py_DECREF(allvalues);
1114 if (r < 0) {
1155 if (r < 0) {
1115 goto bail;
1156 goto bail;
1116 }
1157 }
1117 }
1158 }
1118 value = PyInt_FromSsize_t(rev);
1159 value = PyInt_FromSsize_t(rev);
1119 if (PyList_Append(allvalues, value)) {
1160 if (PyList_Append(allvalues, value)) {
1120 goto bail;
1161 goto bail;
1121 }
1162 }
1122 Py_CLEAR(key);
1163 Py_CLEAR(key);
1123 Py_CLEAR(value);
1164 Py_CLEAR(value);
1124 }
1165 }
1125 Py_RETURN_NONE;
1166 Py_RETURN_NONE;
1126 bail:
1167 bail:
1127 Py_XDECREF(key);
1168 Py_XDECREF(key);
1128 Py_XDECREF(value);
1169 Py_XDECREF(value);
1129 return NULL;
1170 return NULL;
1130 }
1171 }
1131
1172
1132 static PyObject *index_deltachain(indexObject *self, PyObject *args)
1173 static PyObject *index_deltachain(indexObject *self, PyObject *args)
1133 {
1174 {
1134 int rev, generaldelta;
1175 int rev, generaldelta;
1135 PyObject *stoparg;
1176 PyObject *stoparg;
1136 int stoprev, iterrev, baserev = -1;
1177 int stoprev, iterrev, baserev = -1;
1137 int stopped;
1178 int stopped;
1138 PyObject *chain = NULL, *result = NULL;
1179 PyObject *chain = NULL, *result = NULL;
1139 const Py_ssize_t length = index_length(self);
1180 const Py_ssize_t length = index_length(self);
1140
1181
1141 if (!PyArg_ParseTuple(args, "iOi", &rev, &stoparg, &generaldelta)) {
1182 if (!PyArg_ParseTuple(args, "iOi", &rev, &stoparg, &generaldelta)) {
1142 return NULL;
1183 return NULL;
1143 }
1184 }
1144
1185
1145 if (PyInt_Check(stoparg)) {
1186 if (PyInt_Check(stoparg)) {
1146 stoprev = (int)PyInt_AsLong(stoparg);
1187 stoprev = (int)PyInt_AsLong(stoparg);
1147 if (stoprev == -1 && PyErr_Occurred()) {
1188 if (stoprev == -1 && PyErr_Occurred()) {
1148 return NULL;
1189 return NULL;
1149 }
1190 }
1150 } else if (stoparg == Py_None) {
1191 } else if (stoparg == Py_None) {
1151 stoprev = -2;
1192 stoprev = -2;
1152 } else {
1193 } else {
1153 PyErr_SetString(PyExc_ValueError,
1194 PyErr_SetString(PyExc_ValueError,
1154 "stoprev must be integer or None");
1195 "stoprev must be integer or None");
1155 return NULL;
1196 return NULL;
1156 }
1197 }
1157
1198
1158 if (rev < 0 || rev >= length) {
1199 if (rev < 0 || rev >= length) {
1159 PyErr_SetString(PyExc_ValueError, "revlog index out of range");
1200 PyErr_SetString(PyExc_ValueError, "revlog index out of range");
1160 return NULL;
1201 return NULL;
1161 }
1202 }
1162
1203
1163 chain = PyList_New(0);
1204 chain = PyList_New(0);
1164 if (chain == NULL) {
1205 if (chain == NULL) {
1165 return NULL;
1206 return NULL;
1166 }
1207 }
1167
1208
1168 baserev = index_baserev(self, rev);
1209 baserev = index_baserev(self, rev);
1169
1210
1170 /* This should never happen. */
1211 /* This should never happen. */
1171 if (baserev <= -2) {
1212 if (baserev <= -2) {
1172 /* Error should be set by index_deref() */
1213 /* Error should be set by index_deref() */
1173 assert(PyErr_Occurred());
1214 assert(PyErr_Occurred());
1174 goto bail;
1215 goto bail;
1175 }
1216 }
1176
1217
1177 iterrev = rev;
1218 iterrev = rev;
1178
1219
1179 while (iterrev != baserev && iterrev != stoprev) {
1220 while (iterrev != baserev && iterrev != stoprev) {
1180 PyObject *value = PyInt_FromLong(iterrev);
1221 PyObject *value = PyInt_FromLong(iterrev);
1181 if (value == NULL) {
1222 if (value == NULL) {
1182 goto bail;
1223 goto bail;
1183 }
1224 }
1184 if (PyList_Append(chain, value)) {
1225 if (PyList_Append(chain, value)) {
1185 Py_DECREF(value);
1226 Py_DECREF(value);
1186 goto bail;
1227 goto bail;
1187 }
1228 }
1188 Py_DECREF(value);
1229 Py_DECREF(value);
1189
1230
1190 if (generaldelta) {
1231 if (generaldelta) {
1191 iterrev = baserev;
1232 iterrev = baserev;
1192 } else {
1233 } else {
1193 iterrev--;
1234 iterrev--;
1194 }
1235 }
1195
1236
1196 if (iterrev < 0) {
1237 if (iterrev < 0) {
1197 break;
1238 break;
1198 }
1239 }
1199
1240
1200 if (iterrev >= length) {
1241 if (iterrev >= length) {
1201 PyErr_SetString(PyExc_IndexError,
1242 PyErr_SetString(PyExc_IndexError,
1202 "revision outside index");
1243 "revision outside index");
1203 return NULL;
1244 return NULL;
1204 }
1245 }
1205
1246
1206 baserev = index_baserev(self, iterrev);
1247 baserev = index_baserev(self, iterrev);
1207
1248
1208 /* This should never happen. */
1249 /* This should never happen. */
1209 if (baserev <= -2) {
1250 if (baserev <= -2) {
1210 /* Error should be set by index_deref() */
1251 /* Error should be set by index_deref() */
1211 assert(PyErr_Occurred());
1252 assert(PyErr_Occurred());
1212 goto bail;
1253 goto bail;
1213 }
1254 }
1214 }
1255 }
1215
1256
1216 if (iterrev == stoprev) {
1257 if (iterrev == stoprev) {
1217 stopped = 1;
1258 stopped = 1;
1218 } else {
1259 } else {
1219 PyObject *value = PyInt_FromLong(iterrev);
1260 PyObject *value = PyInt_FromLong(iterrev);
1220 if (value == NULL) {
1261 if (value == NULL) {
1221 goto bail;
1262 goto bail;
1222 }
1263 }
1223 if (PyList_Append(chain, value)) {
1264 if (PyList_Append(chain, value)) {
1224 Py_DECREF(value);
1265 Py_DECREF(value);
1225 goto bail;
1266 goto bail;
1226 }
1267 }
1227 Py_DECREF(value);
1268 Py_DECREF(value);
1228
1269
1229 stopped = 0;
1270 stopped = 0;
1230 }
1271 }
1231
1272
1232 if (PyList_Reverse(chain)) {
1273 if (PyList_Reverse(chain)) {
1233 goto bail;
1274 goto bail;
1234 }
1275 }
1235
1276
1236 result = Py_BuildValue("OO", chain, stopped ? Py_True : Py_False);
1277 result = Py_BuildValue("OO", chain, stopped ? Py_True : Py_False);
1237 Py_DECREF(chain);
1278 Py_DECREF(chain);
1238 return result;
1279 return result;
1239
1280
1240 bail:
1281 bail:
1241 Py_DECREF(chain);
1282 Py_DECREF(chain);
1242 return NULL;
1283 return NULL;
1243 }
1284 }
1244
1285
1245 static inline int64_t
1286 static inline int64_t
1246 index_segment_span(indexObject *self, Py_ssize_t start_rev, Py_ssize_t end_rev)
1287 index_segment_span(indexObject *self, Py_ssize_t start_rev, Py_ssize_t end_rev)
1247 {
1288 {
1248 int64_t start_offset;
1289 int64_t start_offset;
1249 int64_t end_offset;
1290 int64_t end_offset;
1250 int end_size;
1291 int end_size;
1251 start_offset = index_get_start(self, start_rev);
1292 start_offset = index_get_start(self, start_rev);
1252 if (start_offset < 0) {
1293 if (start_offset < 0) {
1253 return -1;
1294 return -1;
1254 }
1295 }
1255 end_offset = index_get_start(self, end_rev);
1296 end_offset = index_get_start(self, end_rev);
1256 if (end_offset < 0) {
1297 if (end_offset < 0) {
1257 return -1;
1298 return -1;
1258 }
1299 }
1259 end_size = index_get_length(self, end_rev);
1300 end_size = index_get_length(self, end_rev);
1260 if (end_size < 0) {
1301 if (end_size < 0) {
1261 return -1;
1302 return -1;
1262 }
1303 }
1263 if (end_offset < start_offset) {
1304 if (end_offset < start_offset) {
1264 PyErr_Format(PyExc_ValueError,
1305 PyErr_Format(PyExc_ValueError,
1265 "corrupted revlog index: inconsistent offset "
1306 "corrupted revlog index: inconsistent offset "
1266 "between revisions (%zd) and (%zd)",
1307 "between revisions (%zd) and (%zd)",
1267 start_rev, end_rev);
1308 start_rev, end_rev);
1268 return -1;
1309 return -1;
1269 }
1310 }
1270 return (end_offset - start_offset) + (int64_t)end_size;
1311 return (end_offset - start_offset) + (int64_t)end_size;
1271 }
1312 }
1272
1313
1273 /* returns endidx so that revs[startidx:endidx] has no empty trailing revs */
1314 /* returns endidx so that revs[startidx:endidx] has no empty trailing revs */
1274 static Py_ssize_t trim_endidx(indexObject *self, const Py_ssize_t *revs,
1315 static Py_ssize_t trim_endidx(indexObject *self, const Py_ssize_t *revs,
1275 Py_ssize_t startidx, Py_ssize_t endidx)
1316 Py_ssize_t startidx, Py_ssize_t endidx)
1276 {
1317 {
1277 int length;
1318 int length;
1278 while (endidx > 1 && endidx > startidx) {
1319 while (endidx > 1 && endidx > startidx) {
1279 length = index_get_length(self, revs[endidx - 1]);
1320 length = index_get_length(self, revs[endidx - 1]);
1280 if (length < 0) {
1321 if (length < 0) {
1281 return -1;
1322 return -1;
1282 }
1323 }
1283 if (length != 0) {
1324 if (length != 0) {
1284 break;
1325 break;
1285 }
1326 }
1286 endidx -= 1;
1327 endidx -= 1;
1287 }
1328 }
1288 return endidx;
1329 return endidx;
1289 }
1330 }
1290
1331
1291 struct Gap {
1332 struct Gap {
1292 int64_t size;
1333 int64_t size;
1293 Py_ssize_t idx;
1334 Py_ssize_t idx;
1294 };
1335 };
1295
1336
1296 static int gap_compare(const void *left, const void *right)
1337 static int gap_compare(const void *left, const void *right)
1297 {
1338 {
1298 const struct Gap *l_left = ((const struct Gap *)left);
1339 const struct Gap *l_left = ((const struct Gap *)left);
1299 const struct Gap *l_right = ((const struct Gap *)right);
1340 const struct Gap *l_right = ((const struct Gap *)right);
1300 if (l_left->size < l_right->size) {
1341 if (l_left->size < l_right->size) {
1301 return -1;
1342 return -1;
1302 } else if (l_left->size > l_right->size) {
1343 } else if (l_left->size > l_right->size) {
1303 return 1;
1344 return 1;
1304 }
1345 }
1305 return 0;
1346 return 0;
1306 }
1347 }
1307 static int Py_ssize_t_compare(const void *left, const void *right)
1348 static int Py_ssize_t_compare(const void *left, const void *right)
1308 {
1349 {
1309 const Py_ssize_t l_left = *(const Py_ssize_t *)left;
1350 const Py_ssize_t l_left = *(const Py_ssize_t *)left;
1310 const Py_ssize_t l_right = *(const Py_ssize_t *)right;
1351 const Py_ssize_t l_right = *(const Py_ssize_t *)right;
1311 if (l_left < l_right) {
1352 if (l_left < l_right) {
1312 return -1;
1353 return -1;
1313 } else if (l_left > l_right) {
1354 } else if (l_left > l_right) {
1314 return 1;
1355 return 1;
1315 }
1356 }
1316 return 0;
1357 return 0;
1317 }
1358 }
1318
1359
1319 static PyObject *index_slicechunktodensity(indexObject *self, PyObject *args)
1360 static PyObject *index_slicechunktodensity(indexObject *self, PyObject *args)
1320 {
1361 {
1321 /* method arguments */
1362 /* method arguments */
1322 PyObject *list_revs = NULL; /* revisions in the chain */
1363 PyObject *list_revs = NULL; /* revisions in the chain */
1323 double targetdensity = 0; /* min density to achieve */
1364 double targetdensity = 0; /* min density to achieve */
1324 Py_ssize_t mingapsize = 0; /* threshold to ignore gaps */
1365 Py_ssize_t mingapsize = 0; /* threshold to ignore gaps */
1325
1366
1326 /* other core variables */
1367 /* other core variables */
1327 Py_ssize_t idxlen = index_length(self);
1368 Py_ssize_t idxlen = index_length(self);
1328 Py_ssize_t i; /* used for various iteration */
1369 Py_ssize_t i; /* used for various iteration */
1329 PyObject *result = NULL; /* the final return of the function */
1370 PyObject *result = NULL; /* the final return of the function */
1330
1371
1331 /* generic information about the delta chain being slice */
1372 /* generic information about the delta chain being slice */
1332 Py_ssize_t num_revs = 0; /* size of the full delta chain */
1373 Py_ssize_t num_revs = 0; /* size of the full delta chain */
1333 Py_ssize_t *revs = NULL; /* native array of revision in the chain */
1374 Py_ssize_t *revs = NULL; /* native array of revision in the chain */
1334 int64_t chainpayload = 0; /* sum of all delta in the chain */
1375 int64_t chainpayload = 0; /* sum of all delta in the chain */
1335 int64_t deltachainspan = 0; /* distance from first byte to last byte */
1376 int64_t deltachainspan = 0; /* distance from first byte to last byte */
1336
1377
1337 /* variable used for slicing the delta chain */
1378 /* variable used for slicing the delta chain */
1338 int64_t readdata = 0; /* amount of data currently planned to be read */
1379 int64_t readdata = 0; /* amount of data currently planned to be read */
1339 double density = 0; /* ration of payload data compared to read ones */
1380 double density = 0; /* ration of payload data compared to read ones */
1340 int64_t previous_end;
1381 int64_t previous_end;
1341 struct Gap *gaps = NULL; /* array of notable gap in the chain */
1382 struct Gap *gaps = NULL; /* array of notable gap in the chain */
1342 Py_ssize_t num_gaps =
1383 Py_ssize_t num_gaps =
1343 0; /* total number of notable gap recorded so far */
1384 0; /* total number of notable gap recorded so far */
1344 Py_ssize_t *selected_indices = NULL; /* indices of gap skipped over */
1385 Py_ssize_t *selected_indices = NULL; /* indices of gap skipped over */
1345 Py_ssize_t num_selected = 0; /* number of gaps skipped */
1386 Py_ssize_t num_selected = 0; /* number of gaps skipped */
1346 PyObject *chunk = NULL; /* individual slice */
1387 PyObject *chunk = NULL; /* individual slice */
1347 PyObject *allchunks = NULL; /* all slices */
1388 PyObject *allchunks = NULL; /* all slices */
1348 Py_ssize_t previdx;
1389 Py_ssize_t previdx;
1349
1390
1350 /* parsing argument */
1391 /* parsing argument */
1351 if (!PyArg_ParseTuple(args, "O!dn", &PyList_Type, &list_revs,
1392 if (!PyArg_ParseTuple(args, "O!dn", &PyList_Type, &list_revs,
1352 &targetdensity, &mingapsize)) {
1393 &targetdensity, &mingapsize)) {
1353 goto bail;
1394 goto bail;
1354 }
1395 }
1355
1396
1356 /* If the delta chain contains a single element, we do not need slicing
1397 /* If the delta chain contains a single element, we do not need slicing
1357 */
1398 */
1358 num_revs = PyList_GET_SIZE(list_revs);
1399 num_revs = PyList_GET_SIZE(list_revs);
1359 if (num_revs <= 1) {
1400 if (num_revs <= 1) {
1360 result = PyTuple_Pack(1, list_revs);
1401 result = PyTuple_Pack(1, list_revs);
1361 goto done;
1402 goto done;
1362 }
1403 }
1363
1404
1364 /* Turn the python list into a native integer array (for efficiency) */
1405 /* Turn the python list into a native integer array (for efficiency) */
1365 revs = (Py_ssize_t *)calloc(num_revs, sizeof(Py_ssize_t));
1406 revs = (Py_ssize_t *)calloc(num_revs, sizeof(Py_ssize_t));
1366 if (revs == NULL) {
1407 if (revs == NULL) {
1367 PyErr_NoMemory();
1408 PyErr_NoMemory();
1368 goto bail;
1409 goto bail;
1369 }
1410 }
1370 for (i = 0; i < num_revs; i++) {
1411 for (i = 0; i < num_revs; i++) {
1371 Py_ssize_t revnum = PyInt_AsLong(PyList_GET_ITEM(list_revs, i));
1412 Py_ssize_t revnum = PyInt_AsLong(PyList_GET_ITEM(list_revs, i));
1372 if (revnum == -1 && PyErr_Occurred()) {
1413 if (revnum == -1 && PyErr_Occurred()) {
1373 goto bail;
1414 goto bail;
1374 }
1415 }
1375 if (revnum < nullrev || revnum >= idxlen) {
1416 if (revnum < nullrev || revnum >= idxlen) {
1376 PyErr_Format(PyExc_IndexError,
1417 PyErr_Format(PyExc_IndexError,
1377 "index out of range: %zd", revnum);
1418 "index out of range: %zd", revnum);
1378 goto bail;
1419 goto bail;
1379 }
1420 }
1380 revs[i] = revnum;
1421 revs[i] = revnum;
1381 }
1422 }
1382
1423
1383 /* Compute and check various property of the unsliced delta chain */
1424 /* Compute and check various property of the unsliced delta chain */
1384 deltachainspan = index_segment_span(self, revs[0], revs[num_revs - 1]);
1425 deltachainspan = index_segment_span(self, revs[0], revs[num_revs - 1]);
1385 if (deltachainspan < 0) {
1426 if (deltachainspan < 0) {
1386 goto bail;
1427 goto bail;
1387 }
1428 }
1388
1429
1389 if (deltachainspan <= mingapsize) {
1430 if (deltachainspan <= mingapsize) {
1390 result = PyTuple_Pack(1, list_revs);
1431 result = PyTuple_Pack(1, list_revs);
1391 goto done;
1432 goto done;
1392 }
1433 }
1393 chainpayload = 0;
1434 chainpayload = 0;
1394 for (i = 0; i < num_revs; i++) {
1435 for (i = 0; i < num_revs; i++) {
1395 int tmp = index_get_length(self, revs[i]);
1436 int tmp = index_get_length(self, revs[i]);
1396 if (tmp < 0) {
1437 if (tmp < 0) {
1397 goto bail;
1438 goto bail;
1398 }
1439 }
1399 chainpayload += tmp;
1440 chainpayload += tmp;
1400 }
1441 }
1401
1442
1402 readdata = deltachainspan;
1443 readdata = deltachainspan;
1403 density = 1.0;
1444 density = 1.0;
1404
1445
1405 if (0 < deltachainspan) {
1446 if (0 < deltachainspan) {
1406 density = (double)chainpayload / (double)deltachainspan;
1447 density = (double)chainpayload / (double)deltachainspan;
1407 }
1448 }
1408
1449
1409 if (density >= targetdensity) {
1450 if (density >= targetdensity) {
1410 result = PyTuple_Pack(1, list_revs);
1451 result = PyTuple_Pack(1, list_revs);
1411 goto done;
1452 goto done;
1412 }
1453 }
1413
1454
1414 /* if chain is too sparse, look for relevant gaps */
1455 /* if chain is too sparse, look for relevant gaps */
1415 gaps = (struct Gap *)calloc(num_revs, sizeof(struct Gap));
1456 gaps = (struct Gap *)calloc(num_revs, sizeof(struct Gap));
1416 if (gaps == NULL) {
1457 if (gaps == NULL) {
1417 PyErr_NoMemory();
1458 PyErr_NoMemory();
1418 goto bail;
1459 goto bail;
1419 }
1460 }
1420
1461
1421 previous_end = -1;
1462 previous_end = -1;
1422 for (i = 0; i < num_revs; i++) {
1463 for (i = 0; i < num_revs; i++) {
1423 int64_t revstart;
1464 int64_t revstart;
1424 int revsize;
1465 int revsize;
1425 revstart = index_get_start(self, revs[i]);
1466 revstart = index_get_start(self, revs[i]);
1426 if (revstart < 0) {
1467 if (revstart < 0) {
1427 goto bail;
1468 goto bail;
1428 };
1469 };
1429 revsize = index_get_length(self, revs[i]);
1470 revsize = index_get_length(self, revs[i]);
1430 if (revsize < 0) {
1471 if (revsize < 0) {
1431 goto bail;
1472 goto bail;
1432 };
1473 };
1433 if (revsize == 0) {
1474 if (revsize == 0) {
1434 continue;
1475 continue;
1435 }
1476 }
1436 if (previous_end >= 0) {
1477 if (previous_end >= 0) {
1437 int64_t gapsize = revstart - previous_end;
1478 int64_t gapsize = revstart - previous_end;
1438 if (gapsize > mingapsize) {
1479 if (gapsize > mingapsize) {
1439 gaps[num_gaps].size = gapsize;
1480 gaps[num_gaps].size = gapsize;
1440 gaps[num_gaps].idx = i;
1481 gaps[num_gaps].idx = i;
1441 num_gaps += 1;
1482 num_gaps += 1;
1442 }
1483 }
1443 }
1484 }
1444 previous_end = revstart + revsize;
1485 previous_end = revstart + revsize;
1445 }
1486 }
1446 if (num_gaps == 0) {
1487 if (num_gaps == 0) {
1447 result = PyTuple_Pack(1, list_revs);
1488 result = PyTuple_Pack(1, list_revs);
1448 goto done;
1489 goto done;
1449 }
1490 }
1450 qsort(gaps, num_gaps, sizeof(struct Gap), &gap_compare);
1491 qsort(gaps, num_gaps, sizeof(struct Gap), &gap_compare);
1451
1492
1452 /* Slice the largest gap first, they improve the density the most */
1493 /* Slice the largest gap first, they improve the density the most */
1453 selected_indices =
1494 selected_indices =
1454 (Py_ssize_t *)malloc((num_gaps + 1) * sizeof(Py_ssize_t));
1495 (Py_ssize_t *)malloc((num_gaps + 1) * sizeof(Py_ssize_t));
1455 if (selected_indices == NULL) {
1496 if (selected_indices == NULL) {
1456 PyErr_NoMemory();
1497 PyErr_NoMemory();
1457 goto bail;
1498 goto bail;
1458 }
1499 }
1459
1500
1460 for (i = num_gaps - 1; i >= 0; i--) {
1501 for (i = num_gaps - 1; i >= 0; i--) {
1461 selected_indices[num_selected] = gaps[i].idx;
1502 selected_indices[num_selected] = gaps[i].idx;
1462 readdata -= gaps[i].size;
1503 readdata -= gaps[i].size;
1463 num_selected += 1;
1504 num_selected += 1;
1464 if (readdata <= 0) {
1505 if (readdata <= 0) {
1465 density = 1.0;
1506 density = 1.0;
1466 } else {
1507 } else {
1467 density = (double)chainpayload / (double)readdata;
1508 density = (double)chainpayload / (double)readdata;
1468 }
1509 }
1469 if (density >= targetdensity) {
1510 if (density >= targetdensity) {
1470 break;
1511 break;
1471 }
1512 }
1472 }
1513 }
1473 qsort(selected_indices, num_selected, sizeof(Py_ssize_t),
1514 qsort(selected_indices, num_selected, sizeof(Py_ssize_t),
1474 &Py_ssize_t_compare);
1515 &Py_ssize_t_compare);
1475
1516
1476 /* create the resulting slice */
1517 /* create the resulting slice */
1477 allchunks = PyList_New(0);
1518 allchunks = PyList_New(0);
1478 if (allchunks == NULL) {
1519 if (allchunks == NULL) {
1479 goto bail;
1520 goto bail;
1480 }
1521 }
1481 previdx = 0;
1522 previdx = 0;
1482 selected_indices[num_selected] = num_revs;
1523 selected_indices[num_selected] = num_revs;
1483 for (i = 0; i <= num_selected; i++) {
1524 for (i = 0; i <= num_selected; i++) {
1484 Py_ssize_t idx = selected_indices[i];
1525 Py_ssize_t idx = selected_indices[i];
1485 Py_ssize_t endidx = trim_endidx(self, revs, previdx, idx);
1526 Py_ssize_t endidx = trim_endidx(self, revs, previdx, idx);
1486 if (endidx < 0) {
1527 if (endidx < 0) {
1487 goto bail;
1528 goto bail;
1488 }
1529 }
1489 if (previdx < endidx) {
1530 if (previdx < endidx) {
1490 chunk = PyList_GetSlice(list_revs, previdx, endidx);
1531 chunk = PyList_GetSlice(list_revs, previdx, endidx);
1491 if (chunk == NULL) {
1532 if (chunk == NULL) {
1492 goto bail;
1533 goto bail;
1493 }
1534 }
1494 if (PyList_Append(allchunks, chunk) == -1) {
1535 if (PyList_Append(allchunks, chunk) == -1) {
1495 goto bail;
1536 goto bail;
1496 }
1537 }
1497 Py_DECREF(chunk);
1538 Py_DECREF(chunk);
1498 chunk = NULL;
1539 chunk = NULL;
1499 }
1540 }
1500 previdx = idx;
1541 previdx = idx;
1501 }
1542 }
1502 result = allchunks;
1543 result = allchunks;
1503 goto done;
1544 goto done;
1504
1545
1505 bail:
1546 bail:
1506 Py_XDECREF(allchunks);
1547 Py_XDECREF(allchunks);
1507 Py_XDECREF(chunk);
1548 Py_XDECREF(chunk);
1508 done:
1549 done:
1509 free(revs);
1550 free(revs);
1510 free(gaps);
1551 free(gaps);
1511 free(selected_indices);
1552 free(selected_indices);
1512 return result;
1553 return result;
1513 }
1554 }
1514
1555
1515 static inline int nt_level(const char *node, Py_ssize_t level)
1556 static inline int nt_level(const char *node, Py_ssize_t level)
1516 {
1557 {
1517 int v = node[level >> 1];
1558 int v = node[level >> 1];
1518 if (!(level & 1))
1559 if (!(level & 1))
1519 v >>= 4;
1560 v >>= 4;
1520 return v & 0xf;
1561 return v & 0xf;
1521 }
1562 }
1522
1563
1523 /*
1564 /*
1524 * Return values:
1565 * Return values:
1525 *
1566 *
1526 * -4: match is ambiguous (multiple candidates)
1567 * -4: match is ambiguous (multiple candidates)
1527 * -2: not found
1568 * -2: not found
1528 * rest: valid rev
1569 * rest: valid rev
1529 */
1570 */
1530 static int nt_find(nodetree *self, const char *node, Py_ssize_t nodelen,
1571 static int nt_find(nodetree *self, const char *node, Py_ssize_t nodelen,
1531 int hex)
1572 int hex)
1532 {
1573 {
1533 int (*getnybble)(const char *, Py_ssize_t) = hex ? hexdigit : nt_level;
1574 int (*getnybble)(const char *, Py_ssize_t) = hex ? hexdigit : nt_level;
1534 int level, maxlevel, off;
1575 int level, maxlevel, off;
1535
1576
1536 if (nodelen == 20 && node[0] == '\0' && memcmp(node, nullid, 20) == 0)
1577 if (nodelen == 20 && node[0] == '\0' && memcmp(node, nullid, 20) == 0)
1537 return -1;
1578 return -1;
1538
1579
1539 if (hex)
1580 if (hex)
1540 maxlevel = nodelen > 40 ? 40 : (int)nodelen;
1581 maxlevel = nodelen > 40 ? 40 : (int)nodelen;
1541 else
1582 else
1542 maxlevel = nodelen > 20 ? 40 : ((int)nodelen * 2);
1583 maxlevel = nodelen > 20 ? 40 : ((int)nodelen * 2);
1543
1584
1544 for (level = off = 0; level < maxlevel; level++) {
1585 for (level = off = 0; level < maxlevel; level++) {
1545 int k = getnybble(node, level);
1586 int k = getnybble(node, level);
1546 nodetreenode *n = &self->nodes[off];
1587 nodetreenode *n = &self->nodes[off];
1547 int v = n->children[k];
1588 int v = n->children[k];
1548
1589
1549 if (v < 0) {
1590 if (v < 0) {
1550 const char *n;
1591 const char *n;
1551 Py_ssize_t i;
1592 Py_ssize_t i;
1552
1593
1553 v = -(v + 2);
1594 v = -(v + 2);
1554 n = index_node(self->index, v);
1595 n = index_node(self->index, v);
1555 if (n == NULL)
1596 if (n == NULL)
1556 return -2;
1597 return -2;
1557 for (i = level; i < maxlevel; i++)
1598 for (i = level; i < maxlevel; i++)
1558 if (getnybble(node, i) != nt_level(n, i))
1599 if (getnybble(node, i) != nt_level(n, i))
1559 return -2;
1600 return -2;
1560 return v;
1601 return v;
1561 }
1602 }
1562 if (v == 0)
1603 if (v == 0)
1563 return -2;
1604 return -2;
1564 off = v;
1605 off = v;
1565 }
1606 }
1566 /* multiple matches against an ambiguous prefix */
1607 /* multiple matches against an ambiguous prefix */
1567 return -4;
1608 return -4;
1568 }
1609 }
1569
1610
1570 static int nt_new(nodetree *self)
1611 static int nt_new(nodetree *self)
1571 {
1612 {
1572 if (self->length == self->capacity) {
1613 if (self->length == self->capacity) {
1573 unsigned newcapacity;
1614 unsigned newcapacity;
1574 nodetreenode *newnodes;
1615 nodetreenode *newnodes;
1575 newcapacity = self->capacity * 2;
1616 newcapacity = self->capacity * 2;
1576 if (newcapacity >= INT_MAX / sizeof(nodetreenode)) {
1617 if (newcapacity >= INT_MAX / sizeof(nodetreenode)) {
1577 PyErr_SetString(PyExc_MemoryError,
1618 PyErr_SetString(PyExc_MemoryError,
1578 "overflow in nt_new");
1619 "overflow in nt_new");
1579 return -1;
1620 return -1;
1580 }
1621 }
1581 newnodes =
1622 newnodes =
1582 realloc(self->nodes, newcapacity * sizeof(nodetreenode));
1623 realloc(self->nodes, newcapacity * sizeof(nodetreenode));
1583 if (newnodes == NULL) {
1624 if (newnodes == NULL) {
1584 PyErr_SetString(PyExc_MemoryError, "out of memory");
1625 PyErr_SetString(PyExc_MemoryError, "out of memory");
1585 return -1;
1626 return -1;
1586 }
1627 }
1587 self->capacity = newcapacity;
1628 self->capacity = newcapacity;
1588 self->nodes = newnodes;
1629 self->nodes = newnodes;
1589 memset(&self->nodes[self->length], 0,
1630 memset(&self->nodes[self->length], 0,
1590 sizeof(nodetreenode) * (self->capacity - self->length));
1631 sizeof(nodetreenode) * (self->capacity - self->length));
1591 }
1632 }
1592 return self->length++;
1633 return self->length++;
1593 }
1634 }
1594
1635
1595 static int nt_insert(nodetree *self, const char *node, int rev)
1636 static int nt_insert(nodetree *self, const char *node, int rev)
1596 {
1637 {
1597 int level = 0;
1638 int level = 0;
1598 int off = 0;
1639 int off = 0;
1599
1640
1600 while (level < 40) {
1641 while (level < 40) {
1601 int k = nt_level(node, level);
1642 int k = nt_level(node, level);
1602 nodetreenode *n;
1643 nodetreenode *n;
1603 int v;
1644 int v;
1604
1645
1605 n = &self->nodes[off];
1646 n = &self->nodes[off];
1606 v = n->children[k];
1647 v = n->children[k];
1607
1648
1608 if (v == 0) {
1649 if (v == 0) {
1609 n->children[k] = -rev - 2;
1650 n->children[k] = -rev - 2;
1610 return 0;
1651 return 0;
1611 }
1652 }
1612 if (v < 0) {
1653 if (v < 0) {
1613 const char *oldnode =
1654 const char *oldnode =
1614 index_node_existing(self->index, -(v + 2));
1655 index_node_existing(self->index, -(v + 2));
1615 int noff;
1656 int noff;
1616
1657
1617 if (oldnode == NULL)
1658 if (oldnode == NULL)
1618 return -1;
1659 return -1;
1619 if (!memcmp(oldnode, node, 20)) {
1660 if (!memcmp(oldnode, node, 20)) {
1620 n->children[k] = -rev - 2;
1661 n->children[k] = -rev - 2;
1621 return 0;
1662 return 0;
1622 }
1663 }
1623 noff = nt_new(self);
1664 noff = nt_new(self);
1624 if (noff == -1)
1665 if (noff == -1)
1625 return -1;
1666 return -1;
1626 /* self->nodes may have been changed by realloc */
1667 /* self->nodes may have been changed by realloc */
1627 self->nodes[off].children[k] = noff;
1668 self->nodes[off].children[k] = noff;
1628 off = noff;
1669 off = noff;
1629 n = &self->nodes[off];
1670 n = &self->nodes[off];
1630 n->children[nt_level(oldnode, ++level)] = v;
1671 n->children[nt_level(oldnode, ++level)] = v;
1631 if (level > self->depth)
1672 if (level > self->depth)
1632 self->depth = level;
1673 self->depth = level;
1633 self->splits += 1;
1674 self->splits += 1;
1634 } else {
1675 } else {
1635 level += 1;
1676 level += 1;
1636 off = v;
1677 off = v;
1637 }
1678 }
1638 }
1679 }
1639
1680
1640 return -1;
1681 return -1;
1641 }
1682 }
1642
1683
1643 static PyObject *ntobj_insert(nodetreeObject *self, PyObject *args)
1684 static PyObject *ntobj_insert(nodetreeObject *self, PyObject *args)
1644 {
1685 {
1645 Py_ssize_t rev;
1686 Py_ssize_t rev;
1646 const char *node;
1687 const char *node;
1647 Py_ssize_t length;
1688 Py_ssize_t length;
1648 if (!PyArg_ParseTuple(args, "n", &rev))
1689 if (!PyArg_ParseTuple(args, "n", &rev))
1649 return NULL;
1690 return NULL;
1650 length = index_length(self->nt.index);
1691 length = index_length(self->nt.index);
1651 if (rev < 0 || rev >= length) {
1692 if (rev < 0 || rev >= length) {
1652 PyErr_SetString(PyExc_ValueError, "revlog index out of range");
1693 PyErr_SetString(PyExc_ValueError, "revlog index out of range");
1653 return NULL;
1694 return NULL;
1654 }
1695 }
1655 node = index_node_existing(self->nt.index, rev);
1696 node = index_node_existing(self->nt.index, rev);
1656 if (nt_insert(&self->nt, node, (int)rev) == -1)
1697 if (nt_insert(&self->nt, node, (int)rev) == -1)
1657 return NULL;
1698 return NULL;
1658 Py_RETURN_NONE;
1699 Py_RETURN_NONE;
1659 }
1700 }
1660
1701
1661 static int nt_delete_node(nodetree *self, const char *node)
1702 static int nt_delete_node(nodetree *self, const char *node)
1662 {
1703 {
1663 /* rev==-2 happens to get encoded as 0, which is interpreted as not set
1704 /* rev==-2 happens to get encoded as 0, which is interpreted as not set
1664 */
1705 */
1665 return nt_insert(self, node, -2);
1706 return nt_insert(self, node, -2);
1666 }
1707 }
1667
1708
1668 static int nt_init(nodetree *self, indexObject *index, unsigned capacity)
1709 static int nt_init(nodetree *self, indexObject *index, unsigned capacity)
1669 {
1710 {
1670 /* Initialize before overflow-checking to avoid nt_dealloc() crash. */
1711 /* Initialize before overflow-checking to avoid nt_dealloc() crash. */
1671 self->nodes = NULL;
1712 self->nodes = NULL;
1672
1713
1673 self->index = index;
1714 self->index = index;
1674 /* The input capacity is in terms of revisions, while the field is in
1715 /* The input capacity is in terms of revisions, while the field is in
1675 * terms of nodetree nodes. */
1716 * terms of nodetree nodes. */
1676 self->capacity = (capacity < 4 ? 4 : capacity / 2);
1717 self->capacity = (capacity < 4 ? 4 : capacity / 2);
1677 self->depth = 0;
1718 self->depth = 0;
1678 self->splits = 0;
1719 self->splits = 0;
1679 if ((size_t)self->capacity > INT_MAX / sizeof(nodetreenode)) {
1720 if ((size_t)self->capacity > INT_MAX / sizeof(nodetreenode)) {
1680 PyErr_SetString(PyExc_ValueError, "overflow in init_nt");
1721 PyErr_SetString(PyExc_ValueError, "overflow in init_nt");
1681 return -1;
1722 return -1;
1682 }
1723 }
1683 self->nodes = calloc(self->capacity, sizeof(nodetreenode));
1724 self->nodes = calloc(self->capacity, sizeof(nodetreenode));
1684 if (self->nodes == NULL) {
1725 if (self->nodes == NULL) {
1685 PyErr_NoMemory();
1726 PyErr_NoMemory();
1686 return -1;
1727 return -1;
1687 }
1728 }
1688 self->length = 1;
1729 self->length = 1;
1689 return 0;
1730 return 0;
1690 }
1731 }
1691
1732
1692 static int ntobj_init(nodetreeObject *self, PyObject *args)
1733 static int ntobj_init(nodetreeObject *self, PyObject *args)
1693 {
1734 {
1694 PyObject *index;
1735 PyObject *index;
1695 unsigned capacity;
1736 unsigned capacity;
1696 if (!PyArg_ParseTuple(args, "O!I", &HgRevlogIndex_Type, &index,
1737 if (!PyArg_ParseTuple(args, "O!I", &HgRevlogIndex_Type, &index,
1697 &capacity))
1738 &capacity))
1698 return -1;
1739 return -1;
1699 Py_INCREF(index);
1740 Py_INCREF(index);
1700 return nt_init(&self->nt, (indexObject *)index, capacity);
1741 return nt_init(&self->nt, (indexObject *)index, capacity);
1701 }
1742 }
1702
1743
1703 static int nt_partialmatch(nodetree *self, const char *node, Py_ssize_t nodelen)
1744 static int nt_partialmatch(nodetree *self, const char *node, Py_ssize_t nodelen)
1704 {
1745 {
1705 return nt_find(self, node, nodelen, 1);
1746 return nt_find(self, node, nodelen, 1);
1706 }
1747 }
1707
1748
1708 /*
1749 /*
1709 * Find the length of the shortest unique prefix of node.
1750 * Find the length of the shortest unique prefix of node.
1710 *
1751 *
1711 * Return values:
1752 * Return values:
1712 *
1753 *
1713 * -3: error (exception set)
1754 * -3: error (exception set)
1714 * -2: not found (no exception set)
1755 * -2: not found (no exception set)
1715 * rest: length of shortest prefix
1756 * rest: length of shortest prefix
1716 */
1757 */
1717 static int nt_shortest(nodetree *self, const char *node)
1758 static int nt_shortest(nodetree *self, const char *node)
1718 {
1759 {
1719 int level, off;
1760 int level, off;
1720
1761
1721 for (level = off = 0; level < 40; level++) {
1762 for (level = off = 0; level < 40; level++) {
1722 int k, v;
1763 int k, v;
1723 nodetreenode *n = &self->nodes[off];
1764 nodetreenode *n = &self->nodes[off];
1724 k = nt_level(node, level);
1765 k = nt_level(node, level);
1725 v = n->children[k];
1766 v = n->children[k];
1726 if (v < 0) {
1767 if (v < 0) {
1727 const char *n;
1768 const char *n;
1728 v = -(v + 2);
1769 v = -(v + 2);
1729 n = index_node_existing(self->index, v);
1770 n = index_node_existing(self->index, v);
1730 if (n == NULL)
1771 if (n == NULL)
1731 return -3;
1772 return -3;
1732 if (memcmp(node, n, 20) != 0)
1773 if (memcmp(node, n, 20) != 0)
1733 /*
1774 /*
1734 * Found a unique prefix, but it wasn't for the
1775 * Found a unique prefix, but it wasn't for the
1735 * requested node (i.e the requested node does
1776 * requested node (i.e the requested node does
1736 * not exist).
1777 * not exist).
1737 */
1778 */
1738 return -2;
1779 return -2;
1739 return level + 1;
1780 return level + 1;
1740 }
1781 }
1741 if (v == 0)
1782 if (v == 0)
1742 return -2;
1783 return -2;
1743 off = v;
1784 off = v;
1744 }
1785 }
1745 /*
1786 /*
1746 * The node was still not unique after 40 hex digits, so this won't
1787 * The node was still not unique after 40 hex digits, so this won't
1747 * happen. Also, if we get here, then there's a programming error in
1788 * happen. Also, if we get here, then there's a programming error in
1748 * this file that made us insert a node longer than 40 hex digits.
1789 * this file that made us insert a node longer than 40 hex digits.
1749 */
1790 */
1750 PyErr_SetString(PyExc_Exception, "broken node tree");
1791 PyErr_SetString(PyExc_Exception, "broken node tree");
1751 return -3;
1792 return -3;
1752 }
1793 }
1753
1794
1754 static PyObject *ntobj_shortest(nodetreeObject *self, PyObject *args)
1795 static PyObject *ntobj_shortest(nodetreeObject *self, PyObject *args)
1755 {
1796 {
1756 PyObject *val;
1797 PyObject *val;
1757 char *node;
1798 char *node;
1758 int length;
1799 int length;
1759
1800
1760 if (!PyArg_ParseTuple(args, "O", &val))
1801 if (!PyArg_ParseTuple(args, "O", &val))
1761 return NULL;
1802 return NULL;
1762 if (node_check(val, &node) == -1)
1803 if (node_check(val, &node) == -1)
1763 return NULL;
1804 return NULL;
1764
1805
1765 length = nt_shortest(&self->nt, node);
1806 length = nt_shortest(&self->nt, node);
1766 if (length == -3)
1807 if (length == -3)
1767 return NULL;
1808 return NULL;
1768 if (length == -2) {
1809 if (length == -2) {
1769 raise_revlog_error();
1810 raise_revlog_error();
1770 return NULL;
1811 return NULL;
1771 }
1812 }
1772 return PyInt_FromLong(length);
1813 return PyInt_FromLong(length);
1773 }
1814 }
1774
1815
1775 static void nt_dealloc(nodetree *self)
1816 static void nt_dealloc(nodetree *self)
1776 {
1817 {
1777 free(self->nodes);
1818 free(self->nodes);
1778 self->nodes = NULL;
1819 self->nodes = NULL;
1779 }
1820 }
1780
1821
1781 static void ntobj_dealloc(nodetreeObject *self)
1822 static void ntobj_dealloc(nodetreeObject *self)
1782 {
1823 {
1783 Py_XDECREF(self->nt.index);
1824 Py_XDECREF(self->nt.index);
1784 nt_dealloc(&self->nt);
1825 nt_dealloc(&self->nt);
1785 PyObject_Del(self);
1826 PyObject_Del(self);
1786 }
1827 }
1787
1828
1788 static PyMethodDef ntobj_methods[] = {
1829 static PyMethodDef ntobj_methods[] = {
1789 {"insert", (PyCFunction)ntobj_insert, METH_VARARGS,
1830 {"insert", (PyCFunction)ntobj_insert, METH_VARARGS,
1790 "insert an index entry"},
1831 "insert an index entry"},
1791 {"shortest", (PyCFunction)ntobj_shortest, METH_VARARGS,
1832 {"shortest", (PyCFunction)ntobj_shortest, METH_VARARGS,
1792 "find length of shortest hex nodeid of a binary ID"},
1833 "find length of shortest hex nodeid of a binary ID"},
1793 {NULL} /* Sentinel */
1834 {NULL} /* Sentinel */
1794 };
1835 };
1795
1836
1796 static PyTypeObject nodetreeType = {
1837 static PyTypeObject nodetreeType = {
1797 PyVarObject_HEAD_INIT(NULL, 0) /* header */
1838 PyVarObject_HEAD_INIT(NULL, 0) /* header */
1798 "parsers.nodetree", /* tp_name */
1839 "parsers.nodetree", /* tp_name */
1799 sizeof(nodetreeObject), /* tp_basicsize */
1840 sizeof(nodetreeObject), /* tp_basicsize */
1800 0, /* tp_itemsize */
1841 0, /* tp_itemsize */
1801 (destructor)ntobj_dealloc, /* tp_dealloc */
1842 (destructor)ntobj_dealloc, /* tp_dealloc */
1802 0, /* tp_print */
1843 0, /* tp_print */
1803 0, /* tp_getattr */
1844 0, /* tp_getattr */
1804 0, /* tp_setattr */
1845 0, /* tp_setattr */
1805 0, /* tp_compare */
1846 0, /* tp_compare */
1806 0, /* tp_repr */
1847 0, /* tp_repr */
1807 0, /* tp_as_number */
1848 0, /* tp_as_number */
1808 0, /* tp_as_sequence */
1849 0, /* tp_as_sequence */
1809 0, /* tp_as_mapping */
1850 0, /* tp_as_mapping */
1810 0, /* tp_hash */
1851 0, /* tp_hash */
1811 0, /* tp_call */
1852 0, /* tp_call */
1812 0, /* tp_str */
1853 0, /* tp_str */
1813 0, /* tp_getattro */
1854 0, /* tp_getattro */
1814 0, /* tp_setattro */
1855 0, /* tp_setattro */
1815 0, /* tp_as_buffer */
1856 0, /* tp_as_buffer */
1816 Py_TPFLAGS_DEFAULT, /* tp_flags */
1857 Py_TPFLAGS_DEFAULT, /* tp_flags */
1817 "nodetree", /* tp_doc */
1858 "nodetree", /* tp_doc */
1818 0, /* tp_traverse */
1859 0, /* tp_traverse */
1819 0, /* tp_clear */
1860 0, /* tp_clear */
1820 0, /* tp_richcompare */
1861 0, /* tp_richcompare */
1821 0, /* tp_weaklistoffset */
1862 0, /* tp_weaklistoffset */
1822 0, /* tp_iter */
1863 0, /* tp_iter */
1823 0, /* tp_iternext */
1864 0, /* tp_iternext */
1824 ntobj_methods, /* tp_methods */
1865 ntobj_methods, /* tp_methods */
1825 0, /* tp_members */
1866 0, /* tp_members */
1826 0, /* tp_getset */
1867 0, /* tp_getset */
1827 0, /* tp_base */
1868 0, /* tp_base */
1828 0, /* tp_dict */
1869 0, /* tp_dict */
1829 0, /* tp_descr_get */
1870 0, /* tp_descr_get */
1830 0, /* tp_descr_set */
1871 0, /* tp_descr_set */
1831 0, /* tp_dictoffset */
1872 0, /* tp_dictoffset */
1832 (initproc)ntobj_init, /* tp_init */
1873 (initproc)ntobj_init, /* tp_init */
1833 0, /* tp_alloc */
1874 0, /* tp_alloc */
1834 };
1875 };
1835
1876
1836 static int index_init_nt(indexObject *self)
1877 static int index_init_nt(indexObject *self)
1837 {
1878 {
1838 if (!self->ntinitialized) {
1879 if (!self->ntinitialized) {
1839 if (nt_init(&self->nt, self, (int)self->raw_length) == -1) {
1880 if (nt_init(&self->nt, self, (int)self->raw_length) == -1) {
1840 nt_dealloc(&self->nt);
1881 nt_dealloc(&self->nt);
1841 return -1;
1882 return -1;
1842 }
1883 }
1843 if (nt_insert(&self->nt, nullid, -1) == -1) {
1884 if (nt_insert(&self->nt, nullid, -1) == -1) {
1844 nt_dealloc(&self->nt);
1885 nt_dealloc(&self->nt);
1845 return -1;
1886 return -1;
1846 }
1887 }
1847 self->ntinitialized = 1;
1888 self->ntinitialized = 1;
1848 self->ntrev = (int)index_length(self);
1889 self->ntrev = (int)index_length(self);
1849 self->ntlookups = 1;
1890 self->ntlookups = 1;
1850 self->ntmisses = 0;
1891 self->ntmisses = 0;
1851 }
1892 }
1852 return 0;
1893 return 0;
1853 }
1894 }
1854
1895
1855 /*
1896 /*
1856 * Return values:
1897 * Return values:
1857 *
1898 *
1858 * -3: error (exception set)
1899 * -3: error (exception set)
1859 * -2: not found (no exception set)
1900 * -2: not found (no exception set)
1860 * rest: valid rev
1901 * rest: valid rev
1861 */
1902 */
1862 static int index_find_node(indexObject *self, const char *node,
1903 static int index_find_node(indexObject *self, const char *node,
1863 Py_ssize_t nodelen)
1904 Py_ssize_t nodelen)
1864 {
1905 {
1865 int rev;
1906 int rev;
1866
1907
1867 if (index_init_nt(self) == -1)
1908 if (index_init_nt(self) == -1)
1868 return -3;
1909 return -3;
1869
1910
1870 self->ntlookups++;
1911 self->ntlookups++;
1871 rev = nt_find(&self->nt, node, nodelen, 0);
1912 rev = nt_find(&self->nt, node, nodelen, 0);
1872 if (rev >= -1)
1913 if (rev >= -1)
1873 return rev;
1914 return rev;
1874
1915
1875 /*
1916 /*
1876 * For the first handful of lookups, we scan the entire index,
1917 * For the first handful of lookups, we scan the entire index,
1877 * and cache only the matching nodes. This optimizes for cases
1918 * and cache only the matching nodes. This optimizes for cases
1878 * like "hg tip", where only a few nodes are accessed.
1919 * like "hg tip", where only a few nodes are accessed.
1879 *
1920 *
1880 * After that, we cache every node we visit, using a single
1921 * After that, we cache every node we visit, using a single
1881 * scan amortized over multiple lookups. This gives the best
1922 * scan amortized over multiple lookups. This gives the best
1882 * bulk performance, e.g. for "hg log".
1923 * bulk performance, e.g. for "hg log".
1883 */
1924 */
1884 if (self->ntmisses++ < 4) {
1925 if (self->ntmisses++ < 4) {
1885 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1926 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1886 const char *n = index_node_existing(self, rev);
1927 const char *n = index_node_existing(self, rev);
1887 if (n == NULL)
1928 if (n == NULL)
1888 return -3;
1929 return -3;
1889 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
1930 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
1890 if (nt_insert(&self->nt, n, rev) == -1)
1931 if (nt_insert(&self->nt, n, rev) == -1)
1891 return -3;
1932 return -3;
1892 break;
1933 break;
1893 }
1934 }
1894 }
1935 }
1895 } else {
1936 } else {
1896 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1937 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1897 const char *n = index_node_existing(self, rev);
1938 const char *n = index_node_existing(self, rev);
1898 if (n == NULL)
1939 if (n == NULL)
1899 return -3;
1940 return -3;
1900 if (nt_insert(&self->nt, n, rev) == -1) {
1941 if (nt_insert(&self->nt, n, rev) == -1) {
1901 self->ntrev = rev + 1;
1942 self->ntrev = rev + 1;
1902 return -3;
1943 return -3;
1903 }
1944 }
1904 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
1945 if (memcmp(node, n, nodelen > 20 ? 20 : nodelen) == 0) {
1905 break;
1946 break;
1906 }
1947 }
1907 }
1948 }
1908 self->ntrev = rev;
1949 self->ntrev = rev;
1909 }
1950 }
1910
1951
1911 if (rev >= 0)
1952 if (rev >= 0)
1912 return rev;
1953 return rev;
1913 return -2;
1954 return -2;
1914 }
1955 }
1915
1956
1916 static PyObject *index_getitem(indexObject *self, PyObject *value)
1957 static PyObject *index_getitem(indexObject *self, PyObject *value)
1917 {
1958 {
1918 char *node;
1959 char *node;
1919 int rev;
1960 int rev;
1920
1961
1921 if (PyInt_Check(value)) {
1962 if (PyInt_Check(value)) {
1922 long idx;
1963 long idx;
1923 if (!pylong_to_long(value, &idx)) {
1964 if (!pylong_to_long(value, &idx)) {
1924 return NULL;
1965 return NULL;
1925 }
1966 }
1926 return index_get(self, idx);
1967 return index_get(self, idx);
1927 }
1968 }
1928
1969
1929 if (node_check(value, &node) == -1)
1970 if (node_check(value, &node) == -1)
1930 return NULL;
1971 return NULL;
1931 rev = index_find_node(self, node, 20);
1972 rev = index_find_node(self, node, 20);
1932 if (rev >= -1)
1973 if (rev >= -1)
1933 return PyInt_FromLong(rev);
1974 return PyInt_FromLong(rev);
1934 if (rev == -2)
1975 if (rev == -2)
1935 raise_revlog_error();
1976 raise_revlog_error();
1936 return NULL;
1977 return NULL;
1937 }
1978 }
1938
1979
1939 /*
1980 /*
1940 * Fully populate the radix tree.
1981 * Fully populate the radix tree.
1941 */
1982 */
1942 static int index_populate_nt(indexObject *self)
1983 static int index_populate_nt(indexObject *self)
1943 {
1984 {
1944 int rev;
1985 int rev;
1945 if (self->ntrev > 0) {
1986 if (self->ntrev > 0) {
1946 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1987 for (rev = self->ntrev - 1; rev >= 0; rev--) {
1947 const char *n = index_node_existing(self, rev);
1988 const char *n = index_node_existing(self, rev);
1948 if (n == NULL)
1989 if (n == NULL)
1949 return -1;
1990 return -1;
1950 if (nt_insert(&self->nt, n, rev) == -1)
1991 if (nt_insert(&self->nt, n, rev) == -1)
1951 return -1;
1992 return -1;
1952 }
1993 }
1953 self->ntrev = -1;
1994 self->ntrev = -1;
1954 }
1995 }
1955 return 0;
1996 return 0;
1956 }
1997 }
1957
1998
1958 static PyObject *index_partialmatch(indexObject *self, PyObject *args)
1999 static PyObject *index_partialmatch(indexObject *self, PyObject *args)
1959 {
2000 {
1960 const char *fullnode;
2001 const char *fullnode;
1961 Py_ssize_t nodelen;
2002 Py_ssize_t nodelen;
1962 char *node;
2003 char *node;
1963 int rev, i;
2004 int rev, i;
1964
2005
1965 if (!PyArg_ParseTuple(args, PY23("s#", "y#"), &node, &nodelen))
2006 if (!PyArg_ParseTuple(args, PY23("s#", "y#"), &node, &nodelen))
1966 return NULL;
2007 return NULL;
1967
2008
1968 if (nodelen < 1) {
2009 if (nodelen < 1) {
1969 PyErr_SetString(PyExc_ValueError, "key too short");
2010 PyErr_SetString(PyExc_ValueError, "key too short");
1970 return NULL;
2011 return NULL;
1971 }
2012 }
1972
2013
1973 if (nodelen > 40) {
2014 if (nodelen > 40) {
1974 PyErr_SetString(PyExc_ValueError, "key too long");
2015 PyErr_SetString(PyExc_ValueError, "key too long");
1975 return NULL;
2016 return NULL;
1976 }
2017 }
1977
2018
1978 for (i = 0; i < nodelen; i++)
2019 for (i = 0; i < nodelen; i++)
1979 hexdigit(node, i);
2020 hexdigit(node, i);
1980 if (PyErr_Occurred()) {
2021 if (PyErr_Occurred()) {
1981 /* input contains non-hex characters */
2022 /* input contains non-hex characters */
1982 PyErr_Clear();
2023 PyErr_Clear();
1983 Py_RETURN_NONE;
2024 Py_RETURN_NONE;
1984 }
2025 }
1985
2026
1986 if (index_init_nt(self) == -1)
2027 if (index_init_nt(self) == -1)
1987 return NULL;
2028 return NULL;
1988 if (index_populate_nt(self) == -1)
2029 if (index_populate_nt(self) == -1)
1989 return NULL;
2030 return NULL;
1990 rev = nt_partialmatch(&self->nt, node, nodelen);
2031 rev = nt_partialmatch(&self->nt, node, nodelen);
1991
2032
1992 switch (rev) {
2033 switch (rev) {
1993 case -4:
2034 case -4:
1994 raise_revlog_error();
2035 raise_revlog_error();
1995 return NULL;
2036 return NULL;
1996 case -2:
2037 case -2:
1997 Py_RETURN_NONE;
2038 Py_RETURN_NONE;
1998 case -1:
2039 case -1:
1999 return PyBytes_FromStringAndSize(nullid, 20);
2040 return PyBytes_FromStringAndSize(nullid, 20);
2000 }
2041 }
2001
2042
2002 fullnode = index_node_existing(self, rev);
2043 fullnode = index_node_existing(self, rev);
2003 if (fullnode == NULL) {
2044 if (fullnode == NULL) {
2004 return NULL;
2045 return NULL;
2005 }
2046 }
2006 return PyBytes_FromStringAndSize(fullnode, 20);
2047 return PyBytes_FromStringAndSize(fullnode, 20);
2007 }
2048 }
2008
2049
2009 static PyObject *index_shortest(indexObject *self, PyObject *args)
2050 static PyObject *index_shortest(indexObject *self, PyObject *args)
2010 {
2051 {
2011 PyObject *val;
2052 PyObject *val;
2012 char *node;
2053 char *node;
2013 int length;
2054 int length;
2014
2055
2015 if (!PyArg_ParseTuple(args, "O", &val))
2056 if (!PyArg_ParseTuple(args, "O", &val))
2016 return NULL;
2057 return NULL;
2017 if (node_check(val, &node) == -1)
2058 if (node_check(val, &node) == -1)
2018 return NULL;
2059 return NULL;
2019
2060
2020 self->ntlookups++;
2061 self->ntlookups++;
2021 if (index_init_nt(self) == -1)
2062 if (index_init_nt(self) == -1)
2022 return NULL;
2063 return NULL;
2023 if (index_populate_nt(self) == -1)
2064 if (index_populate_nt(self) == -1)
2024 return NULL;
2065 return NULL;
2025 length = nt_shortest(&self->nt, node);
2066 length = nt_shortest(&self->nt, node);
2026 if (length == -3)
2067 if (length == -3)
2027 return NULL;
2068 return NULL;
2028 if (length == -2) {
2069 if (length == -2) {
2029 raise_revlog_error();
2070 raise_revlog_error();
2030 return NULL;
2071 return NULL;
2031 }
2072 }
2032 return PyInt_FromLong(length);
2073 return PyInt_FromLong(length);
2033 }
2074 }
2034
2075
2035 static PyObject *index_m_get(indexObject *self, PyObject *args)
2076 static PyObject *index_m_get(indexObject *self, PyObject *args)
2036 {
2077 {
2037 PyObject *val;
2078 PyObject *val;
2038 char *node;
2079 char *node;
2039 int rev;
2080 int rev;
2040
2081
2041 if (!PyArg_ParseTuple(args, "O", &val))
2082 if (!PyArg_ParseTuple(args, "O", &val))
2042 return NULL;
2083 return NULL;
2043 if (node_check(val, &node) == -1)
2084 if (node_check(val, &node) == -1)
2044 return NULL;
2085 return NULL;
2045 rev = index_find_node(self, node, 20);
2086 rev = index_find_node(self, node, 20);
2046 if (rev == -3)
2087 if (rev == -3)
2047 return NULL;
2088 return NULL;
2048 if (rev == -2)
2089 if (rev == -2)
2049 Py_RETURN_NONE;
2090 Py_RETURN_NONE;
2050 return PyInt_FromLong(rev);
2091 return PyInt_FromLong(rev);
2051 }
2092 }
2052
2093
2053 static int index_contains(indexObject *self, PyObject *value)
2094 static int index_contains(indexObject *self, PyObject *value)
2054 {
2095 {
2055 char *node;
2096 char *node;
2056
2097
2057 if (PyInt_Check(value)) {
2098 if (PyInt_Check(value)) {
2058 long rev;
2099 long rev;
2059 if (!pylong_to_long(value, &rev)) {
2100 if (!pylong_to_long(value, &rev)) {
2060 return -1;
2101 return -1;
2061 }
2102 }
2062 return rev >= -1 && rev < index_length(self);
2103 return rev >= -1 && rev < index_length(self);
2063 }
2104 }
2064
2105
2065 if (node_check(value, &node) == -1)
2106 if (node_check(value, &node) == -1)
2066 return -1;
2107 return -1;
2067
2108
2068 switch (index_find_node(self, node, 20)) {
2109 switch (index_find_node(self, node, 20)) {
2069 case -3:
2110 case -3:
2070 return -1;
2111 return -1;
2071 case -2:
2112 case -2:
2072 return 0;
2113 return 0;
2073 default:
2114 default:
2074 return 1;
2115 return 1;
2075 }
2116 }
2076 }
2117 }
2077
2118
2078 static PyObject *index_m_has_node(indexObject *self, PyObject *args)
2119 static PyObject *index_m_has_node(indexObject *self, PyObject *args)
2079 {
2120 {
2080 int ret = index_contains(self, args);
2121 int ret = index_contains(self, args);
2081 if (ret < 0)
2122 if (ret < 0)
2082 return NULL;
2123 return NULL;
2083 return PyBool_FromLong((long)ret);
2124 return PyBool_FromLong((long)ret);
2084 }
2125 }
2085
2126
2086 static PyObject *index_m_rev(indexObject *self, PyObject *val)
2127 static PyObject *index_m_rev(indexObject *self, PyObject *val)
2087 {
2128 {
2088 char *node;
2129 char *node;
2089 int rev;
2130 int rev;
2090
2131
2091 if (node_check(val, &node) == -1)
2132 if (node_check(val, &node) == -1)
2092 return NULL;
2133 return NULL;
2093 rev = index_find_node(self, node, 20);
2134 rev = index_find_node(self, node, 20);
2094 if (rev >= -1)
2135 if (rev >= -1)
2095 return PyInt_FromLong(rev);
2136 return PyInt_FromLong(rev);
2096 if (rev == -2)
2137 if (rev == -2)
2097 raise_revlog_error();
2138 raise_revlog_error();
2098 return NULL;
2139 return NULL;
2099 }
2140 }
2100
2141
2101 typedef uint64_t bitmask;
2142 typedef uint64_t bitmask;
2102
2143
2103 /*
2144 /*
2104 * Given a disjoint set of revs, return all candidates for the
2145 * Given a disjoint set of revs, return all candidates for the
2105 * greatest common ancestor. In revset notation, this is the set
2146 * greatest common ancestor. In revset notation, this is the set
2106 * "heads(::a and ::b and ...)"
2147 * "heads(::a and ::b and ...)"
2107 */
2148 */
2108 static PyObject *find_gca_candidates(indexObject *self, const int *revs,
2149 static PyObject *find_gca_candidates(indexObject *self, const int *revs,
2109 int revcount)
2150 int revcount)
2110 {
2151 {
2111 const bitmask allseen = (1ull << revcount) - 1;
2152 const bitmask allseen = (1ull << revcount) - 1;
2112 const bitmask poison = 1ull << revcount;
2153 const bitmask poison = 1ull << revcount;
2113 PyObject *gca = PyList_New(0);
2154 PyObject *gca = PyList_New(0);
2114 int i, v, interesting;
2155 int i, v, interesting;
2115 int maxrev = -1;
2156 int maxrev = -1;
2116 bitmask sp;
2157 bitmask sp;
2117 bitmask *seen;
2158 bitmask *seen;
2118
2159
2119 if (gca == NULL)
2160 if (gca == NULL)
2120 return PyErr_NoMemory();
2161 return PyErr_NoMemory();
2121
2162
2122 for (i = 0; i < revcount; i++) {
2163 for (i = 0; i < revcount; i++) {
2123 if (revs[i] > maxrev)
2164 if (revs[i] > maxrev)
2124 maxrev = revs[i];
2165 maxrev = revs[i];
2125 }
2166 }
2126
2167
2127 seen = calloc(sizeof(*seen), maxrev + 1);
2168 seen = calloc(sizeof(*seen), maxrev + 1);
2128 if (seen == NULL) {
2169 if (seen == NULL) {
2129 Py_DECREF(gca);
2170 Py_DECREF(gca);
2130 return PyErr_NoMemory();
2171 return PyErr_NoMemory();
2131 }
2172 }
2132
2173
2133 for (i = 0; i < revcount; i++)
2174 for (i = 0; i < revcount; i++)
2134 seen[revs[i]] = 1ull << i;
2175 seen[revs[i]] = 1ull << i;
2135
2176
2136 interesting = revcount;
2177 interesting = revcount;
2137
2178
2138 for (v = maxrev; v >= 0 && interesting; v--) {
2179 for (v = maxrev; v >= 0 && interesting; v--) {
2139 bitmask sv = seen[v];
2180 bitmask sv = seen[v];
2140 int parents[2];
2181 int parents[2];
2141
2182
2142 if (!sv)
2183 if (!sv)
2143 continue;
2184 continue;
2144
2185
2145 if (sv < poison) {
2186 if (sv < poison) {
2146 interesting -= 1;
2187 interesting -= 1;
2147 if (sv == allseen) {
2188 if (sv == allseen) {
2148 PyObject *obj = PyInt_FromLong(v);
2189 PyObject *obj = PyInt_FromLong(v);
2149 if (obj == NULL)
2190 if (obj == NULL)
2150 goto bail;
2191 goto bail;
2151 if (PyList_Append(gca, obj) == -1) {
2192 if (PyList_Append(gca, obj) == -1) {
2152 Py_DECREF(obj);
2193 Py_DECREF(obj);
2153 goto bail;
2194 goto bail;
2154 }
2195 }
2155 sv |= poison;
2196 sv |= poison;
2156 for (i = 0; i < revcount; i++) {
2197 for (i = 0; i < revcount; i++) {
2157 if (revs[i] == v)
2198 if (revs[i] == v)
2158 goto done;
2199 goto done;
2159 }
2200 }
2160 }
2201 }
2161 }
2202 }
2162 if (index_get_parents(self, v, parents, maxrev) < 0)
2203 if (index_get_parents(self, v, parents, maxrev) < 0)
2163 goto bail;
2204 goto bail;
2164
2205
2165 for (i = 0; i < 2; i++) {
2206 for (i = 0; i < 2; i++) {
2166 int p = parents[i];
2207 int p = parents[i];
2167 if (p == -1)
2208 if (p == -1)
2168 continue;
2209 continue;
2169 sp = seen[p];
2210 sp = seen[p];
2170 if (sv < poison) {
2211 if (sv < poison) {
2171 if (sp == 0) {
2212 if (sp == 0) {
2172 seen[p] = sv;
2213 seen[p] = sv;
2173 interesting++;
2214 interesting++;
2174 } else if (sp != sv)
2215 } else if (sp != sv)
2175 seen[p] |= sv;
2216 seen[p] |= sv;
2176 } else {
2217 } else {
2177 if (sp && sp < poison)
2218 if (sp && sp < poison)
2178 interesting--;
2219 interesting--;
2179 seen[p] = sv;
2220 seen[p] = sv;
2180 }
2221 }
2181 }
2222 }
2182 }
2223 }
2183
2224
2184 done:
2225 done:
2185 free(seen);
2226 free(seen);
2186 return gca;
2227 return gca;
2187 bail:
2228 bail:
2188 free(seen);
2229 free(seen);
2189 Py_XDECREF(gca);
2230 Py_XDECREF(gca);
2190 return NULL;
2231 return NULL;
2191 }
2232 }
2192
2233
2193 /*
2234 /*
2194 * Given a disjoint set of revs, return the subset with the longest
2235 * Given a disjoint set of revs, return the subset with the longest
2195 * path to the root.
2236 * path to the root.
2196 */
2237 */
2197 static PyObject *find_deepest(indexObject *self, PyObject *revs)
2238 static PyObject *find_deepest(indexObject *self, PyObject *revs)
2198 {
2239 {
2199 const Py_ssize_t revcount = PyList_GET_SIZE(revs);
2240 const Py_ssize_t revcount = PyList_GET_SIZE(revs);
2200 static const Py_ssize_t capacity = 24;
2241 static const Py_ssize_t capacity = 24;
2201 int *depth, *interesting = NULL;
2242 int *depth, *interesting = NULL;
2202 int i, j, v, ninteresting;
2243 int i, j, v, ninteresting;
2203 PyObject *dict = NULL, *keys = NULL;
2244 PyObject *dict = NULL, *keys = NULL;
2204 long *seen = NULL;
2245 long *seen = NULL;
2205 int maxrev = -1;
2246 int maxrev = -1;
2206 long final;
2247 long final;
2207
2248
2208 if (revcount > capacity) {
2249 if (revcount > capacity) {
2209 PyErr_Format(PyExc_OverflowError,
2250 PyErr_Format(PyExc_OverflowError,
2210 "bitset size (%ld) > capacity (%ld)",
2251 "bitset size (%ld) > capacity (%ld)",
2211 (long)revcount, (long)capacity);
2252 (long)revcount, (long)capacity);
2212 return NULL;
2253 return NULL;
2213 }
2254 }
2214
2255
2215 for (i = 0; i < revcount; i++) {
2256 for (i = 0; i < revcount; i++) {
2216 int n = (int)PyInt_AsLong(PyList_GET_ITEM(revs, i));
2257 int n = (int)PyInt_AsLong(PyList_GET_ITEM(revs, i));
2217 if (n > maxrev)
2258 if (n > maxrev)
2218 maxrev = n;
2259 maxrev = n;
2219 }
2260 }
2220
2261
2221 depth = calloc(sizeof(*depth), maxrev + 1);
2262 depth = calloc(sizeof(*depth), maxrev + 1);
2222 if (depth == NULL)
2263 if (depth == NULL)
2223 return PyErr_NoMemory();
2264 return PyErr_NoMemory();
2224
2265
2225 seen = calloc(sizeof(*seen), maxrev + 1);
2266 seen = calloc(sizeof(*seen), maxrev + 1);
2226 if (seen == NULL) {
2267 if (seen == NULL) {
2227 PyErr_NoMemory();
2268 PyErr_NoMemory();
2228 goto bail;
2269 goto bail;
2229 }
2270 }
2230
2271
2231 interesting = calloc(sizeof(*interesting), ((size_t)1) << revcount);
2272 interesting = calloc(sizeof(*interesting), ((size_t)1) << revcount);
2232 if (interesting == NULL) {
2273 if (interesting == NULL) {
2233 PyErr_NoMemory();
2274 PyErr_NoMemory();
2234 goto bail;
2275 goto bail;
2235 }
2276 }
2236
2277
2237 if (PyList_Sort(revs) == -1)
2278 if (PyList_Sort(revs) == -1)
2238 goto bail;
2279 goto bail;
2239
2280
2240 for (i = 0; i < revcount; i++) {
2281 for (i = 0; i < revcount; i++) {
2241 int n = (int)PyInt_AsLong(PyList_GET_ITEM(revs, i));
2282 int n = (int)PyInt_AsLong(PyList_GET_ITEM(revs, i));
2242 long b = 1l << i;
2283 long b = 1l << i;
2243 depth[n] = 1;
2284 depth[n] = 1;
2244 seen[n] = b;
2285 seen[n] = b;
2245 interesting[b] = 1;
2286 interesting[b] = 1;
2246 }
2287 }
2247
2288
2248 /* invariant: ninteresting is the number of non-zero entries in
2289 /* invariant: ninteresting is the number of non-zero entries in
2249 * interesting. */
2290 * interesting. */
2250 ninteresting = (int)revcount;
2291 ninteresting = (int)revcount;
2251
2292
2252 for (v = maxrev; v >= 0 && ninteresting > 1; v--) {
2293 for (v = maxrev; v >= 0 && ninteresting > 1; v--) {
2253 int dv = depth[v];
2294 int dv = depth[v];
2254 int parents[2];
2295 int parents[2];
2255 long sv;
2296 long sv;
2256
2297
2257 if (dv == 0)
2298 if (dv == 0)
2258 continue;
2299 continue;
2259
2300
2260 sv = seen[v];
2301 sv = seen[v];
2261 if (index_get_parents(self, v, parents, maxrev) < 0)
2302 if (index_get_parents(self, v, parents, maxrev) < 0)
2262 goto bail;
2303 goto bail;
2263
2304
2264 for (i = 0; i < 2; i++) {
2305 for (i = 0; i < 2; i++) {
2265 int p = parents[i];
2306 int p = parents[i];
2266 long sp;
2307 long sp;
2267 int dp;
2308 int dp;
2268
2309
2269 if (p == -1)
2310 if (p == -1)
2270 continue;
2311 continue;
2271
2312
2272 dp = depth[p];
2313 dp = depth[p];
2273 sp = seen[p];
2314 sp = seen[p];
2274 if (dp <= dv) {
2315 if (dp <= dv) {
2275 depth[p] = dv + 1;
2316 depth[p] = dv + 1;
2276 if (sp != sv) {
2317 if (sp != sv) {
2277 interesting[sv] += 1;
2318 interesting[sv] += 1;
2278 seen[p] = sv;
2319 seen[p] = sv;
2279 if (sp) {
2320 if (sp) {
2280 interesting[sp] -= 1;
2321 interesting[sp] -= 1;
2281 if (interesting[sp] == 0)
2322 if (interesting[sp] == 0)
2282 ninteresting -= 1;
2323 ninteresting -= 1;
2283 }
2324 }
2284 }
2325 }
2285 } else if (dv == dp - 1) {
2326 } else if (dv == dp - 1) {
2286 long nsp = sp | sv;
2327 long nsp = sp | sv;
2287 if (nsp == sp)
2328 if (nsp == sp)
2288 continue;
2329 continue;
2289 seen[p] = nsp;
2330 seen[p] = nsp;
2290 interesting[sp] -= 1;
2331 interesting[sp] -= 1;
2291 if (interesting[sp] == 0)
2332 if (interesting[sp] == 0)
2292 ninteresting -= 1;
2333 ninteresting -= 1;
2293 if (interesting[nsp] == 0)
2334 if (interesting[nsp] == 0)
2294 ninteresting += 1;
2335 ninteresting += 1;
2295 interesting[nsp] += 1;
2336 interesting[nsp] += 1;
2296 }
2337 }
2297 }
2338 }
2298 interesting[sv] -= 1;
2339 interesting[sv] -= 1;
2299 if (interesting[sv] == 0)
2340 if (interesting[sv] == 0)
2300 ninteresting -= 1;
2341 ninteresting -= 1;
2301 }
2342 }
2302
2343
2303 final = 0;
2344 final = 0;
2304 j = ninteresting;
2345 j = ninteresting;
2305 for (i = 0; i < (int)(2 << revcount) && j > 0; i++) {
2346 for (i = 0; i < (int)(2 << revcount) && j > 0; i++) {
2306 if (interesting[i] == 0)
2347 if (interesting[i] == 0)
2307 continue;
2348 continue;
2308 final |= i;
2349 final |= i;
2309 j -= 1;
2350 j -= 1;
2310 }
2351 }
2311 if (final == 0) {
2352 if (final == 0) {
2312 keys = PyList_New(0);
2353 keys = PyList_New(0);
2313 goto bail;
2354 goto bail;
2314 }
2355 }
2315
2356
2316 dict = PyDict_New();
2357 dict = PyDict_New();
2317 if (dict == NULL)
2358 if (dict == NULL)
2318 goto bail;
2359 goto bail;
2319
2360
2320 for (i = 0; i < revcount; i++) {
2361 for (i = 0; i < revcount; i++) {
2321 PyObject *key;
2362 PyObject *key;
2322
2363
2323 if ((final & (1 << i)) == 0)
2364 if ((final & (1 << i)) == 0)
2324 continue;
2365 continue;
2325
2366
2326 key = PyList_GET_ITEM(revs, i);
2367 key = PyList_GET_ITEM(revs, i);
2327 Py_INCREF(key);
2368 Py_INCREF(key);
2328 Py_INCREF(Py_None);
2369 Py_INCREF(Py_None);
2329 if (PyDict_SetItem(dict, key, Py_None) == -1) {
2370 if (PyDict_SetItem(dict, key, Py_None) == -1) {
2330 Py_DECREF(key);
2371 Py_DECREF(key);
2331 Py_DECREF(Py_None);
2372 Py_DECREF(Py_None);
2332 goto bail;
2373 goto bail;
2333 }
2374 }
2334 }
2375 }
2335
2376
2336 keys = PyDict_Keys(dict);
2377 keys = PyDict_Keys(dict);
2337
2378
2338 bail:
2379 bail:
2339 free(depth);
2380 free(depth);
2340 free(seen);
2381 free(seen);
2341 free(interesting);
2382 free(interesting);
2342 Py_XDECREF(dict);
2383 Py_XDECREF(dict);
2343
2384
2344 return keys;
2385 return keys;
2345 }
2386 }
2346
2387
2347 /*
2388 /*
2348 * Given a (possibly overlapping) set of revs, return all the
2389 * Given a (possibly overlapping) set of revs, return all the
2349 * common ancestors heads: heads(::args[0] and ::a[1] and ...)
2390 * common ancestors heads: heads(::args[0] and ::a[1] and ...)
2350 */
2391 */
2351 static PyObject *index_commonancestorsheads(indexObject *self, PyObject *args)
2392 static PyObject *index_commonancestorsheads(indexObject *self, PyObject *args)
2352 {
2393 {
2353 PyObject *ret = NULL;
2394 PyObject *ret = NULL;
2354 Py_ssize_t argcount, i, len;
2395 Py_ssize_t argcount, i, len;
2355 bitmask repeat = 0;
2396 bitmask repeat = 0;
2356 int revcount = 0;
2397 int revcount = 0;
2357 int *revs;
2398 int *revs;
2358
2399
2359 argcount = PySequence_Length(args);
2400 argcount = PySequence_Length(args);
2360 revs = PyMem_Malloc(argcount * sizeof(*revs));
2401 revs = PyMem_Malloc(argcount * sizeof(*revs));
2361 if (argcount > 0 && revs == NULL)
2402 if (argcount > 0 && revs == NULL)
2362 return PyErr_NoMemory();
2403 return PyErr_NoMemory();
2363 len = index_length(self);
2404 len = index_length(self);
2364
2405
2365 for (i = 0; i < argcount; i++) {
2406 for (i = 0; i < argcount; i++) {
2366 static const int capacity = 24;
2407 static const int capacity = 24;
2367 PyObject *obj = PySequence_GetItem(args, i);
2408 PyObject *obj = PySequence_GetItem(args, i);
2368 bitmask x;
2409 bitmask x;
2369 long val;
2410 long val;
2370
2411
2371 if (!PyInt_Check(obj)) {
2412 if (!PyInt_Check(obj)) {
2372 PyErr_SetString(PyExc_TypeError,
2413 PyErr_SetString(PyExc_TypeError,
2373 "arguments must all be ints");
2414 "arguments must all be ints");
2374 Py_DECREF(obj);
2415 Py_DECREF(obj);
2375 goto bail;
2416 goto bail;
2376 }
2417 }
2377 val = PyInt_AsLong(obj);
2418 val = PyInt_AsLong(obj);
2378 Py_DECREF(obj);
2419 Py_DECREF(obj);
2379 if (val == -1) {
2420 if (val == -1) {
2380 ret = PyList_New(0);
2421 ret = PyList_New(0);
2381 goto done;
2422 goto done;
2382 }
2423 }
2383 if (val < 0 || val >= len) {
2424 if (val < 0 || val >= len) {
2384 PyErr_SetString(PyExc_IndexError, "index out of range");
2425 PyErr_SetString(PyExc_IndexError, "index out of range");
2385 goto bail;
2426 goto bail;
2386 }
2427 }
2387 /* this cheesy bloom filter lets us avoid some more
2428 /* this cheesy bloom filter lets us avoid some more
2388 * expensive duplicate checks in the common set-is-disjoint
2429 * expensive duplicate checks in the common set-is-disjoint
2389 * case */
2430 * case */
2390 x = 1ull << (val & 0x3f);
2431 x = 1ull << (val & 0x3f);
2391 if (repeat & x) {
2432 if (repeat & x) {
2392 int k;
2433 int k;
2393 for (k = 0; k < revcount; k++) {
2434 for (k = 0; k < revcount; k++) {
2394 if (val == revs[k])
2435 if (val == revs[k])
2395 goto duplicate;
2436 goto duplicate;
2396 }
2437 }
2397 } else
2438 } else
2398 repeat |= x;
2439 repeat |= x;
2399 if (revcount >= capacity) {
2440 if (revcount >= capacity) {
2400 PyErr_Format(PyExc_OverflowError,
2441 PyErr_Format(PyExc_OverflowError,
2401 "bitset size (%d) > capacity (%d)",
2442 "bitset size (%d) > capacity (%d)",
2402 revcount, capacity);
2443 revcount, capacity);
2403 goto bail;
2444 goto bail;
2404 }
2445 }
2405 revs[revcount++] = (int)val;
2446 revs[revcount++] = (int)val;
2406 duplicate:;
2447 duplicate:;
2407 }
2448 }
2408
2449
2409 if (revcount == 0) {
2450 if (revcount == 0) {
2410 ret = PyList_New(0);
2451 ret = PyList_New(0);
2411 goto done;
2452 goto done;
2412 }
2453 }
2413 if (revcount == 1) {
2454 if (revcount == 1) {
2414 PyObject *obj;
2455 PyObject *obj;
2415 ret = PyList_New(1);
2456 ret = PyList_New(1);
2416 if (ret == NULL)
2457 if (ret == NULL)
2417 goto bail;
2458 goto bail;
2418 obj = PyInt_FromLong(revs[0]);
2459 obj = PyInt_FromLong(revs[0]);
2419 if (obj == NULL)
2460 if (obj == NULL)
2420 goto bail;
2461 goto bail;
2421 PyList_SET_ITEM(ret, 0, obj);
2462 PyList_SET_ITEM(ret, 0, obj);
2422 goto done;
2463 goto done;
2423 }
2464 }
2424
2465
2425 ret = find_gca_candidates(self, revs, revcount);
2466 ret = find_gca_candidates(self, revs, revcount);
2426 if (ret == NULL)
2467 if (ret == NULL)
2427 goto bail;
2468 goto bail;
2428
2469
2429 done:
2470 done:
2430 PyMem_Free(revs);
2471 PyMem_Free(revs);
2431 return ret;
2472 return ret;
2432
2473
2433 bail:
2474 bail:
2434 PyMem_Free(revs);
2475 PyMem_Free(revs);
2435 Py_XDECREF(ret);
2476 Py_XDECREF(ret);
2436 return NULL;
2477 return NULL;
2437 }
2478 }
2438
2479
2439 /*
2480 /*
2440 * Given a (possibly overlapping) set of revs, return the greatest
2481 * Given a (possibly overlapping) set of revs, return the greatest
2441 * common ancestors: those with the longest path to the root.
2482 * common ancestors: those with the longest path to the root.
2442 */
2483 */
2443 static PyObject *index_ancestors(indexObject *self, PyObject *args)
2484 static PyObject *index_ancestors(indexObject *self, PyObject *args)
2444 {
2485 {
2445 PyObject *ret;
2486 PyObject *ret;
2446 PyObject *gca = index_commonancestorsheads(self, args);
2487 PyObject *gca = index_commonancestorsheads(self, args);
2447 if (gca == NULL)
2488 if (gca == NULL)
2448 return NULL;
2489 return NULL;
2449
2490
2450 if (PyList_GET_SIZE(gca) <= 1) {
2491 if (PyList_GET_SIZE(gca) <= 1) {
2451 return gca;
2492 return gca;
2452 }
2493 }
2453
2494
2454 ret = find_deepest(self, gca);
2495 ret = find_deepest(self, gca);
2455 Py_DECREF(gca);
2496 Py_DECREF(gca);
2456 return ret;
2497 return ret;
2457 }
2498 }
2458
2499
2459 /*
2500 /*
2460 * Invalidate any trie entries introduced by added revs.
2501 * Invalidate any trie entries introduced by added revs.
2461 */
2502 */
2462 static void index_invalidate_added(indexObject *self, Py_ssize_t start)
2503 static void index_invalidate_added(indexObject *self, Py_ssize_t start)
2463 {
2504 {
2464 Py_ssize_t i, len = PyList_GET_SIZE(self->added);
2505 Py_ssize_t i, len = PyList_GET_SIZE(self->added);
2465
2506
2466 for (i = start; i < len; i++) {
2507 for (i = start; i < len; i++) {
2467 PyObject *tuple = PyList_GET_ITEM(self->added, i);
2508 PyObject *tuple = PyList_GET_ITEM(self->added, i);
2468 PyObject *node = PyTuple_GET_ITEM(tuple, 7);
2509 PyObject *node = PyTuple_GET_ITEM(tuple, 7);
2469
2510
2470 nt_delete_node(&self->nt, PyBytes_AS_STRING(node));
2511 nt_delete_node(&self->nt, PyBytes_AS_STRING(node));
2471 }
2512 }
2472
2513
2473 if (start == 0)
2514 if (start == 0)
2474 Py_CLEAR(self->added);
2515 Py_CLEAR(self->added);
2475 }
2516 }
2476
2517
2477 /*
2518 /*
2478 * Delete a numeric range of revs, which must be at the end of the
2519 * Delete a numeric range of revs, which must be at the end of the
2479 * range.
2520 * range.
2480 */
2521 */
2481 static int index_slice_del(indexObject *self, PyObject *item)
2522 static int index_slice_del(indexObject *self, PyObject *item)
2482 {
2523 {
2483 Py_ssize_t start, stop, step, slicelength;
2524 Py_ssize_t start, stop, step, slicelength;
2484 Py_ssize_t length = index_length(self) + 1;
2525 Py_ssize_t length = index_length(self) + 1;
2485 int ret = 0;
2526 int ret = 0;
2486
2527
2487 /* Argument changed from PySliceObject* to PyObject* in Python 3. */
2528 /* Argument changed from PySliceObject* to PyObject* in Python 3. */
2488 #ifdef IS_PY3K
2529 #ifdef IS_PY3K
2489 if (PySlice_GetIndicesEx(item, length, &start, &stop, &step,
2530 if (PySlice_GetIndicesEx(item, length, &start, &stop, &step,
2490 &slicelength) < 0)
2531 &slicelength) < 0)
2491 #else
2532 #else
2492 if (PySlice_GetIndicesEx((PySliceObject *)item, length, &start, &stop,
2533 if (PySlice_GetIndicesEx((PySliceObject *)item, length, &start, &stop,
2493 &step, &slicelength) < 0)
2534 &step, &slicelength) < 0)
2494 #endif
2535 #endif
2495 return -1;
2536 return -1;
2496
2537
2497 if (slicelength <= 0)
2538 if (slicelength <= 0)
2498 return 0;
2539 return 0;
2499
2540
2500 if ((step < 0 && start < stop) || (step > 0 && start > stop))
2541 if ((step < 0 && start < stop) || (step > 0 && start > stop))
2501 stop = start;
2542 stop = start;
2502
2543
2503 if (step < 0) {
2544 if (step < 0) {
2504 stop = start + 1;
2545 stop = start + 1;
2505 start = stop + step * (slicelength - 1) - 1;
2546 start = stop + step * (slicelength - 1) - 1;
2506 step = -step;
2547 step = -step;
2507 }
2548 }
2508
2549
2509 if (step != 1) {
2550 if (step != 1) {
2510 PyErr_SetString(PyExc_ValueError,
2551 PyErr_SetString(PyExc_ValueError,
2511 "revlog index delete requires step size of 1");
2552 "revlog index delete requires step size of 1");
2512 return -1;
2553 return -1;
2513 }
2554 }
2514
2555
2515 if (stop != length - 1) {
2556 if (stop != length - 1) {
2516 PyErr_SetString(PyExc_IndexError,
2557 PyErr_SetString(PyExc_IndexError,
2517 "revlog index deletion indices are invalid");
2558 "revlog index deletion indices are invalid");
2518 return -1;
2559 return -1;
2519 }
2560 }
2520
2561
2521 if (start < self->length) {
2562 if (start < self->length) {
2522 if (self->ntinitialized) {
2563 if (self->ntinitialized) {
2523 Py_ssize_t i;
2564 Py_ssize_t i;
2524
2565
2525 for (i = start; i < self->length; i++) {
2566 for (i = start; i < self->length; i++) {
2526 const char *node = index_node_existing(self, i);
2567 const char *node = index_node_existing(self, i);
2527 if (node == NULL)
2568 if (node == NULL)
2528 return -1;
2569 return -1;
2529
2570
2530 nt_delete_node(&self->nt, node);
2571 nt_delete_node(&self->nt, node);
2531 }
2572 }
2532 if (self->added)
2573 if (self->added)
2533 index_invalidate_added(self, 0);
2574 index_invalidate_added(self, 0);
2534 if (self->ntrev > start)
2575 if (self->ntrev > start)
2535 self->ntrev = (int)start;
2576 self->ntrev = (int)start;
2536 } else if (self->added) {
2577 } else if (self->added) {
2537 Py_CLEAR(self->added);
2578 Py_CLEAR(self->added);
2538 }
2579 }
2539
2580
2540 self->length = start;
2581 self->length = start;
2541 if (start < self->raw_length) {
2582 if (start < self->raw_length) {
2542 if (self->cache) {
2583 if (self->cache) {
2543 Py_ssize_t i;
2584 Py_ssize_t i;
2544 for (i = start; i < self->raw_length; i++)
2585 for (i = start; i < self->raw_length; i++)
2545 Py_CLEAR(self->cache[i]);
2586 Py_CLEAR(self->cache[i]);
2546 }
2587 }
2547 self->raw_length = start;
2588 self->raw_length = start;
2548 }
2589 }
2549 goto done;
2590 goto done;
2550 }
2591 }
2551
2592
2552 if (self->ntinitialized) {
2593 if (self->ntinitialized) {
2553 index_invalidate_added(self, start - self->length);
2594 index_invalidate_added(self, start - self->length);
2554 if (self->ntrev > start)
2595 if (self->ntrev > start)
2555 self->ntrev = (int)start;
2596 self->ntrev = (int)start;
2556 }
2597 }
2557 if (self->added)
2598 if (self->added)
2558 ret = PyList_SetSlice(self->added, start - self->length,
2599 ret = PyList_SetSlice(self->added, start - self->length,
2559 PyList_GET_SIZE(self->added), NULL);
2600 PyList_GET_SIZE(self->added), NULL);
2560 done:
2601 done:
2561 Py_CLEAR(self->headrevs);
2602 Py_CLEAR(self->headrevs);
2562 return ret;
2603 return ret;
2563 }
2604 }
2564
2605
2565 /*
2606 /*
2566 * Supported ops:
2607 * Supported ops:
2567 *
2608 *
2568 * slice deletion
2609 * slice deletion
2569 * string assignment (extend node->rev mapping)
2610 * string assignment (extend node->rev mapping)
2570 * string deletion (shrink node->rev mapping)
2611 * string deletion (shrink node->rev mapping)
2571 */
2612 */
2572 static int index_assign_subscript(indexObject *self, PyObject *item,
2613 static int index_assign_subscript(indexObject *self, PyObject *item,
2573 PyObject *value)
2614 PyObject *value)
2574 {
2615 {
2575 char *node;
2616 char *node;
2576 long rev;
2617 long rev;
2577
2618
2578 if (PySlice_Check(item) && value == NULL)
2619 if (PySlice_Check(item) && value == NULL)
2579 return index_slice_del(self, item);
2620 return index_slice_del(self, item);
2580
2621
2581 if (node_check(item, &node) == -1)
2622 if (node_check(item, &node) == -1)
2582 return -1;
2623 return -1;
2583
2624
2584 if (value == NULL)
2625 if (value == NULL)
2585 return self->ntinitialized ? nt_delete_node(&self->nt, node)
2626 return self->ntinitialized ? nt_delete_node(&self->nt, node)
2586 : 0;
2627 : 0;
2587 rev = PyInt_AsLong(value);
2628 rev = PyInt_AsLong(value);
2588 if (rev > INT_MAX || rev < 0) {
2629 if (rev > INT_MAX || rev < 0) {
2589 if (!PyErr_Occurred())
2630 if (!PyErr_Occurred())
2590 PyErr_SetString(PyExc_ValueError, "rev out of range");
2631 PyErr_SetString(PyExc_ValueError, "rev out of range");
2591 return -1;
2632 return -1;
2592 }
2633 }
2593
2634
2594 if (index_init_nt(self) == -1)
2635 if (index_init_nt(self) == -1)
2595 return -1;
2636 return -1;
2596 return nt_insert(&self->nt, node, (int)rev);
2637 return nt_insert(&self->nt, node, (int)rev);
2597 }
2638 }
2598
2639
2599 /*
2640 /*
2600 * Find all RevlogNG entries in an index that has inline data. Update
2641 * Find all RevlogNG entries in an index that has inline data. Update
2601 * the optional "offsets" table with those entries.
2642 * the optional "offsets" table with those entries.
2602 */
2643 */
2603 static Py_ssize_t inline_scan(indexObject *self, const char **offsets)
2644 static Py_ssize_t inline_scan(indexObject *self, const char **offsets)
2604 {
2645 {
2605 const char *data = (const char *)self->buf.buf;
2646 const char *data = (const char *)self->buf.buf;
2606 Py_ssize_t pos = 0;
2647 Py_ssize_t pos = 0;
2607 Py_ssize_t end = self->buf.len;
2648 Py_ssize_t end = self->buf.len;
2608 long incr = v1_hdrsize;
2649 long incr = v1_hdrsize;
2609 Py_ssize_t len = 0;
2650 Py_ssize_t len = 0;
2610
2651
2611 while (pos + v1_hdrsize <= end && pos >= 0) {
2652 while (pos + v1_hdrsize <= end && pos >= 0) {
2612 uint32_t comp_len;
2653 uint32_t comp_len;
2613 /* 3rd element of header is length of compressed inline data */
2654 /* 3rd element of header is length of compressed inline data */
2614 comp_len = getbe32(data + pos + 8);
2655 comp_len = getbe32(data + pos + 8);
2615 incr = v1_hdrsize + comp_len;
2656 incr = v1_hdrsize + comp_len;
2616 if (offsets)
2657 if (offsets)
2617 offsets[len] = data + pos;
2658 offsets[len] = data + pos;
2618 len++;
2659 len++;
2619 pos += incr;
2660 pos += incr;
2620 }
2661 }
2621
2662
2622 if (pos != end) {
2663 if (pos != end) {
2623 if (!PyErr_Occurred())
2664 if (!PyErr_Occurred())
2624 PyErr_SetString(PyExc_ValueError, "corrupt index file");
2665 PyErr_SetString(PyExc_ValueError, "corrupt index file");
2625 return -1;
2666 return -1;
2626 }
2667 }
2627
2668
2628 return len;
2669 return len;
2629 }
2670 }
2630
2671
2631 static int index_init(indexObject *self, PyObject *args)
2672 static int index_init(indexObject *self, PyObject *args)
2632 {
2673 {
2633 PyObject *data_obj, *inlined_obj;
2674 PyObject *data_obj, *inlined_obj;
2634 Py_ssize_t size;
2675 Py_ssize_t size;
2635
2676
2636 /* Initialize before argument-checking to avoid index_dealloc() crash.
2677 /* Initialize before argument-checking to avoid index_dealloc() crash.
2637 */
2678 */
2638 self->raw_length = 0;
2679 self->raw_length = 0;
2639 self->added = NULL;
2680 self->added = NULL;
2640 self->cache = NULL;
2681 self->cache = NULL;
2641 self->data = NULL;
2682 self->data = NULL;
2642 memset(&self->buf, 0, sizeof(self->buf));
2683 memset(&self->buf, 0, sizeof(self->buf));
2643 self->headrevs = NULL;
2684 self->headrevs = NULL;
2644 self->filteredrevs = Py_None;
2685 self->filteredrevs = Py_None;
2645 Py_INCREF(Py_None);
2686 Py_INCREF(Py_None);
2646 self->ntinitialized = 0;
2687 self->ntinitialized = 0;
2647 self->offsets = NULL;
2688 self->offsets = NULL;
2648
2689
2649 if (!PyArg_ParseTuple(args, "OO", &data_obj, &inlined_obj))
2690 if (!PyArg_ParseTuple(args, "OO", &data_obj, &inlined_obj))
2650 return -1;
2691 return -1;
2651 if (!PyObject_CheckBuffer(data_obj)) {
2692 if (!PyObject_CheckBuffer(data_obj)) {
2652 PyErr_SetString(PyExc_TypeError,
2693 PyErr_SetString(PyExc_TypeError,
2653 "data does not support buffer interface");
2694 "data does not support buffer interface");
2654 return -1;
2695 return -1;
2655 }
2696 }
2656
2697
2657 if (PyObject_GetBuffer(data_obj, &self->buf, PyBUF_SIMPLE) == -1)
2698 if (PyObject_GetBuffer(data_obj, &self->buf, PyBUF_SIMPLE) == -1)
2658 return -1;
2699 return -1;
2659 size = self->buf.len;
2700 size = self->buf.len;
2660
2701
2661 self->inlined = inlined_obj && PyObject_IsTrue(inlined_obj);
2702 self->inlined = inlined_obj && PyObject_IsTrue(inlined_obj);
2662 self->data = data_obj;
2703 self->data = data_obj;
2663
2704
2664 self->ntlookups = self->ntmisses = 0;
2705 self->ntlookups = self->ntmisses = 0;
2665 self->ntrev = -1;
2706 self->ntrev = -1;
2666 Py_INCREF(self->data);
2707 Py_INCREF(self->data);
2667
2708
2668 if (self->inlined) {
2709 if (self->inlined) {
2669 Py_ssize_t len = inline_scan(self, NULL);
2710 Py_ssize_t len = inline_scan(self, NULL);
2670 if (len == -1)
2711 if (len == -1)
2671 goto bail;
2712 goto bail;
2672 self->raw_length = len;
2713 self->raw_length = len;
2673 self->length = len;
2714 self->length = len;
2674 } else {
2715 } else {
2675 if (size % v1_hdrsize) {
2716 if (size % v1_hdrsize) {
2676 PyErr_SetString(PyExc_ValueError, "corrupt index file");
2717 PyErr_SetString(PyExc_ValueError, "corrupt index file");
2677 goto bail;
2718 goto bail;
2678 }
2719 }
2679 self->raw_length = size / v1_hdrsize;
2720 self->raw_length = size / v1_hdrsize;
2680 self->length = self->raw_length;
2721 self->length = self->raw_length;
2681 }
2722 }
2682
2723
2683 return 0;
2724 return 0;
2684 bail:
2725 bail:
2685 return -1;
2726 return -1;
2686 }
2727 }
2687
2728
2688 static PyObject *index_nodemap(indexObject *self)
2729 static PyObject *index_nodemap(indexObject *self)
2689 {
2730 {
2690 Py_INCREF(self);
2731 Py_INCREF(self);
2691 return (PyObject *)self;
2732 return (PyObject *)self;
2692 }
2733 }
2693
2734
2694 static void _index_clearcaches(indexObject *self)
2735 static void _index_clearcaches(indexObject *self)
2695 {
2736 {
2696 if (self->cache) {
2737 if (self->cache) {
2697 Py_ssize_t i;
2738 Py_ssize_t i;
2698
2739
2699 for (i = 0; i < self->raw_length; i++)
2740 for (i = 0; i < self->raw_length; i++)
2700 Py_CLEAR(self->cache[i]);
2741 Py_CLEAR(self->cache[i]);
2701 free(self->cache);
2742 free(self->cache);
2702 self->cache = NULL;
2743 self->cache = NULL;
2703 }
2744 }
2704 if (self->offsets) {
2745 if (self->offsets) {
2705 PyMem_Free((void *)self->offsets);
2746 PyMem_Free((void *)self->offsets);
2706 self->offsets = NULL;
2747 self->offsets = NULL;
2707 }
2748 }
2708 if (self->ntinitialized) {
2749 if (self->ntinitialized) {
2709 nt_dealloc(&self->nt);
2750 nt_dealloc(&self->nt);
2710 }
2751 }
2711 self->ntinitialized = 0;
2752 self->ntinitialized = 0;
2712 Py_CLEAR(self->headrevs);
2753 Py_CLEAR(self->headrevs);
2713 }
2754 }
2714
2755
2715 static PyObject *index_clearcaches(indexObject *self)
2756 static PyObject *index_clearcaches(indexObject *self)
2716 {
2757 {
2717 _index_clearcaches(self);
2758 _index_clearcaches(self);
2718 self->ntrev = -1;
2759 self->ntrev = -1;
2719 self->ntlookups = self->ntmisses = 0;
2760 self->ntlookups = self->ntmisses = 0;
2720 Py_RETURN_NONE;
2761 Py_RETURN_NONE;
2721 }
2762 }
2722
2763
2723 static void index_dealloc(indexObject *self)
2764 static void index_dealloc(indexObject *self)
2724 {
2765 {
2725 _index_clearcaches(self);
2766 _index_clearcaches(self);
2726 Py_XDECREF(self->filteredrevs);
2767 Py_XDECREF(self->filteredrevs);
2727 if (self->buf.buf) {
2768 if (self->buf.buf) {
2728 PyBuffer_Release(&self->buf);
2769 PyBuffer_Release(&self->buf);
2729 memset(&self->buf, 0, sizeof(self->buf));
2770 memset(&self->buf, 0, sizeof(self->buf));
2730 }
2771 }
2731 Py_XDECREF(self->data);
2772 Py_XDECREF(self->data);
2732 Py_XDECREF(self->added);
2773 Py_XDECREF(self->added);
2733 PyObject_Del(self);
2774 PyObject_Del(self);
2734 }
2775 }
2735
2776
2736 static PySequenceMethods index_sequence_methods = {
2777 static PySequenceMethods index_sequence_methods = {
2737 (lenfunc)index_length, /* sq_length */
2778 (lenfunc)index_length, /* sq_length */
2738 0, /* sq_concat */
2779 0, /* sq_concat */
2739 0, /* sq_repeat */
2780 0, /* sq_repeat */
2740 (ssizeargfunc)index_get, /* sq_item */
2781 (ssizeargfunc)index_get, /* sq_item */
2741 0, /* sq_slice */
2782 0, /* sq_slice */
2742 0, /* sq_ass_item */
2783 0, /* sq_ass_item */
2743 0, /* sq_ass_slice */
2784 0, /* sq_ass_slice */
2744 (objobjproc)index_contains, /* sq_contains */
2785 (objobjproc)index_contains, /* sq_contains */
2745 };
2786 };
2746
2787
2747 static PyMappingMethods index_mapping_methods = {
2788 static PyMappingMethods index_mapping_methods = {
2748 (lenfunc)index_length, /* mp_length */
2789 (lenfunc)index_length, /* mp_length */
2749 (binaryfunc)index_getitem, /* mp_subscript */
2790 (binaryfunc)index_getitem, /* mp_subscript */
2750 (objobjargproc)index_assign_subscript, /* mp_ass_subscript */
2791 (objobjargproc)index_assign_subscript, /* mp_ass_subscript */
2751 };
2792 };
2752
2793
2753 static PyMethodDef index_methods[] = {
2794 static PyMethodDef index_methods[] = {
2754 {"ancestors", (PyCFunction)index_ancestors, METH_VARARGS,
2795 {"ancestors", (PyCFunction)index_ancestors, METH_VARARGS,
2755 "return the gca set of the given revs"},
2796 "return the gca set of the given revs"},
2756 {"commonancestorsheads", (PyCFunction)index_commonancestorsheads,
2797 {"commonancestorsheads", (PyCFunction)index_commonancestorsheads,
2757 METH_VARARGS,
2798 METH_VARARGS,
2758 "return the heads of the common ancestors of the given revs"},
2799 "return the heads of the common ancestors of the given revs"},
2759 {"clearcaches", (PyCFunction)index_clearcaches, METH_NOARGS,
2800 {"clearcaches", (PyCFunction)index_clearcaches, METH_NOARGS,
2760 "clear the index caches"},
2801 "clear the index caches"},
2761 {"get", (PyCFunction)index_m_get, METH_VARARGS, "get an index entry"},
2802 {"get", (PyCFunction)index_m_get, METH_VARARGS, "get an index entry"},
2762 {"get_rev", (PyCFunction)index_m_get, METH_VARARGS,
2803 {"get_rev", (PyCFunction)index_m_get, METH_VARARGS,
2763 "return `rev` associated with a node or None"},
2804 "return `rev` associated with a node or None"},
2764 {"has_node", (PyCFunction)index_m_has_node, METH_O,
2805 {"has_node", (PyCFunction)index_m_has_node, METH_O,
2765 "return True if the node exist in the index"},
2806 "return True if the node exist in the index"},
2766 {"rev", (PyCFunction)index_m_rev, METH_O,
2807 {"rev", (PyCFunction)index_m_rev, METH_O,
2767 "return `rev` associated with a node or raise RevlogError"},
2808 "return `rev` associated with a node or raise RevlogError"},
2768 {"computephasesmapsets", (PyCFunction)compute_phases_map_sets, METH_VARARGS,
2809 {"computephasesmapsets", (PyCFunction)compute_phases_map_sets, METH_VARARGS,
2769 "compute phases"},
2810 "compute phases"},
2770 {"reachableroots2", (PyCFunction)reachableroots2, METH_VARARGS,
2811 {"reachableroots2", (PyCFunction)reachableroots2, METH_VARARGS,
2771 "reachableroots"},
2812 "reachableroots"},
2772 {"headrevs", (PyCFunction)index_headrevs, METH_VARARGS,
2813 {"headrevs", (PyCFunction)index_headrevs, METH_VARARGS,
2773 "get head revisions"}, /* Can do filtering since 3.2 */
2814 "get head revisions"}, /* Can do filtering since 3.2 */
2774 {"headrevsfiltered", (PyCFunction)index_headrevs, METH_VARARGS,
2815 {"headrevsfiltered", (PyCFunction)index_headrevs, METH_VARARGS,
2775 "get filtered head revisions"}, /* Can always do filtering */
2816 "get filtered head revisions"}, /* Can always do filtering */
2776 {"issnapshot", (PyCFunction)index_issnapshot, METH_O,
2817 {"issnapshot", (PyCFunction)index_issnapshot, METH_O,
2777 "True if the object is a snapshot"},
2818 "True if the object is a snapshot"},
2778 {"findsnapshots", (PyCFunction)index_findsnapshots, METH_VARARGS,
2819 {"findsnapshots", (PyCFunction)index_findsnapshots, METH_VARARGS,
2779 "Gather snapshot data in a cache dict"},
2820 "Gather snapshot data in a cache dict"},
2780 {"deltachain", (PyCFunction)index_deltachain, METH_VARARGS,
2821 {"deltachain", (PyCFunction)index_deltachain, METH_VARARGS,
2781 "determine revisions with deltas to reconstruct fulltext"},
2822 "determine revisions with deltas to reconstruct fulltext"},
2782 {"slicechunktodensity", (PyCFunction)index_slicechunktodensity,
2823 {"slicechunktodensity", (PyCFunction)index_slicechunktodensity,
2783 METH_VARARGS, "determine revisions with deltas to reconstruct fulltext"},
2824 METH_VARARGS, "determine revisions with deltas to reconstruct fulltext"},
2784 {"append", (PyCFunction)index_append, METH_O, "append an index entry"},
2825 {"append", (PyCFunction)index_append, METH_O, "append an index entry"},
2785 {"partialmatch", (PyCFunction)index_partialmatch, METH_VARARGS,
2826 {"partialmatch", (PyCFunction)index_partialmatch, METH_VARARGS,
2786 "match a potentially ambiguous node ID"},
2827 "match a potentially ambiguous node ID"},
2787 {"shortest", (PyCFunction)index_shortest, METH_VARARGS,
2828 {"shortest", (PyCFunction)index_shortest, METH_VARARGS,
2788 "find length of shortest hex nodeid of a binary ID"},
2829 "find length of shortest hex nodeid of a binary ID"},
2789 {"stats", (PyCFunction)index_stats, METH_NOARGS, "stats for the index"},
2830 {"stats", (PyCFunction)index_stats, METH_NOARGS, "stats for the index"},
2790 {NULL} /* Sentinel */
2831 {NULL} /* Sentinel */
2791 };
2832 };
2792
2833
2793 static PyGetSetDef index_getset[] = {
2834 static PyGetSetDef index_getset[] = {
2794 {"nodemap", (getter)index_nodemap, NULL, "nodemap", NULL},
2835 {"nodemap", (getter)index_nodemap, NULL, "nodemap", NULL},
2795 {NULL} /* Sentinel */
2836 {NULL} /* Sentinel */
2796 };
2837 };
2797
2838
2798 PyTypeObject HgRevlogIndex_Type = {
2839 PyTypeObject HgRevlogIndex_Type = {
2799 PyVarObject_HEAD_INIT(NULL, 0) /* header */
2840 PyVarObject_HEAD_INIT(NULL, 0) /* header */
2800 "parsers.index", /* tp_name */
2841 "parsers.index", /* tp_name */
2801 sizeof(indexObject), /* tp_basicsize */
2842 sizeof(indexObject), /* tp_basicsize */
2802 0, /* tp_itemsize */
2843 0, /* tp_itemsize */
2803 (destructor)index_dealloc, /* tp_dealloc */
2844 (destructor)index_dealloc, /* tp_dealloc */
2804 0, /* tp_print */
2845 0, /* tp_print */
2805 0, /* tp_getattr */
2846 0, /* tp_getattr */
2806 0, /* tp_setattr */
2847 0, /* tp_setattr */
2807 0, /* tp_compare */
2848 0, /* tp_compare */
2808 0, /* tp_repr */
2849 0, /* tp_repr */
2809 0, /* tp_as_number */
2850 0, /* tp_as_number */
2810 &index_sequence_methods, /* tp_as_sequence */
2851 &index_sequence_methods, /* tp_as_sequence */
2811 &index_mapping_methods, /* tp_as_mapping */
2852 &index_mapping_methods, /* tp_as_mapping */
2812 0, /* tp_hash */
2853 0, /* tp_hash */
2813 0, /* tp_call */
2854 0, /* tp_call */
2814 0, /* tp_str */
2855 0, /* tp_str */
2815 0, /* tp_getattro */
2856 0, /* tp_getattro */
2816 0, /* tp_setattro */
2857 0, /* tp_setattro */
2817 0, /* tp_as_buffer */
2858 0, /* tp_as_buffer */
2818 Py_TPFLAGS_DEFAULT, /* tp_flags */
2859 Py_TPFLAGS_DEFAULT, /* tp_flags */
2819 "revlog index", /* tp_doc */
2860 "revlog index", /* tp_doc */
2820 0, /* tp_traverse */
2861 0, /* tp_traverse */
2821 0, /* tp_clear */
2862 0, /* tp_clear */
2822 0, /* tp_richcompare */
2863 0, /* tp_richcompare */
2823 0, /* tp_weaklistoffset */
2864 0, /* tp_weaklistoffset */
2824 0, /* tp_iter */
2865 0, /* tp_iter */
2825 0, /* tp_iternext */
2866 0, /* tp_iternext */
2826 index_methods, /* tp_methods */
2867 index_methods, /* tp_methods */
2827 0, /* tp_members */
2868 0, /* tp_members */
2828 index_getset, /* tp_getset */
2869 index_getset, /* tp_getset */
2829 0, /* tp_base */
2870 0, /* tp_base */
2830 0, /* tp_dict */
2871 0, /* tp_dict */
2831 0, /* tp_descr_get */
2872 0, /* tp_descr_get */
2832 0, /* tp_descr_set */
2873 0, /* tp_descr_set */
2833 0, /* tp_dictoffset */
2874 0, /* tp_dictoffset */
2834 (initproc)index_init, /* tp_init */
2875 (initproc)index_init, /* tp_init */
2835 0, /* tp_alloc */
2876 0, /* tp_alloc */
2836 };
2877 };
2837
2878
2838 /*
2879 /*
2839 * returns a tuple of the form (index, index, cache) with elements as
2880 * returns a tuple of the form (index, index, cache) with elements as
2840 * follows:
2881 * follows:
2841 *
2882 *
2842 * index: an index object that lazily parses RevlogNG records
2883 * index: an index object that lazily parses RevlogNG records
2843 * cache: if data is inlined, a tuple (0, index_file_content), else None
2884 * cache: if data is inlined, a tuple (0, index_file_content), else None
2844 * index_file_content could be a string, or a buffer
2885 * index_file_content could be a string, or a buffer
2845 *
2886 *
2846 * added complications are for backwards compatibility
2887 * added complications are for backwards compatibility
2847 */
2888 */
2848 PyObject *parse_index2(PyObject *self, PyObject *args)
2889 PyObject *parse_index2(PyObject *self, PyObject *args)
2849 {
2890 {
2850 PyObject *tuple = NULL, *cache = NULL;
2891 PyObject *tuple = NULL, *cache = NULL;
2851 indexObject *idx;
2892 indexObject *idx;
2852 int ret;
2893 int ret;
2853
2894
2854 idx = PyObject_New(indexObject, &HgRevlogIndex_Type);
2895 idx = PyObject_New(indexObject, &HgRevlogIndex_Type);
2855 if (idx == NULL)
2896 if (idx == NULL)
2856 goto bail;
2897 goto bail;
2857
2898
2858 ret = index_init(idx, args);
2899 ret = index_init(idx, args);
2859 if (ret == -1)
2900 if (ret == -1)
2860 goto bail;
2901 goto bail;
2861
2902
2862 if (idx->inlined) {
2903 if (idx->inlined) {
2863 cache = Py_BuildValue("iO", 0, idx->data);
2904 cache = Py_BuildValue("iO", 0, idx->data);
2864 if (cache == NULL)
2905 if (cache == NULL)
2865 goto bail;
2906 goto bail;
2866 } else {
2907 } else {
2867 cache = Py_None;
2908 cache = Py_None;
2868 Py_INCREF(cache);
2909 Py_INCREF(cache);
2869 }
2910 }
2870
2911
2871 tuple = Py_BuildValue("NN", idx, cache);
2912 tuple = Py_BuildValue("NN", idx, cache);
2872 if (!tuple)
2913 if (!tuple)
2873 goto bail;
2914 goto bail;
2874 return tuple;
2915 return tuple;
2875
2916
2876 bail:
2917 bail:
2877 Py_XDECREF(idx);
2918 Py_XDECREF(idx);
2878 Py_XDECREF(cache);
2919 Py_XDECREF(cache);
2879 Py_XDECREF(tuple);
2920 Py_XDECREF(tuple);
2880 return NULL;
2921 return NULL;
2881 }
2922 }
2882
2923
2883 static Revlog_CAPI CAPI = {
2924 static Revlog_CAPI CAPI = {
2884 /* increment the abi_version field upon each change in the Revlog_CAPI
2925 /* increment the abi_version field upon each change in the Revlog_CAPI
2885 struct or in the ABI of the listed functions */
2926 struct or in the ABI of the listed functions */
2886 2,
2927 2,
2887 index_length,
2928 index_length,
2888 index_node,
2929 index_node,
2889 HgRevlogIndex_GetParents,
2930 HgRevlogIndex_GetParents,
2890 };
2931 };
2891
2932
2892 void revlog_module_init(PyObject *mod)
2933 void revlog_module_init(PyObject *mod)
2893 {
2934 {
2894 PyObject *caps = NULL;
2935 PyObject *caps = NULL;
2895 HgRevlogIndex_Type.tp_new = PyType_GenericNew;
2936 HgRevlogIndex_Type.tp_new = PyType_GenericNew;
2896 if (PyType_Ready(&HgRevlogIndex_Type) < 0)
2937 if (PyType_Ready(&HgRevlogIndex_Type) < 0)
2897 return;
2938 return;
2898 Py_INCREF(&HgRevlogIndex_Type);
2939 Py_INCREF(&HgRevlogIndex_Type);
2899 PyModule_AddObject(mod, "index", (PyObject *)&HgRevlogIndex_Type);
2940 PyModule_AddObject(mod, "index", (PyObject *)&HgRevlogIndex_Type);
2900
2941
2901 nodetreeType.tp_new = PyType_GenericNew;
2942 nodetreeType.tp_new = PyType_GenericNew;
2902 if (PyType_Ready(&nodetreeType) < 0)
2943 if (PyType_Ready(&nodetreeType) < 0)
2903 return;
2944 return;
2904 Py_INCREF(&nodetreeType);
2945 Py_INCREF(&nodetreeType);
2905 PyModule_AddObject(mod, "nodetree", (PyObject *)&nodetreeType);
2946 PyModule_AddObject(mod, "nodetree", (PyObject *)&nodetreeType);
2906
2947
2907 if (!nullentry) {
2948 if (!nullentry) {
2908 nullentry =
2949 nullentry =
2909 Py_BuildValue(PY23("iiiiiiis#", "iiiiiiiy#"), 0, 0, 0, -1,
2950 Py_BuildValue(PY23("iiiiiiis#", "iiiiiiiy#"), 0, 0, 0, -1,
2910 -1, -1, -1, nullid, (Py_ssize_t)20);
2951 -1, -1, -1, nullid, (Py_ssize_t)20);
2911 }
2952 }
2912 if (nullentry)
2953 if (nullentry)
2913 PyObject_GC_UnTrack(nullentry);
2954 PyObject_GC_UnTrack(nullentry);
2914
2955
2915 caps = PyCapsule_New(&CAPI, "mercurial.cext.parsers.revlog_CAPI", NULL);
2956 caps = PyCapsule_New(&CAPI, "mercurial.cext.parsers.revlog_CAPI", NULL);
2916 if (caps != NULL)
2957 if (caps != NULL)
2917 PyModule_AddObject(mod, "revlog_CAPI", caps);
2958 PyModule_AddObject(mod, "revlog_CAPI", caps);
2918 }
2959 }
@@ -1,924 +1,929 b''
1 """ Mercurial phases support code
1 """ Mercurial phases support code
2
2
3 ---
3 ---
4
4
5 Copyright 2011 Pierre-Yves David <pierre-yves.david@ens-lyon.org>
5 Copyright 2011 Pierre-Yves David <pierre-yves.david@ens-lyon.org>
6 Logilab SA <contact@logilab.fr>
6 Logilab SA <contact@logilab.fr>
7 Augie Fackler <durin42@gmail.com>
7 Augie Fackler <durin42@gmail.com>
8
8
9 This software may be used and distributed according to the terms
9 This software may be used and distributed according to the terms
10 of the GNU General Public License version 2 or any later version.
10 of the GNU General Public License version 2 or any later version.
11
11
12 ---
12 ---
13
13
14 This module implements most phase logic in mercurial.
14 This module implements most phase logic in mercurial.
15
15
16
16
17 Basic Concept
17 Basic Concept
18 =============
18 =============
19
19
20 A 'changeset phase' is an indicator that tells us how a changeset is
20 A 'changeset phase' is an indicator that tells us how a changeset is
21 manipulated and communicated. The details of each phase is described
21 manipulated and communicated. The details of each phase is described
22 below, here we describe the properties they have in common.
22 below, here we describe the properties they have in common.
23
23
24 Like bookmarks, phases are not stored in history and thus are not
24 Like bookmarks, phases are not stored in history and thus are not
25 permanent and leave no audit trail.
25 permanent and leave no audit trail.
26
26
27 First, no changeset can be in two phases at once. Phases are ordered,
27 First, no changeset can be in two phases at once. Phases are ordered,
28 so they can be considered from lowest to highest. The default, lowest
28 so they can be considered from lowest to highest. The default, lowest
29 phase is 'public' - this is the normal phase of existing changesets. A
29 phase is 'public' - this is the normal phase of existing changesets. A
30 child changeset can not be in a lower phase than its parents.
30 child changeset can not be in a lower phase than its parents.
31
31
32 These phases share a hierarchy of traits:
32 These phases share a hierarchy of traits:
33
33
34 immutable shared
34 immutable shared
35 public: X X
35 public: X X
36 draft: X
36 draft: X
37 secret:
37 secret:
38
38
39 Local commits are draft by default.
39 Local commits are draft by default.
40
40
41 Phase Movement and Exchange
41 Phase Movement and Exchange
42 ===========================
42 ===========================
43
43
44 Phase data is exchanged by pushkey on pull and push. Some servers have
44 Phase data is exchanged by pushkey on pull and push. Some servers have
45 a publish option set, we call such a server a "publishing server".
45 a publish option set, we call such a server a "publishing server".
46 Pushing a draft changeset to a publishing server changes the phase to
46 Pushing a draft changeset to a publishing server changes the phase to
47 public.
47 public.
48
48
49 A small list of fact/rules define the exchange of phase:
49 A small list of fact/rules define the exchange of phase:
50
50
51 * old client never changes server states
51 * old client never changes server states
52 * pull never changes server states
52 * pull never changes server states
53 * publish and old server changesets are seen as public by client
53 * publish and old server changesets are seen as public by client
54 * any secret changeset seen in another repository is lowered to at
54 * any secret changeset seen in another repository is lowered to at
55 least draft
55 least draft
56
56
57 Here is the final table summing up the 49 possible use cases of phase
57 Here is the final table summing up the 49 possible use cases of phase
58 exchange:
58 exchange:
59
59
60 server
60 server
61 old publish non-publish
61 old publish non-publish
62 N X N D P N D P
62 N X N D P N D P
63 old client
63 old client
64 pull
64 pull
65 N - X/X - X/D X/P - X/D X/P
65 N - X/X - X/D X/P - X/D X/P
66 X - X/X - X/D X/P - X/D X/P
66 X - X/X - X/D X/P - X/D X/P
67 push
67 push
68 X X/X X/X X/P X/P X/P X/D X/D X/P
68 X X/X X/X X/P X/P X/P X/D X/D X/P
69 new client
69 new client
70 pull
70 pull
71 N - P/X - P/D P/P - D/D P/P
71 N - P/X - P/D P/P - D/D P/P
72 D - P/X - P/D P/P - D/D P/P
72 D - P/X - P/D P/P - D/D P/P
73 P - P/X - P/D P/P - P/D P/P
73 P - P/X - P/D P/P - P/D P/P
74 push
74 push
75 D P/X P/X P/P P/P P/P D/D D/D P/P
75 D P/X P/X P/P P/P P/P D/D D/D P/P
76 P P/X P/X P/P P/P P/P P/P P/P P/P
76 P P/X P/X P/P P/P P/P P/P P/P P/P
77
77
78 Legend:
78 Legend:
79
79
80 A/B = final state on client / state on server
80 A/B = final state on client / state on server
81
81
82 * N = new/not present,
82 * N = new/not present,
83 * P = public,
83 * P = public,
84 * D = draft,
84 * D = draft,
85 * X = not tracked (i.e., the old client or server has no internal
85 * X = not tracked (i.e., the old client or server has no internal
86 way of recording the phase.)
86 way of recording the phase.)
87
87
88 passive = only pushes
88 passive = only pushes
89
89
90
90
91 A cell here can be read like this:
91 A cell here can be read like this:
92
92
93 "When a new client pushes a draft changeset (D) to a publishing
93 "When a new client pushes a draft changeset (D) to a publishing
94 server where it's not present (N), it's marked public on both
94 server where it's not present (N), it's marked public on both
95 sides (P/P)."
95 sides (P/P)."
96
96
97 Note: old client behave as a publishing server with draft only content
97 Note: old client behave as a publishing server with draft only content
98 - other people see it as public
98 - other people see it as public
99 - content is pushed as draft
99 - content is pushed as draft
100
100
101 """
101 """
102
102
103 from __future__ import absolute_import
103 from __future__ import absolute_import
104
104
105 import errno
105 import errno
106 import struct
106 import struct
107
107
108 from .i18n import _
108 from .i18n import _
109 from .node import (
109 from .node import (
110 bin,
110 bin,
111 hex,
111 hex,
112 nullid,
112 nullid,
113 nullrev,
113 nullrev,
114 short,
114 short,
115 wdirrev,
115 wdirrev,
116 )
116 )
117 from .pycompat import (
117 from .pycompat import (
118 getattr,
118 getattr,
119 setattr,
119 setattr,
120 )
120 )
121 from . import (
121 from . import (
122 error,
122 error,
123 pycompat,
123 pycompat,
124 smartset,
124 smartset,
125 txnutil,
125 txnutil,
126 util,
126 util,
127 )
127 )
128
128
129 _fphasesentry = struct.Struct(b'>i20s')
129 _fphasesentry = struct.Struct(b'>i20s')
130
130
131 # record phase index
131 # record phase index
132 public, draft, secret = range(3)
132 public, draft, secret = range(3)
133 archived = 32 # non-continuous for compatibility
133 archived = 32 # non-continuous for compatibility
134 internal = 96 # non-continuous for compatibility
134 internal = 96 # non-continuous for compatibility
135 allphases = (public, draft, secret, archived, internal)
135 allphases = (public, draft, secret, archived, internal)
136 trackedphases = (draft, secret, archived, internal)
136 trackedphases = (draft, secret, archived, internal)
137 # record phase names
137 # record phase names
138 cmdphasenames = [b'public', b'draft', b'secret'] # known to `hg phase` command
138 cmdphasenames = [b'public', b'draft', b'secret'] # known to `hg phase` command
139 phasenames = dict(enumerate(cmdphasenames))
139 phasenames = dict(enumerate(cmdphasenames))
140 phasenames[archived] = b'archived'
140 phasenames[archived] = b'archived'
141 phasenames[internal] = b'internal'
141 phasenames[internal] = b'internal'
142 # map phase name to phase number
142 # map phase name to phase number
143 phasenumber = {name: phase for phase, name in phasenames.items()}
143 phasenumber = {name: phase for phase, name in phasenames.items()}
144 # like phasenumber, but also include maps for the numeric and binary
144 # like phasenumber, but also include maps for the numeric and binary
145 # phase number to the phase number
145 # phase number to the phase number
146 phasenumber2 = phasenumber.copy()
146 phasenumber2 = phasenumber.copy()
147 phasenumber2.update({phase: phase for phase in phasenames})
147 phasenumber2.update({phase: phase for phase in phasenames})
148 phasenumber2.update({b'%i' % phase: phase for phase in phasenames})
148 phasenumber2.update({b'%i' % phase: phase for phase in phasenames})
149 # record phase property
149 # record phase property
150 mutablephases = (draft, secret, archived, internal)
150 mutablephases = (draft, secret, archived, internal)
151 remotehiddenphases = (secret, archived, internal)
151 remotehiddenphases = (secret, archived, internal)
152 localhiddenphases = (internal, archived)
152 localhiddenphases = (internal, archived)
153
153
154
154
155 def supportinternal(repo):
155 def supportinternal(repo):
156 """True if the internal phase can be used on a repository"""
156 """True if the internal phase can be used on a repository"""
157 return b'internal-phase' in repo.requirements
157 return b'internal-phase' in repo.requirements
158
158
159
159
160 def _readroots(repo, phasedefaults=None):
160 def _readroots(repo, phasedefaults=None):
161 """Read phase roots from disk
161 """Read phase roots from disk
162
162
163 phasedefaults is a list of fn(repo, roots) callable, which are
163 phasedefaults is a list of fn(repo, roots) callable, which are
164 executed if the phase roots file does not exist. When phases are
164 executed if the phase roots file does not exist. When phases are
165 being initialized on an existing repository, this could be used to
165 being initialized on an existing repository, this could be used to
166 set selected changesets phase to something else than public.
166 set selected changesets phase to something else than public.
167
167
168 Return (roots, dirty) where dirty is true if roots differ from
168 Return (roots, dirty) where dirty is true if roots differ from
169 what is being stored.
169 what is being stored.
170 """
170 """
171 repo = repo.unfiltered()
171 repo = repo.unfiltered()
172 dirty = False
172 dirty = False
173 roots = [set() for i in range(max(allphases) + 1)]
173 roots = {i: set() for i in allphases}
174 try:
174 try:
175 f, pending = txnutil.trypending(repo.root, repo.svfs, b'phaseroots')
175 f, pending = txnutil.trypending(repo.root, repo.svfs, b'phaseroots')
176 try:
176 try:
177 for line in f:
177 for line in f:
178 phase, nh = line.split()
178 phase, nh = line.split()
179 roots[int(phase)].add(bin(nh))
179 roots[int(phase)].add(bin(nh))
180 finally:
180 finally:
181 f.close()
181 f.close()
182 except IOError as inst:
182 except IOError as inst:
183 if inst.errno != errno.ENOENT:
183 if inst.errno != errno.ENOENT:
184 raise
184 raise
185 if phasedefaults:
185 if phasedefaults:
186 for f in phasedefaults:
186 for f in phasedefaults:
187 roots = f(repo, roots)
187 roots = f(repo, roots)
188 dirty = True
188 dirty = True
189 return roots, dirty
189 return roots, dirty
190
190
191
191
192 def binaryencode(phasemapping):
192 def binaryencode(phasemapping):
193 """encode a 'phase -> nodes' mapping into a binary stream
193 """encode a 'phase -> nodes' mapping into a binary stream
194
194
195 The revision lists are encoded as (phase, root) pairs.
195 The revision lists are encoded as (phase, root) pairs.
196 """
196 """
197 binarydata = []
197 binarydata = []
198 for phase, nodes in pycompat.iteritems(phasemapping):
198 for phase, nodes in pycompat.iteritems(phasemapping):
199 for head in nodes:
199 for head in nodes:
200 binarydata.append(_fphasesentry.pack(phase, head))
200 binarydata.append(_fphasesentry.pack(phase, head))
201 return b''.join(binarydata)
201 return b''.join(binarydata)
202
202
203
203
204 def binarydecode(stream):
204 def binarydecode(stream):
205 """decode a binary stream into a 'phase -> nodes' mapping
205 """decode a binary stream into a 'phase -> nodes' mapping
206
206
207 The (phase, root) pairs are turned back into a dictionary with
207 The (phase, root) pairs are turned back into a dictionary with
208 the phase as index and the aggregated roots of that phase as value."""
208 the phase as index and the aggregated roots of that phase as value."""
209 headsbyphase = {i: [] for i in allphases}
209 headsbyphase = {i: [] for i in allphases}
210 entrysize = _fphasesentry.size
210 entrysize = _fphasesentry.size
211 while True:
211 while True:
212 entry = stream.read(entrysize)
212 entry = stream.read(entrysize)
213 if len(entry) < entrysize:
213 if len(entry) < entrysize:
214 if entry:
214 if entry:
215 raise error.Abort(_(b'bad phase-heads stream'))
215 raise error.Abort(_(b'bad phase-heads stream'))
216 break
216 break
217 phase, node = _fphasesentry.unpack(entry)
217 phase, node = _fphasesentry.unpack(entry)
218 headsbyphase[phase].append(node)
218 headsbyphase[phase].append(node)
219 return headsbyphase
219 return headsbyphase
220
220
221
221
222 def _sortedrange_insert(data, idx, rev, t):
222 def _sortedrange_insert(data, idx, rev, t):
223 merge_before = False
223 merge_before = False
224 if idx:
224 if idx:
225 r1, t1 = data[idx - 1]
225 r1, t1 = data[idx - 1]
226 merge_before = r1[-1] + 1 == rev and t1 == t
226 merge_before = r1[-1] + 1 == rev and t1 == t
227 merge_after = False
227 merge_after = False
228 if idx < len(data):
228 if idx < len(data):
229 r2, t2 = data[idx]
229 r2, t2 = data[idx]
230 merge_after = r2[0] == rev + 1 and t2 == t
230 merge_after = r2[0] == rev + 1 and t2 == t
231
231
232 if merge_before and merge_after:
232 if merge_before and merge_after:
233 data[idx - 1] = (pycompat.xrange(r1[0], r2[-1] + 1), t)
233 data[idx - 1] = (pycompat.xrange(r1[0], r2[-1] + 1), t)
234 data.pop(idx)
234 data.pop(idx)
235 elif merge_before:
235 elif merge_before:
236 data[idx - 1] = (pycompat.xrange(r1[0], rev + 1), t)
236 data[idx - 1] = (pycompat.xrange(r1[0], rev + 1), t)
237 elif merge_after:
237 elif merge_after:
238 data[idx] = (pycompat.xrange(rev, r2[-1] + 1), t)
238 data[idx] = (pycompat.xrange(rev, r2[-1] + 1), t)
239 else:
239 else:
240 data.insert(idx, (pycompat.xrange(rev, rev + 1), t))
240 data.insert(idx, (pycompat.xrange(rev, rev + 1), t))
241
241
242
242
243 def _sortedrange_split(data, idx, rev, t):
243 def _sortedrange_split(data, idx, rev, t):
244 r1, t1 = data[idx]
244 r1, t1 = data[idx]
245 if t == t1:
245 if t == t1:
246 return
246 return
247 t = (t1[0], t[1])
247 t = (t1[0], t[1])
248 if len(r1) == 1:
248 if len(r1) == 1:
249 data.pop(idx)
249 data.pop(idx)
250 _sortedrange_insert(data, idx, rev, t)
250 _sortedrange_insert(data, idx, rev, t)
251 elif r1[0] == rev:
251 elif r1[0] == rev:
252 data[idx] = (pycompat.xrange(rev + 1, r1[-1] + 1), t1)
252 data[idx] = (pycompat.xrange(rev + 1, r1[-1] + 1), t1)
253 _sortedrange_insert(data, idx, rev, t)
253 _sortedrange_insert(data, idx, rev, t)
254 elif r1[-1] == rev:
254 elif r1[-1] == rev:
255 data[idx] = (pycompat.xrange(r1[0], rev), t1)
255 data[idx] = (pycompat.xrange(r1[0], rev), t1)
256 _sortedrange_insert(data, idx + 1, rev, t)
256 _sortedrange_insert(data, idx + 1, rev, t)
257 else:
257 else:
258 data[idx : idx + 1] = [
258 data[idx : idx + 1] = [
259 (pycompat.xrange(r1[0], rev), t1),
259 (pycompat.xrange(r1[0], rev), t1),
260 (pycompat.xrange(rev, rev + 1), t),
260 (pycompat.xrange(rev, rev + 1), t),
261 (pycompat.xrange(rev + 1, r1[-1] + 1), t1),
261 (pycompat.xrange(rev + 1, r1[-1] + 1), t1),
262 ]
262 ]
263
263
264
264
265 def _trackphasechange(data, rev, old, new):
265 def _trackphasechange(data, rev, old, new):
266 """add a phase move to the <data> list of ranges
266 """add a phase move to the <data> list of ranges
267
267
268 If data is None, nothing happens.
268 If data is None, nothing happens.
269 """
269 """
270 if data is None:
270 if data is None:
271 return
271 return
272
272
273 # If data is empty, create a one-revision range and done
273 # If data is empty, create a one-revision range and done
274 if not data:
274 if not data:
275 data.insert(0, (pycompat.xrange(rev, rev + 1), (old, new)))
275 data.insert(0, (pycompat.xrange(rev, rev + 1), (old, new)))
276 return
276 return
277
277
278 low = 0
278 low = 0
279 high = len(data)
279 high = len(data)
280 t = (old, new)
280 t = (old, new)
281 while low < high:
281 while low < high:
282 mid = (low + high) // 2
282 mid = (low + high) // 2
283 revs = data[mid][0]
283 revs = data[mid][0]
284
284
285 if rev in revs:
285 if rev in revs:
286 _sortedrange_split(data, mid, rev, t)
286 _sortedrange_split(data, mid, rev, t)
287 return
287 return
288
288
289 if revs[0] == rev + 1:
289 if revs[0] == rev + 1:
290 if mid and data[mid - 1][0][-1] == rev:
290 if mid and data[mid - 1][0][-1] == rev:
291 _sortedrange_split(data, mid - 1, rev, t)
291 _sortedrange_split(data, mid - 1, rev, t)
292 else:
292 else:
293 _sortedrange_insert(data, mid, rev, t)
293 _sortedrange_insert(data, mid, rev, t)
294 return
294 return
295
295
296 if revs[-1] == rev - 1:
296 if revs[-1] == rev - 1:
297 if mid + 1 < len(data) and data[mid + 1][0][0] == rev:
297 if mid + 1 < len(data) and data[mid + 1][0][0] == rev:
298 _sortedrange_split(data, mid + 1, rev, t)
298 _sortedrange_split(data, mid + 1, rev, t)
299 else:
299 else:
300 _sortedrange_insert(data, mid + 1, rev, t)
300 _sortedrange_insert(data, mid + 1, rev, t)
301 return
301 return
302
302
303 if revs[0] > rev:
303 if revs[0] > rev:
304 high = mid
304 high = mid
305 else:
305 else:
306 low = mid + 1
306 low = mid + 1
307
307
308 if low == len(data):
308 if low == len(data):
309 data.append((pycompat.xrange(rev, rev + 1), t))
309 data.append((pycompat.xrange(rev, rev + 1), t))
310 return
310 return
311
311
312 r1, t1 = data[low]
312 r1, t1 = data[low]
313 if r1[0] > rev:
313 if r1[0] > rev:
314 data.insert(low, (pycompat.xrange(rev, rev + 1), t))
314 data.insert(low, (pycompat.xrange(rev, rev + 1), t))
315 else:
315 else:
316 data.insert(low + 1, (pycompat.xrange(rev, rev + 1), t))
316 data.insert(low + 1, (pycompat.xrange(rev, rev + 1), t))
317
317
318
318
319 class phasecache(object):
319 class phasecache(object):
320 def __init__(self, repo, phasedefaults, _load=True):
320 def __init__(self, repo, phasedefaults, _load=True):
321 if _load:
321 if _load:
322 # Cheap trick to allow shallow-copy without copy module
322 # Cheap trick to allow shallow-copy without copy module
323 self.phaseroots, self.dirty = _readroots(repo, phasedefaults)
323 self.phaseroots, self.dirty = _readroots(repo, phasedefaults)
324 self._loadedrevslen = 0
324 self._loadedrevslen = 0
325 self._phasesets = None
325 self._phasesets = None
326 self.filterunknown(repo)
326 self.filterunknown(repo)
327 self.opener = repo.svfs
327 self.opener = repo.svfs
328
328
329 def hasnonpublicphases(self, repo):
329 def hasnonpublicphases(self, repo):
330 """detect if there are revisions with non-public phase"""
330 """detect if there are revisions with non-public phase"""
331 repo = repo.unfiltered()
331 repo = repo.unfiltered()
332 cl = repo.changelog
332 cl = repo.changelog
333 if len(cl) >= self._loadedrevslen:
333 if len(cl) >= self._loadedrevslen:
334 self.invalidate()
334 self.invalidate()
335 self.loadphaserevs(repo)
335 self.loadphaserevs(repo)
336 return any(self.phaseroots[1:])
336 return any(
337 revs
338 for phase, revs in pycompat.iteritems(self.phaseroots)
339 if phase != public
340 )
337
341
338 def nonpublicphaseroots(self, repo):
342 def nonpublicphaseroots(self, repo):
339 """returns the roots of all non-public phases
343 """returns the roots of all non-public phases
340
344
341 The roots are not minimized, so if the secret revisions are
345 The roots are not minimized, so if the secret revisions are
342 descendants of draft revisions, their roots will still be present.
346 descendants of draft revisions, their roots will still be present.
343 """
347 """
344 repo = repo.unfiltered()
348 repo = repo.unfiltered()
345 cl = repo.changelog
349 cl = repo.changelog
346 if len(cl) >= self._loadedrevslen:
350 if len(cl) >= self._loadedrevslen:
347 self.invalidate()
351 self.invalidate()
348 self.loadphaserevs(repo)
352 self.loadphaserevs(repo)
349 return set().union(*[roots for roots in self.phaseroots[1:] if roots])
353 return set().union(
354 *[
355 revs
356 for phase, revs in pycompat.iteritems(self.phaseroots)
357 if phase != public
358 ]
359 )
350
360
351 def getrevset(self, repo, phases, subset=None):
361 def getrevset(self, repo, phases, subset=None):
352 """return a smartset for the given phases"""
362 """return a smartset for the given phases"""
353 self.loadphaserevs(repo) # ensure phase's sets are loaded
363 self.loadphaserevs(repo) # ensure phase's sets are loaded
354 phases = set(phases)
364 phases = set(phases)
355 publicphase = public in phases
365 publicphase = public in phases
356
366
357 if publicphase:
367 if publicphase:
358 # In this case, phases keeps all the *other* phases.
368 # In this case, phases keeps all the *other* phases.
359 phases = set(allphases).difference(phases)
369 phases = set(allphases).difference(phases)
360 if not phases:
370 if not phases:
361 return smartset.fullreposet(repo)
371 return smartset.fullreposet(repo)
362
372
363 # fast path: _phasesets contains the interesting sets,
373 # fast path: _phasesets contains the interesting sets,
364 # might only need a union and post-filtering.
374 # might only need a union and post-filtering.
365 revsneedscopy = False
375 revsneedscopy = False
366 if len(phases) == 1:
376 if len(phases) == 1:
367 [p] = phases
377 [p] = phases
368 revs = self._phasesets[p]
378 revs = self._phasesets[p]
369 revsneedscopy = True # Don't modify _phasesets
379 revsneedscopy = True # Don't modify _phasesets
370 else:
380 else:
371 # revs has the revisions in all *other* phases.
381 # revs has the revisions in all *other* phases.
372 revs = set.union(*[self._phasesets[p] for p in phases])
382 revs = set.union(*[self._phasesets[p] for p in phases])
373
383
374 def _addwdir(wdirsubset, wdirrevs):
384 def _addwdir(wdirsubset, wdirrevs):
375 if wdirrev in wdirsubset and repo[None].phase() in phases:
385 if wdirrev in wdirsubset and repo[None].phase() in phases:
376 if revsneedscopy:
386 if revsneedscopy:
377 wdirrevs = wdirrevs.copy()
387 wdirrevs = wdirrevs.copy()
378 # The working dir would never be in the # cache, but it was in
388 # The working dir would never be in the # cache, but it was in
379 # the subset being filtered for its phase (or filtered out,
389 # the subset being filtered for its phase (or filtered out,
380 # depending on publicphase), so add it to the output to be
390 # depending on publicphase), so add it to the output to be
381 # included (or filtered out).
391 # included (or filtered out).
382 wdirrevs.add(wdirrev)
392 wdirrevs.add(wdirrev)
383 return wdirrevs
393 return wdirrevs
384
394
385 if not publicphase:
395 if not publicphase:
386 if repo.changelog.filteredrevs:
396 if repo.changelog.filteredrevs:
387 revs = revs - repo.changelog.filteredrevs
397 revs = revs - repo.changelog.filteredrevs
388
398
389 if subset is None:
399 if subset is None:
390 return smartset.baseset(revs)
400 return smartset.baseset(revs)
391 else:
401 else:
392 revs = _addwdir(subset, revs)
402 revs = _addwdir(subset, revs)
393 return subset & smartset.baseset(revs)
403 return subset & smartset.baseset(revs)
394 else:
404 else:
395 if subset is None:
405 if subset is None:
396 subset = smartset.fullreposet(repo)
406 subset = smartset.fullreposet(repo)
397
407
398 revs = _addwdir(subset, revs)
408 revs = _addwdir(subset, revs)
399
409
400 if not revs:
410 if not revs:
401 return subset
411 return subset
402 return subset.filter(lambda r: r not in revs)
412 return subset.filter(lambda r: r not in revs)
403
413
404 def copy(self):
414 def copy(self):
405 # Shallow copy meant to ensure isolation in
415 # Shallow copy meant to ensure isolation in
406 # advance/retractboundary(), nothing more.
416 # advance/retractboundary(), nothing more.
407 ph = self.__class__(None, None, _load=False)
417 ph = self.__class__(None, None, _load=False)
408 ph.phaseroots = self.phaseroots[:]
418 ph.phaseroots = self.phaseroots.copy()
409 ph.dirty = self.dirty
419 ph.dirty = self.dirty
410 ph.opener = self.opener
420 ph.opener = self.opener
411 ph._loadedrevslen = self._loadedrevslen
421 ph._loadedrevslen = self._loadedrevslen
412 ph._phasesets = self._phasesets
422 ph._phasesets = self._phasesets
413 return ph
423 return ph
414
424
415 def replace(self, phcache):
425 def replace(self, phcache):
416 """replace all values in 'self' with content of phcache"""
426 """replace all values in 'self' with content of phcache"""
417 for a in (
427 for a in (
418 b'phaseroots',
428 b'phaseroots',
419 b'dirty',
429 b'dirty',
420 b'opener',
430 b'opener',
421 b'_loadedrevslen',
431 b'_loadedrevslen',
422 b'_phasesets',
432 b'_phasesets',
423 ):
433 ):
424 setattr(self, a, getattr(phcache, a))
434 setattr(self, a, getattr(phcache, a))
425
435
426 def _getphaserevsnative(self, repo):
436 def _getphaserevsnative(self, repo):
427 repo = repo.unfiltered()
437 repo = repo.unfiltered()
428 nativeroots = []
438 return repo.changelog.computephases(self.phaseroots)
429 for phase in trackedphases:
430 nativeroots.append(
431 pycompat.maplist(repo.changelog.rev, self.phaseroots[phase])
432 )
433 revslen, phasesets = repo.changelog.computephases(nativeroots)
434 phasesets2 = [set() for phase in range(max(allphases) + 1)]
435 for phase, phaseset in zip(allphases, phasesets):
436 phasesets2[phase] = phaseset
437 return revslen, phasesets2
438
439
439 def _computephaserevspure(self, repo):
440 def _computephaserevspure(self, repo):
440 repo = repo.unfiltered()
441 repo = repo.unfiltered()
441 cl = repo.changelog
442 cl = repo.changelog
442 self._phasesets = [set() for phase in range(max(allphases) + 1)]
443 self._phasesets = {phase: set() for phase in allphases}
443 lowerroots = set()
444 lowerroots = set()
444 for phase in reversed(trackedphases):
445 for phase in reversed(trackedphases):
445 roots = pycompat.maplist(cl.rev, self.phaseroots[phase])
446 roots = pycompat.maplist(cl.rev, self.phaseroots[phase])
446 if roots:
447 if roots:
447 ps = set(cl.descendants(roots))
448 ps = set(cl.descendants(roots))
448 for root in roots:
449 for root in roots:
449 ps.add(root)
450 ps.add(root)
450 ps.difference_update(lowerroots)
451 ps.difference_update(lowerroots)
451 lowerroots.update(ps)
452 lowerroots.update(ps)
452 self._phasesets[phase] = ps
453 self._phasesets[phase] = ps
453 self._loadedrevslen = len(cl)
454 self._loadedrevslen = len(cl)
454
455
455 def loadphaserevs(self, repo):
456 def loadphaserevs(self, repo):
456 """ensure phase information is loaded in the object"""
457 """ensure phase information is loaded in the object"""
457 if self._phasesets is None:
458 if self._phasesets is None:
458 try:
459 try:
459 res = self._getphaserevsnative(repo)
460 res = self._getphaserevsnative(repo)
460 self._loadedrevslen, self._phasesets = res
461 self._loadedrevslen, self._phasesets = res
461 except AttributeError:
462 except AttributeError:
462 self._computephaserevspure(repo)
463 self._computephaserevspure(repo)
463
464
464 def invalidate(self):
465 def invalidate(self):
465 self._loadedrevslen = 0
466 self._loadedrevslen = 0
466 self._phasesets = None
467 self._phasesets = None
467
468
468 def phase(self, repo, rev):
469 def phase(self, repo, rev):
469 # We need a repo argument here to be able to build _phasesets
470 # We need a repo argument here to be able to build _phasesets
470 # if necessary. The repository instance is not stored in
471 # if necessary. The repository instance is not stored in
471 # phasecache to avoid reference cycles. The changelog instance
472 # phasecache to avoid reference cycles. The changelog instance
472 # is not stored because it is a filecache() property and can
473 # is not stored because it is a filecache() property and can
473 # be replaced without us being notified.
474 # be replaced without us being notified.
474 if rev == nullrev:
475 if rev == nullrev:
475 return public
476 return public
476 if rev < nullrev:
477 if rev < nullrev:
477 raise ValueError(_(b'cannot lookup negative revision'))
478 raise ValueError(_(b'cannot lookup negative revision'))
478 if rev >= self._loadedrevslen:
479 if rev >= self._loadedrevslen:
479 self.invalidate()
480 self.invalidate()
480 self.loadphaserevs(repo)
481 self.loadphaserevs(repo)
481 for phase in trackedphases:
482 for phase in trackedphases:
482 if rev in self._phasesets[phase]:
483 if rev in self._phasesets[phase]:
483 return phase
484 return phase
484 return public
485 return public
485
486
486 def write(self):
487 def write(self):
487 if not self.dirty:
488 if not self.dirty:
488 return
489 return
489 f = self.opener(b'phaseroots', b'w', atomictemp=True, checkambig=True)
490 f = self.opener(b'phaseroots', b'w', atomictemp=True, checkambig=True)
490 try:
491 try:
491 self._write(f)
492 self._write(f)
492 finally:
493 finally:
493 f.close()
494 f.close()
494
495
495 def _write(self, fp):
496 def _write(self, fp):
496 for phase, roots in enumerate(self.phaseroots):
497 for phase, roots in pycompat.iteritems(self.phaseroots):
497 for h in sorted(roots):
498 for h in sorted(roots):
498 fp.write(b'%i %s\n' % (phase, hex(h)))
499 fp.write(b'%i %s\n' % (phase, hex(h)))
499 self.dirty = False
500 self.dirty = False
500
501
501 def _updateroots(self, phase, newroots, tr):
502 def _updateroots(self, phase, newroots, tr):
502 self.phaseroots[phase] = newroots
503 self.phaseroots[phase] = newroots
503 self.invalidate()
504 self.invalidate()
504 self.dirty = True
505 self.dirty = True
505
506
506 tr.addfilegenerator(b'phase', (b'phaseroots',), self._write)
507 tr.addfilegenerator(b'phase', (b'phaseroots',), self._write)
507 tr.hookargs[b'phases_moved'] = b'1'
508 tr.hookargs[b'phases_moved'] = b'1'
508
509
509 def registernew(self, repo, tr, targetphase, nodes):
510 def registernew(self, repo, tr, targetphase, nodes):
510 repo = repo.unfiltered()
511 repo = repo.unfiltered()
511 self._retractboundary(repo, tr, targetphase, nodes)
512 self._retractboundary(repo, tr, targetphase, nodes)
512 if tr is not None and b'phases' in tr.changes:
513 if tr is not None and b'phases' in tr.changes:
513 phasetracking = tr.changes[b'phases']
514 phasetracking = tr.changes[b'phases']
514 torev = repo.changelog.rev
515 torev = repo.changelog.rev
515 phase = self.phase
516 phase = self.phase
516 revs = [torev(node) for node in nodes]
517 revs = [torev(node) for node in nodes]
517 revs.sort()
518 revs.sort()
518 for rev in revs:
519 for rev in revs:
519 revphase = phase(repo, rev)
520 revphase = phase(repo, rev)
520 _trackphasechange(phasetracking, rev, None, revphase)
521 _trackphasechange(phasetracking, rev, None, revphase)
521 repo.invalidatevolatilesets()
522 repo.invalidatevolatilesets()
522
523
523 def advanceboundary(self, repo, tr, targetphase, nodes, dryrun=None):
524 def advanceboundary(self, repo, tr, targetphase, nodes, dryrun=None):
524 """Set all 'nodes' to phase 'targetphase'
525 """Set all 'nodes' to phase 'targetphase'
525
526
526 Nodes with a phase lower than 'targetphase' are not affected.
527 Nodes with a phase lower than 'targetphase' are not affected.
527
528
528 If dryrun is True, no actions will be performed
529 If dryrun is True, no actions will be performed
529
530
530 Returns a set of revs whose phase is changed or should be changed
531 Returns a set of revs whose phase is changed or should be changed
531 """
532 """
532 # Be careful to preserve shallow-copied values: do not update
533 # Be careful to preserve shallow-copied values: do not update
533 # phaseroots values, replace them.
534 # phaseroots values, replace them.
534 if tr is None:
535 if tr is None:
535 phasetracking = None
536 phasetracking = None
536 else:
537 else:
537 phasetracking = tr.changes.get(b'phases')
538 phasetracking = tr.changes.get(b'phases')
538
539
539 repo = repo.unfiltered()
540 repo = repo.unfiltered()
540
541
541 changes = set() # set of revisions to be changed
542 changes = set() # set of revisions to be changed
542 delroots = [] # set of root deleted by this path
543 delroots = [] # set of root deleted by this path
543 for phase in (phase for phase in allphases if phase > targetphase):
544 for phase in (phase for phase in allphases if phase > targetphase):
544 # filter nodes that are not in a compatible phase already
545 # filter nodes that are not in a compatible phase already
545 nodes = [
546 nodes = [
546 n for n in nodes if self.phase(repo, repo[n].rev()) >= phase
547 n for n in nodes if self.phase(repo, repo[n].rev()) >= phase
547 ]
548 ]
548 if not nodes:
549 if not nodes:
549 break # no roots to move anymore
550 break # no roots to move anymore
550
551
551 olds = self.phaseroots[phase]
552 olds = self.phaseroots[phase]
552
553
553 affected = repo.revs(b'%ln::%ln', olds, nodes)
554 affected = repo.revs(b'%ln::%ln', olds, nodes)
554 changes.update(affected)
555 changes.update(affected)
555 if dryrun:
556 if dryrun:
556 continue
557 continue
557 for r in affected:
558 for r in affected:
558 _trackphasechange(
559 _trackphasechange(
559 phasetracking, r, self.phase(repo, r), targetphase
560 phasetracking, r, self.phase(repo, r), targetphase
560 )
561 )
561
562
562 roots = {
563 roots = {
563 ctx.node()
564 ctx.node()
564 for ctx in repo.set(b'roots((%ln::) - %ld)', olds, affected)
565 for ctx in repo.set(b'roots((%ln::) - %ld)', olds, affected)
565 }
566 }
566 if olds != roots:
567 if olds != roots:
567 self._updateroots(phase, roots, tr)
568 self._updateroots(phase, roots, tr)
568 # some roots may need to be declared for lower phases
569 # some roots may need to be declared for lower phases
569 delroots.extend(olds - roots)
570 delroots.extend(olds - roots)
570 if not dryrun:
571 if not dryrun:
571 # declare deleted root in the target phase
572 # declare deleted root in the target phase
572 if targetphase != 0:
573 if targetphase != 0:
573 self._retractboundary(repo, tr, targetphase, delroots)
574 self._retractboundary(repo, tr, targetphase, delroots)
574 repo.invalidatevolatilesets()
575 repo.invalidatevolatilesets()
575 return changes
576 return changes
576
577
577 def retractboundary(self, repo, tr, targetphase, nodes):
578 def retractboundary(self, repo, tr, targetphase, nodes):
578 oldroots = self.phaseroots[: targetphase + 1]
579 oldroots = {
580 phase: revs
581 for phase, revs in pycompat.iteritems(self.phaseroots)
582 if phase <= targetphase
583 }
579 if tr is None:
584 if tr is None:
580 phasetracking = None
585 phasetracking = None
581 else:
586 else:
582 phasetracking = tr.changes.get(b'phases')
587 phasetracking = tr.changes.get(b'phases')
583 repo = repo.unfiltered()
588 repo = repo.unfiltered()
584 if (
589 if (
585 self._retractboundary(repo, tr, targetphase, nodes)
590 self._retractboundary(repo, tr, targetphase, nodes)
586 and phasetracking is not None
591 and phasetracking is not None
587 ):
592 ):
588
593
589 # find the affected revisions
594 # find the affected revisions
590 new = self.phaseroots[targetphase]
595 new = self.phaseroots[targetphase]
591 old = oldroots[targetphase]
596 old = oldroots[targetphase]
592 affected = set(repo.revs(b'(%ln::) - (%ln::)', new, old))
597 affected = set(repo.revs(b'(%ln::) - (%ln::)', new, old))
593
598
594 # find the phase of the affected revision
599 # find the phase of the affected revision
595 for phase in pycompat.xrange(targetphase, -1, -1):
600 for phase in pycompat.xrange(targetphase, -1, -1):
596 if phase:
601 if phase:
597 roots = oldroots[phase]
602 roots = oldroots.get(phase, [])
598 revs = set(repo.revs(b'%ln::%ld', roots, affected))
603 revs = set(repo.revs(b'%ln::%ld', roots, affected))
599 affected -= revs
604 affected -= revs
600 else: # public phase
605 else: # public phase
601 revs = affected
606 revs = affected
602 for r in sorted(revs):
607 for r in sorted(revs):
603 _trackphasechange(phasetracking, r, phase, targetphase)
608 _trackphasechange(phasetracking, r, phase, targetphase)
604 repo.invalidatevolatilesets()
609 repo.invalidatevolatilesets()
605
610
606 def _retractboundary(self, repo, tr, targetphase, nodes):
611 def _retractboundary(self, repo, tr, targetphase, nodes):
607 # Be careful to preserve shallow-copied values: do not update
612 # Be careful to preserve shallow-copied values: do not update
608 # phaseroots values, replace them.
613 # phaseroots values, replace them.
609 if targetphase in (archived, internal) and not supportinternal(repo):
614 if targetphase in (archived, internal) and not supportinternal(repo):
610 name = phasenames[targetphase]
615 name = phasenames[targetphase]
611 msg = b'this repository does not support the %s phase' % name
616 msg = b'this repository does not support the %s phase' % name
612 raise error.ProgrammingError(msg)
617 raise error.ProgrammingError(msg)
613
618
614 repo = repo.unfiltered()
619 repo = repo.unfiltered()
615 torev = repo.changelog.rev
620 torev = repo.changelog.rev
616 tonode = repo.changelog.node
621 tonode = repo.changelog.node
617 currentroots = {torev(node) for node in self.phaseroots[targetphase]}
622 currentroots = {torev(node) for node in self.phaseroots[targetphase]}
618 finalroots = oldroots = set(currentroots)
623 finalroots = oldroots = set(currentroots)
619 newroots = [torev(node) for node in nodes]
624 newroots = [torev(node) for node in nodes]
620 newroots = [
625 newroots = [
621 rev for rev in newroots if self.phase(repo, rev) < targetphase
626 rev for rev in newroots if self.phase(repo, rev) < targetphase
622 ]
627 ]
623
628
624 if newroots:
629 if newroots:
625 if nullrev in newroots:
630 if nullrev in newroots:
626 raise error.Abort(_(b'cannot change null revision phase'))
631 raise error.Abort(_(b'cannot change null revision phase'))
627 currentroots.update(newroots)
632 currentroots.update(newroots)
628
633
629 # Only compute new roots for revs above the roots that are being
634 # Only compute new roots for revs above the roots that are being
630 # retracted.
635 # retracted.
631 minnewroot = min(newroots)
636 minnewroot = min(newroots)
632 aboveroots = [rev for rev in currentroots if rev >= minnewroot]
637 aboveroots = [rev for rev in currentroots if rev >= minnewroot]
633 updatedroots = repo.revs(b'roots(%ld::)', aboveroots)
638 updatedroots = repo.revs(b'roots(%ld::)', aboveroots)
634
639
635 finalroots = {rev for rev in currentroots if rev < minnewroot}
640 finalroots = {rev for rev in currentroots if rev < minnewroot}
636 finalroots.update(updatedroots)
641 finalroots.update(updatedroots)
637 if finalroots != oldroots:
642 if finalroots != oldroots:
638 self._updateroots(
643 self._updateroots(
639 targetphase, {tonode(rev) for rev in finalroots}, tr
644 targetphase, {tonode(rev) for rev in finalroots}, tr
640 )
645 )
641 return True
646 return True
642 return False
647 return False
643
648
644 def filterunknown(self, repo):
649 def filterunknown(self, repo):
645 """remove unknown nodes from the phase boundary
650 """remove unknown nodes from the phase boundary
646
651
647 Nothing is lost as unknown nodes only hold data for their descendants.
652 Nothing is lost as unknown nodes only hold data for their descendants.
648 """
653 """
649 filtered = False
654 filtered = False
650 has_node = repo.changelog.index.has_node # to filter unknown nodes
655 has_node = repo.changelog.index.has_node # to filter unknown nodes
651 for phase, nodes in enumerate(self.phaseroots):
656 for phase, nodes in pycompat.iteritems(self.phaseroots):
652 missing = sorted(node for node in nodes if not has_node(node))
657 missing = sorted(node for node in nodes if not has_node(node))
653 if missing:
658 if missing:
654 for mnode in missing:
659 for mnode in missing:
655 repo.ui.debug(
660 repo.ui.debug(
656 b'removing unknown node %s from %i-phase boundary\n'
661 b'removing unknown node %s from %i-phase boundary\n'
657 % (short(mnode), phase)
662 % (short(mnode), phase)
658 )
663 )
659 nodes.symmetric_difference_update(missing)
664 nodes.symmetric_difference_update(missing)
660 filtered = True
665 filtered = True
661 if filtered:
666 if filtered:
662 self.dirty = True
667 self.dirty = True
663 # filterunknown is called by repo.destroyed, we may have no changes in
668 # filterunknown is called by repo.destroyed, we may have no changes in
664 # root but _phasesets contents is certainly invalid (or at least we
669 # root but _phasesets contents is certainly invalid (or at least we
665 # have not proper way to check that). related to issue 3858.
670 # have not proper way to check that). related to issue 3858.
666 #
671 #
667 # The other caller is __init__ that have no _phasesets initialized
672 # The other caller is __init__ that have no _phasesets initialized
668 # anyway. If this change we should consider adding a dedicated
673 # anyway. If this change we should consider adding a dedicated
669 # "destroyed" function to phasecache or a proper cache key mechanism
674 # "destroyed" function to phasecache or a proper cache key mechanism
670 # (see branchmap one)
675 # (see branchmap one)
671 self.invalidate()
676 self.invalidate()
672
677
673
678
674 def advanceboundary(repo, tr, targetphase, nodes, dryrun=None):
679 def advanceboundary(repo, tr, targetphase, nodes, dryrun=None):
675 """Add nodes to a phase changing other nodes phases if necessary.
680 """Add nodes to a phase changing other nodes phases if necessary.
676
681
677 This function move boundary *forward* this means that all nodes
682 This function move boundary *forward* this means that all nodes
678 are set in the target phase or kept in a *lower* phase.
683 are set in the target phase or kept in a *lower* phase.
679
684
680 Simplify boundary to contains phase roots only.
685 Simplify boundary to contains phase roots only.
681
686
682 If dryrun is True, no actions will be performed
687 If dryrun is True, no actions will be performed
683
688
684 Returns a set of revs whose phase is changed or should be changed
689 Returns a set of revs whose phase is changed or should be changed
685 """
690 """
686 phcache = repo._phasecache.copy()
691 phcache = repo._phasecache.copy()
687 changes = phcache.advanceboundary(
692 changes = phcache.advanceboundary(
688 repo, tr, targetphase, nodes, dryrun=dryrun
693 repo, tr, targetphase, nodes, dryrun=dryrun
689 )
694 )
690 if not dryrun:
695 if not dryrun:
691 repo._phasecache.replace(phcache)
696 repo._phasecache.replace(phcache)
692 return changes
697 return changes
693
698
694
699
695 def retractboundary(repo, tr, targetphase, nodes):
700 def retractboundary(repo, tr, targetphase, nodes):
696 """Set nodes back to a phase changing other nodes phases if
701 """Set nodes back to a phase changing other nodes phases if
697 necessary.
702 necessary.
698
703
699 This function move boundary *backward* this means that all nodes
704 This function move boundary *backward* this means that all nodes
700 are set in the target phase or kept in a *higher* phase.
705 are set in the target phase or kept in a *higher* phase.
701
706
702 Simplify boundary to contains phase roots only."""
707 Simplify boundary to contains phase roots only."""
703 phcache = repo._phasecache.copy()
708 phcache = repo._phasecache.copy()
704 phcache.retractboundary(repo, tr, targetphase, nodes)
709 phcache.retractboundary(repo, tr, targetphase, nodes)
705 repo._phasecache.replace(phcache)
710 repo._phasecache.replace(phcache)
706
711
707
712
708 def registernew(repo, tr, targetphase, nodes):
713 def registernew(repo, tr, targetphase, nodes):
709 """register a new revision and its phase
714 """register a new revision and its phase
710
715
711 Code adding revisions to the repository should use this function to
716 Code adding revisions to the repository should use this function to
712 set new changeset in their target phase (or higher).
717 set new changeset in their target phase (or higher).
713 """
718 """
714 phcache = repo._phasecache.copy()
719 phcache = repo._phasecache.copy()
715 phcache.registernew(repo, tr, targetphase, nodes)
720 phcache.registernew(repo, tr, targetphase, nodes)
716 repo._phasecache.replace(phcache)
721 repo._phasecache.replace(phcache)
717
722
718
723
719 def listphases(repo):
724 def listphases(repo):
720 """List phases root for serialization over pushkey"""
725 """List phases root for serialization over pushkey"""
721 # Use ordered dictionary so behavior is deterministic.
726 # Use ordered dictionary so behavior is deterministic.
722 keys = util.sortdict()
727 keys = util.sortdict()
723 value = b'%i' % draft
728 value = b'%i' % draft
724 cl = repo.unfiltered().changelog
729 cl = repo.unfiltered().changelog
725 for root in repo._phasecache.phaseroots[draft]:
730 for root in repo._phasecache.phaseroots[draft]:
726 if repo._phasecache.phase(repo, cl.rev(root)) <= draft:
731 if repo._phasecache.phase(repo, cl.rev(root)) <= draft:
727 keys[hex(root)] = value
732 keys[hex(root)] = value
728
733
729 if repo.publishing():
734 if repo.publishing():
730 # Add an extra data to let remote know we are a publishing
735 # Add an extra data to let remote know we are a publishing
731 # repo. Publishing repo can't just pretend they are old repo.
736 # repo. Publishing repo can't just pretend they are old repo.
732 # When pushing to a publishing repo, the client still need to
737 # When pushing to a publishing repo, the client still need to
733 # push phase boundary
738 # push phase boundary
734 #
739 #
735 # Push do not only push changeset. It also push phase data.
740 # Push do not only push changeset. It also push phase data.
736 # New phase data may apply to common changeset which won't be
741 # New phase data may apply to common changeset which won't be
737 # push (as they are common). Here is a very simple example:
742 # push (as they are common). Here is a very simple example:
738 #
743 #
739 # 1) repo A push changeset X as draft to repo B
744 # 1) repo A push changeset X as draft to repo B
740 # 2) repo B make changeset X public
745 # 2) repo B make changeset X public
741 # 3) repo B push to repo A. X is not pushed but the data that
746 # 3) repo B push to repo A. X is not pushed but the data that
742 # X as now public should
747 # X as now public should
743 #
748 #
744 # The server can't handle it on it's own as it has no idea of
749 # The server can't handle it on it's own as it has no idea of
745 # client phase data.
750 # client phase data.
746 keys[b'publishing'] = b'True'
751 keys[b'publishing'] = b'True'
747 return keys
752 return keys
748
753
749
754
750 def pushphase(repo, nhex, oldphasestr, newphasestr):
755 def pushphase(repo, nhex, oldphasestr, newphasestr):
751 """List phases root for serialization over pushkey"""
756 """List phases root for serialization over pushkey"""
752 repo = repo.unfiltered()
757 repo = repo.unfiltered()
753 with repo.lock():
758 with repo.lock():
754 currentphase = repo[nhex].phase()
759 currentphase = repo[nhex].phase()
755 newphase = abs(int(newphasestr)) # let's avoid negative index surprise
760 newphase = abs(int(newphasestr)) # let's avoid negative index surprise
756 oldphase = abs(int(oldphasestr)) # let's avoid negative index surprise
761 oldphase = abs(int(oldphasestr)) # let's avoid negative index surprise
757 if currentphase == oldphase and newphase < oldphase:
762 if currentphase == oldphase and newphase < oldphase:
758 with repo.transaction(b'pushkey-phase') as tr:
763 with repo.transaction(b'pushkey-phase') as tr:
759 advanceboundary(repo, tr, newphase, [bin(nhex)])
764 advanceboundary(repo, tr, newphase, [bin(nhex)])
760 return True
765 return True
761 elif currentphase == newphase:
766 elif currentphase == newphase:
762 # raced, but got correct result
767 # raced, but got correct result
763 return True
768 return True
764 else:
769 else:
765 return False
770 return False
766
771
767
772
768 def subsetphaseheads(repo, subset):
773 def subsetphaseheads(repo, subset):
769 """Finds the phase heads for a subset of a history
774 """Finds the phase heads for a subset of a history
770
775
771 Returns a list indexed by phase number where each item is a list of phase
776 Returns a list indexed by phase number where each item is a list of phase
772 head nodes.
777 head nodes.
773 """
778 """
774 cl = repo.changelog
779 cl = repo.changelog
775
780
776 headsbyphase = {i: [] for i in allphases}
781 headsbyphase = {i: [] for i in allphases}
777 # No need to keep track of secret phase; any heads in the subset that
782 # No need to keep track of secret phase; any heads in the subset that
778 # are not mentioned are implicitly secret.
783 # are not mentioned are implicitly secret.
779 for phase in allphases[:secret]:
784 for phase in allphases[:secret]:
780 revset = b"heads(%%ln & %s())" % phasenames[phase]
785 revset = b"heads(%%ln & %s())" % phasenames[phase]
781 headsbyphase[phase] = [cl.node(r) for r in repo.revs(revset, subset)]
786 headsbyphase[phase] = [cl.node(r) for r in repo.revs(revset, subset)]
782 return headsbyphase
787 return headsbyphase
783
788
784
789
785 def updatephases(repo, trgetter, headsbyphase):
790 def updatephases(repo, trgetter, headsbyphase):
786 """Updates the repo with the given phase heads"""
791 """Updates the repo with the given phase heads"""
787 # Now advance phase boundaries of all phases
792 # Now advance phase boundaries of all phases
788 #
793 #
789 # run the update (and fetch transaction) only if there are actually things
794 # run the update (and fetch transaction) only if there are actually things
790 # to update. This avoid creating empty transaction during no-op operation.
795 # to update. This avoid creating empty transaction during no-op operation.
791
796
792 for phase in allphases:
797 for phase in allphases:
793 revset = b'%ln - _phase(%s)'
798 revset = b'%ln - _phase(%s)'
794 heads = [c.node() for c in repo.set(revset, headsbyphase[phase], phase)]
799 heads = [c.node() for c in repo.set(revset, headsbyphase[phase], phase)]
795 if heads:
800 if heads:
796 advanceboundary(repo, trgetter(), phase, heads)
801 advanceboundary(repo, trgetter(), phase, heads)
797
802
798
803
799 def analyzeremotephases(repo, subset, roots):
804 def analyzeremotephases(repo, subset, roots):
800 """Compute phases heads and root in a subset of node from root dict
805 """Compute phases heads and root in a subset of node from root dict
801
806
802 * subset is heads of the subset
807 * subset is heads of the subset
803 * roots is {<nodeid> => phase} mapping. key and value are string.
808 * roots is {<nodeid> => phase} mapping. key and value are string.
804
809
805 Accept unknown element input
810 Accept unknown element input
806 """
811 """
807 repo = repo.unfiltered()
812 repo = repo.unfiltered()
808 # build list from dictionary
813 # build list from dictionary
809 draftroots = []
814 draftroots = []
810 has_node = repo.changelog.index.has_node # to filter unknown nodes
815 has_node = repo.changelog.index.has_node # to filter unknown nodes
811 for nhex, phase in pycompat.iteritems(roots):
816 for nhex, phase in pycompat.iteritems(roots):
812 if nhex == b'publishing': # ignore data related to publish option
817 if nhex == b'publishing': # ignore data related to publish option
813 continue
818 continue
814 node = bin(nhex)
819 node = bin(nhex)
815 phase = int(phase)
820 phase = int(phase)
816 if phase == public:
821 if phase == public:
817 if node != nullid:
822 if node != nullid:
818 repo.ui.warn(
823 repo.ui.warn(
819 _(
824 _(
820 b'ignoring inconsistent public root'
825 b'ignoring inconsistent public root'
821 b' from remote: %s\n'
826 b' from remote: %s\n'
822 )
827 )
823 % nhex
828 % nhex
824 )
829 )
825 elif phase == draft:
830 elif phase == draft:
826 if has_node(node):
831 if has_node(node):
827 draftroots.append(node)
832 draftroots.append(node)
828 else:
833 else:
829 repo.ui.warn(
834 repo.ui.warn(
830 _(b'ignoring unexpected root from remote: %i %s\n')
835 _(b'ignoring unexpected root from remote: %i %s\n')
831 % (phase, nhex)
836 % (phase, nhex)
832 )
837 )
833 # compute heads
838 # compute heads
834 publicheads = newheads(repo, subset, draftroots)
839 publicheads = newheads(repo, subset, draftroots)
835 return publicheads, draftroots
840 return publicheads, draftroots
836
841
837
842
838 class remotephasessummary(object):
843 class remotephasessummary(object):
839 """summarize phase information on the remote side
844 """summarize phase information on the remote side
840
845
841 :publishing: True is the remote is publishing
846 :publishing: True is the remote is publishing
842 :publicheads: list of remote public phase heads (nodes)
847 :publicheads: list of remote public phase heads (nodes)
843 :draftheads: list of remote draft phase heads (nodes)
848 :draftheads: list of remote draft phase heads (nodes)
844 :draftroots: list of remote draft phase root (nodes)
849 :draftroots: list of remote draft phase root (nodes)
845 """
850 """
846
851
847 def __init__(self, repo, remotesubset, remoteroots):
852 def __init__(self, repo, remotesubset, remoteroots):
848 unfi = repo.unfiltered()
853 unfi = repo.unfiltered()
849 self._allremoteroots = remoteroots
854 self._allremoteroots = remoteroots
850
855
851 self.publishing = remoteroots.get(b'publishing', False)
856 self.publishing = remoteroots.get(b'publishing', False)
852
857
853 ana = analyzeremotephases(repo, remotesubset, remoteroots)
858 ana = analyzeremotephases(repo, remotesubset, remoteroots)
854 self.publicheads, self.draftroots = ana
859 self.publicheads, self.draftroots = ana
855 # Get the list of all "heads" revs draft on remote
860 # Get the list of all "heads" revs draft on remote
856 dheads = unfi.set(b'heads(%ln::%ln)', self.draftroots, remotesubset)
861 dheads = unfi.set(b'heads(%ln::%ln)', self.draftroots, remotesubset)
857 self.draftheads = [c.node() for c in dheads]
862 self.draftheads = [c.node() for c in dheads]
858
863
859
864
860 def newheads(repo, heads, roots):
865 def newheads(repo, heads, roots):
861 """compute new head of a subset minus another
866 """compute new head of a subset minus another
862
867
863 * `heads`: define the first subset
868 * `heads`: define the first subset
864 * `roots`: define the second we subtract from the first"""
869 * `roots`: define the second we subtract from the first"""
865 # prevent an import cycle
870 # prevent an import cycle
866 # phases > dagop > patch > copies > scmutil > obsolete > obsutil > phases
871 # phases > dagop > patch > copies > scmutil > obsolete > obsutil > phases
867 from . import dagop
872 from . import dagop
868
873
869 repo = repo.unfiltered()
874 repo = repo.unfiltered()
870 cl = repo.changelog
875 cl = repo.changelog
871 rev = cl.index.get_rev
876 rev = cl.index.get_rev
872 if not roots:
877 if not roots:
873 return heads
878 return heads
874 if not heads or heads == [nullid]:
879 if not heads or heads == [nullid]:
875 return []
880 return []
876 # The logic operated on revisions, convert arguments early for convenience
881 # The logic operated on revisions, convert arguments early for convenience
877 new_heads = {rev(n) for n in heads if n != nullid}
882 new_heads = {rev(n) for n in heads if n != nullid}
878 roots = [rev(n) for n in roots]
883 roots = [rev(n) for n in roots]
879 # compute the area we need to remove
884 # compute the area we need to remove
880 affected_zone = repo.revs(b"(%ld::%ld)", roots, new_heads)
885 affected_zone = repo.revs(b"(%ld::%ld)", roots, new_heads)
881 # heads in the area are no longer heads
886 # heads in the area are no longer heads
882 new_heads.difference_update(affected_zone)
887 new_heads.difference_update(affected_zone)
883 # revisions in the area have children outside of it,
888 # revisions in the area have children outside of it,
884 # They might be new heads
889 # They might be new heads
885 candidates = repo.revs(
890 candidates = repo.revs(
886 b"parents(%ld + (%ld and merge())) and not null", roots, affected_zone
891 b"parents(%ld + (%ld and merge())) and not null", roots, affected_zone
887 )
892 )
888 candidates -= affected_zone
893 candidates -= affected_zone
889 if new_heads or candidates:
894 if new_heads or candidates:
890 # remove candidate that are ancestors of other heads
895 # remove candidate that are ancestors of other heads
891 new_heads.update(candidates)
896 new_heads.update(candidates)
892 prunestart = repo.revs(b"parents(%ld) and not null", new_heads)
897 prunestart = repo.revs(b"parents(%ld) and not null", new_heads)
893 pruned = dagop.reachableroots(repo, candidates, prunestart)
898 pruned = dagop.reachableroots(repo, candidates, prunestart)
894 new_heads.difference_update(pruned)
899 new_heads.difference_update(pruned)
895
900
896 return pycompat.maplist(cl.node, sorted(new_heads))
901 return pycompat.maplist(cl.node, sorted(new_heads))
897
902
898
903
899 def newcommitphase(ui):
904 def newcommitphase(ui):
900 """helper to get the target phase of new commit
905 """helper to get the target phase of new commit
901
906
902 Handle all possible values for the phases.new-commit options.
907 Handle all possible values for the phases.new-commit options.
903
908
904 """
909 """
905 v = ui.config(b'phases', b'new-commit')
910 v = ui.config(b'phases', b'new-commit')
906 try:
911 try:
907 return phasenumber2[v]
912 return phasenumber2[v]
908 except KeyError:
913 except KeyError:
909 raise error.ConfigError(
914 raise error.ConfigError(
910 _(b"phases.new-commit: not a valid phase name ('%s')") % v
915 _(b"phases.new-commit: not a valid phase name ('%s')") % v
911 )
916 )
912
917
913
918
914 def hassecret(repo):
919 def hassecret(repo):
915 """utility function that check if a repo have any secret changeset."""
920 """utility function that check if a repo have any secret changeset."""
916 return bool(repo._phasecache.phaseroots[secret])
921 return bool(repo._phasecache.phaseroots[secret])
917
922
918
923
919 def preparehookargs(node, old, new):
924 def preparehookargs(node, old, new):
920 if old is None:
925 if old is None:
921 old = b''
926 old = b''
922 else:
927 else:
923 old = phasenames[old]
928 old = phasenames[old]
924 return {b'node': node, b'oldphase': old, b'phase': phasenames[new]}
929 return {b'node': node, b'oldphase': old, b'phase': phasenames[new]}
@@ -1,157 +1,157 b''
1 # policy.py - module policy logic for Mercurial.
1 # policy.py - module policy logic for Mercurial.
2 #
2 #
3 # Copyright 2015 Gregory Szorc <gregory.szorc@gmail.com>
3 # Copyright 2015 Gregory Szorc <gregory.szorc@gmail.com>
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 os
10 import os
11 import sys
11 import sys
12
12
13 from .pycompat import getattr
13 from .pycompat import getattr
14
14
15 # Rules for how modules can be loaded. Values are:
15 # Rules for how modules can be loaded. Values are:
16 #
16 #
17 # c - require C extensions
17 # c - require C extensions
18 # rust+c - require Rust and C extensions
18 # rust+c - require Rust and C extensions
19 # rust+c-allow - allow Rust and C extensions with fallback to pure Python
19 # rust+c-allow - allow Rust and C extensions with fallback to pure Python
20 # for each
20 # for each
21 # allow - allow pure Python implementation when C loading fails
21 # allow - allow pure Python implementation when C loading fails
22 # cffi - required cffi versions (implemented within pure module)
22 # cffi - required cffi versions (implemented within pure module)
23 # cffi-allow - allow pure Python implementation if cffi version is missing
23 # cffi-allow - allow pure Python implementation if cffi version is missing
24 # py - only load pure Python modules
24 # py - only load pure Python modules
25 #
25 #
26 # By default, fall back to the pure modules so the in-place build can
26 # By default, fall back to the pure modules so the in-place build can
27 # run without recompiling the C extensions. This will be overridden by
27 # run without recompiling the C extensions. This will be overridden by
28 # __modulepolicy__ generated by setup.py.
28 # __modulepolicy__ generated by setup.py.
29 policy = b'allow'
29 policy = b'allow'
30 _packageprefs = {
30 _packageprefs = {
31 # policy: (versioned package, pure package)
31 # policy: (versioned package, pure package)
32 b'c': ('cext', None),
32 b'c': ('cext', None),
33 b'allow': ('cext', 'pure'),
33 b'allow': ('cext', 'pure'),
34 b'cffi': ('cffi', None),
34 b'cffi': ('cffi', None),
35 b'cffi-allow': ('cffi', 'pure'),
35 b'cffi-allow': ('cffi', 'pure'),
36 b'py': (None, 'pure'),
36 b'py': (None, 'pure'),
37 # For now, rust policies impact importrust only
37 # For now, rust policies impact importrust only
38 b'rust+c': ('cext', None),
38 b'rust+c': ('cext', None),
39 b'rust+c-allow': ('cext', 'pure'),
39 b'rust+c-allow': ('cext', 'pure'),
40 }
40 }
41
41
42 try:
42 try:
43 from . import __modulepolicy__
43 from . import __modulepolicy__
44
44
45 policy = __modulepolicy__.modulepolicy
45 policy = __modulepolicy__.modulepolicy
46 except ImportError:
46 except ImportError:
47 pass
47 pass
48
48
49 # PyPy doesn't load C extensions.
49 # PyPy doesn't load C extensions.
50 #
50 #
51 # The canonical way to do this is to test platform.python_implementation().
51 # The canonical way to do this is to test platform.python_implementation().
52 # But we don't import platform and don't bloat for it here.
52 # But we don't import platform and don't bloat for it here.
53 if '__pypy__' in sys.builtin_module_names:
53 if '__pypy__' in sys.builtin_module_names:
54 policy = b'cffi'
54 policy = b'cffi'
55
55
56 # Environment variable can always force settings.
56 # Environment variable can always force settings.
57 if sys.version_info[0] >= 3:
57 if sys.version_info[0] >= 3:
58 if 'HGMODULEPOLICY' in os.environ:
58 if 'HGMODULEPOLICY' in os.environ:
59 policy = os.environ['HGMODULEPOLICY'].encode('utf-8')
59 policy = os.environ['HGMODULEPOLICY'].encode('utf-8')
60 else:
60 else:
61 policy = os.environ.get('HGMODULEPOLICY', policy)
61 policy = os.environ.get('HGMODULEPOLICY', policy)
62
62
63
63
64 def _importfrom(pkgname, modname):
64 def _importfrom(pkgname, modname):
65 # from .<pkgname> import <modname> (where . is looked through this module)
65 # from .<pkgname> import <modname> (where . is looked through this module)
66 fakelocals = {}
66 fakelocals = {}
67 pkg = __import__(pkgname, globals(), fakelocals, [modname], level=1)
67 pkg = __import__(pkgname, globals(), fakelocals, [modname], level=1)
68 try:
68 try:
69 fakelocals[modname] = mod = getattr(pkg, modname)
69 fakelocals[modname] = mod = getattr(pkg, modname)
70 except AttributeError:
70 except AttributeError:
71 raise ImportError('cannot import name %s' % modname)
71 raise ImportError('cannot import name %s' % modname)
72 # force import; fakelocals[modname] may be replaced with the real module
72 # force import; fakelocals[modname] may be replaced with the real module
73 getattr(mod, '__doc__', None)
73 getattr(mod, '__doc__', None)
74 return fakelocals[modname]
74 return fakelocals[modname]
75
75
76
76
77 # keep in sync with "version" in C modules
77 # keep in sync with "version" in C modules
78 _cextversions = {
78 _cextversions = {
79 ('cext', 'base85'): 1,
79 ('cext', 'base85'): 1,
80 ('cext', 'bdiff'): 3,
80 ('cext', 'bdiff'): 3,
81 ('cext', 'mpatch'): 1,
81 ('cext', 'mpatch'): 1,
82 ('cext', 'osutil'): 4,
82 ('cext', 'osutil'): 4,
83 ('cext', 'parsers'): 16,
83 ('cext', 'parsers'): 17,
84 }
84 }
85
85
86 # map import request to other package or module
86 # map import request to other package or module
87 _modredirects = {
87 _modredirects = {
88 ('cext', 'charencode'): ('cext', 'parsers'),
88 ('cext', 'charencode'): ('cext', 'parsers'),
89 ('cffi', 'base85'): ('pure', 'base85'),
89 ('cffi', 'base85'): ('pure', 'base85'),
90 ('cffi', 'charencode'): ('pure', 'charencode'),
90 ('cffi', 'charencode'): ('pure', 'charencode'),
91 ('cffi', 'parsers'): ('pure', 'parsers'),
91 ('cffi', 'parsers'): ('pure', 'parsers'),
92 }
92 }
93
93
94
94
95 def _checkmod(pkgname, modname, mod):
95 def _checkmod(pkgname, modname, mod):
96 expected = _cextversions.get((pkgname, modname))
96 expected = _cextversions.get((pkgname, modname))
97 actual = getattr(mod, 'version', None)
97 actual = getattr(mod, 'version', None)
98 if actual != expected:
98 if actual != expected:
99 raise ImportError(
99 raise ImportError(
100 'cannot import module %s.%s '
100 'cannot import module %s.%s '
101 '(expected version: %d, actual: %r)'
101 '(expected version: %d, actual: %r)'
102 % (pkgname, modname, expected, actual)
102 % (pkgname, modname, expected, actual)
103 )
103 )
104
104
105
105
106 def importmod(modname):
106 def importmod(modname):
107 """Import module according to policy and check API version"""
107 """Import module according to policy and check API version"""
108 try:
108 try:
109 verpkg, purepkg = _packageprefs[policy]
109 verpkg, purepkg = _packageprefs[policy]
110 except KeyError:
110 except KeyError:
111 raise ImportError('invalid HGMODULEPOLICY %r' % policy)
111 raise ImportError('invalid HGMODULEPOLICY %r' % policy)
112 assert verpkg or purepkg
112 assert verpkg or purepkg
113 if verpkg:
113 if verpkg:
114 pn, mn = _modredirects.get((verpkg, modname), (verpkg, modname))
114 pn, mn = _modredirects.get((verpkg, modname), (verpkg, modname))
115 try:
115 try:
116 mod = _importfrom(pn, mn)
116 mod = _importfrom(pn, mn)
117 if pn == verpkg:
117 if pn == verpkg:
118 _checkmod(pn, mn, mod)
118 _checkmod(pn, mn, mod)
119 return mod
119 return mod
120 except ImportError:
120 except ImportError:
121 if not purepkg:
121 if not purepkg:
122 raise
122 raise
123 pn, mn = _modredirects.get((purepkg, modname), (purepkg, modname))
123 pn, mn = _modredirects.get((purepkg, modname), (purepkg, modname))
124 return _importfrom(pn, mn)
124 return _importfrom(pn, mn)
125
125
126
126
127 def _isrustpermissive():
127 def _isrustpermissive():
128 """Assuming the policy is a Rust one, tell if it's permissive."""
128 """Assuming the policy is a Rust one, tell if it's permissive."""
129 return policy.endswith(b'-allow')
129 return policy.endswith(b'-allow')
130
130
131
131
132 def importrust(modname, member=None, default=None):
132 def importrust(modname, member=None, default=None):
133 """Import Rust module according to policy and availability.
133 """Import Rust module according to policy and availability.
134
134
135 If policy isn't a Rust one, this returns `default`.
135 If policy isn't a Rust one, this returns `default`.
136
136
137 If either the module or its member is not available, this returns `default`
137 If either the module or its member is not available, this returns `default`
138 if policy is permissive and raises `ImportError` if not.
138 if policy is permissive and raises `ImportError` if not.
139 """
139 """
140 if not policy.startswith(b'rust'):
140 if not policy.startswith(b'rust'):
141 return default
141 return default
142
142
143 try:
143 try:
144 mod = _importfrom('rustext', modname)
144 mod = _importfrom('rustext', modname)
145 except ImportError:
145 except ImportError:
146 if _isrustpermissive():
146 if _isrustpermissive():
147 return default
147 return default
148 raise
148 raise
149 if member is None:
149 if member is None:
150 return mod
150 return mod
151
151
152 try:
152 try:
153 return getattr(mod, member)
153 return getattr(mod, member)
154 except AttributeError:
154 except AttributeError:
155 if _isrustpermissive():
155 if _isrustpermissive():
156 return default
156 return default
157 raise ImportError("Cannot import name %s" % member)
157 raise ImportError("Cannot import name %s" % member)
@@ -1,223 +1,223 b''
1 revlog.parseindex must be able to parse the index file even if
1 revlog.parseindex must be able to parse the index file even if
2 an index entry is split between two 64k blocks. The ideal test
2 an index entry is split between two 64k blocks. The ideal test
3 would be to create an index file with inline data where
3 would be to create an index file with inline data where
4 64k < size < 64k + 64 (64k is the size of the read buffer, 64 is
4 64k < size < 64k + 64 (64k is the size of the read buffer, 64 is
5 the size of an index entry) and with an index entry starting right
5 the size of an index entry) and with an index entry starting right
6 before the 64k block boundary, and try to read it.
6 before the 64k block boundary, and try to read it.
7 We approximate that by reducing the read buffer to 1 byte.
7 We approximate that by reducing the read buffer to 1 byte.
8
8
9 $ hg init a
9 $ hg init a
10 $ cd a
10 $ cd a
11 $ echo abc > foo
11 $ echo abc > foo
12 $ hg add foo
12 $ hg add foo
13 $ hg commit -m 'add foo'
13 $ hg commit -m 'add foo'
14 $ echo >> foo
14 $ echo >> foo
15 $ hg commit -m 'change foo'
15 $ hg commit -m 'change foo'
16 $ hg log -r 0:
16 $ hg log -r 0:
17 changeset: 0:7c31755bf9b5
17 changeset: 0:7c31755bf9b5
18 user: test
18 user: test
19 date: Thu Jan 01 00:00:00 1970 +0000
19 date: Thu Jan 01 00:00:00 1970 +0000
20 summary: add foo
20 summary: add foo
21
21
22 changeset: 1:26333235a41c
22 changeset: 1:26333235a41c
23 tag: tip
23 tag: tip
24 user: test
24 user: test
25 date: Thu Jan 01 00:00:00 1970 +0000
25 date: Thu Jan 01 00:00:00 1970 +0000
26 summary: change foo
26 summary: change foo
27
27
28 $ cat >> test.py << EOF
28 $ cat >> test.py << EOF
29 > from __future__ import print_function
29 > from __future__ import print_function
30 > from mercurial import changelog, node, pycompat, vfs
30 > from mercurial import changelog, node, pycompat, vfs
31 >
31 >
32 > class singlebyteread(object):
32 > class singlebyteread(object):
33 > def __init__(self, real):
33 > def __init__(self, real):
34 > self.real = real
34 > self.real = real
35 >
35 >
36 > def read(self, size=-1):
36 > def read(self, size=-1):
37 > if size == 65536:
37 > if size == 65536:
38 > size = 1
38 > size = 1
39 > return self.real.read(size)
39 > return self.real.read(size)
40 >
40 >
41 > def __getattr__(self, key):
41 > def __getattr__(self, key):
42 > return getattr(self.real, key)
42 > return getattr(self.real, key)
43 >
43 >
44 > def __enter__(self):
44 > def __enter__(self):
45 > self.real.__enter__()
45 > self.real.__enter__()
46 > return self
46 > return self
47 >
47 >
48 > def __exit__(self, *args, **kwargs):
48 > def __exit__(self, *args, **kwargs):
49 > return self.real.__exit__(*args, **kwargs)
49 > return self.real.__exit__(*args, **kwargs)
50 >
50 >
51 > def opener(*args):
51 > def opener(*args):
52 > o = vfs.vfs(*args)
52 > o = vfs.vfs(*args)
53 > def wrapper(*a, **kwargs):
53 > def wrapper(*a, **kwargs):
54 > f = o(*a, **kwargs)
54 > f = o(*a, **kwargs)
55 > return singlebyteread(f)
55 > return singlebyteread(f)
56 > wrapper.options = o.options
56 > wrapper.options = o.options
57 > return wrapper
57 > return wrapper
58 >
58 >
59 > cl = changelog.changelog(opener(b'.hg/store'))
59 > cl = changelog.changelog(opener(b'.hg/store'))
60 > print(len(cl), 'revisions:')
60 > print(len(cl), 'revisions:')
61 > for r in cl:
61 > for r in cl:
62 > print(pycompat.sysstr(node.short(cl.node(r))))
62 > print(pycompat.sysstr(node.short(cl.node(r))))
63 > EOF
63 > EOF
64 $ "$PYTHON" test.py
64 $ "$PYTHON" test.py
65 2 revisions:
65 2 revisions:
66 7c31755bf9b5
66 7c31755bf9b5
67 26333235a41c
67 26333235a41c
68
68
69 $ cd ..
69 $ cd ..
70
70
71 #if no-pure
71 #if no-pure
72
72
73 Test SEGV caused by bad revision passed to reachableroots() (issue4775):
73 Test SEGV caused by bad revision passed to reachableroots() (issue4775):
74
74
75 $ cd a
75 $ cd a
76
76
77 $ "$PYTHON" <<EOF
77 $ "$PYTHON" <<EOF
78 > from __future__ import print_function
78 > from __future__ import print_function
79 > from mercurial import changelog, vfs
79 > from mercurial import changelog, vfs
80 > cl = changelog.changelog(vfs.vfs(b'.hg/store'))
80 > cl = changelog.changelog(vfs.vfs(b'.hg/store'))
81 > print('good heads:')
81 > print('good heads:')
82 > for head in [0, len(cl) - 1, -1]:
82 > for head in [0, len(cl) - 1, -1]:
83 > print('%s: %r' % (head, cl.reachableroots(0, [head], [0])))
83 > print('%s: %r' % (head, cl.reachableroots(0, [head], [0])))
84 > print('bad heads:')
84 > print('bad heads:')
85 > for head in [len(cl), 10000, -2, -10000, None]:
85 > for head in [len(cl), 10000, -2, -10000, None]:
86 > print('%s:' % head, end=' ')
86 > print('%s:' % head, end=' ')
87 > try:
87 > try:
88 > cl.reachableroots(0, [head], [0])
88 > cl.reachableroots(0, [head], [0])
89 > print('uncaught buffer overflow?')
89 > print('uncaught buffer overflow?')
90 > except (IndexError, TypeError) as inst:
90 > except (IndexError, TypeError) as inst:
91 > print(inst)
91 > print(inst)
92 > print('good roots:')
92 > print('good roots:')
93 > for root in [0, len(cl) - 1, -1]:
93 > for root in [0, len(cl) - 1, -1]:
94 > print('%s: %r' % (root, cl.reachableroots(root, [len(cl) - 1], [root])))
94 > print('%s: %r' % (root, cl.reachableroots(root, [len(cl) - 1], [root])))
95 > print('out-of-range roots are ignored:')
95 > print('out-of-range roots are ignored:')
96 > for root in [len(cl), 10000, -2, -10000]:
96 > for root in [len(cl), 10000, -2, -10000]:
97 > print('%s: %r' % (root, cl.reachableroots(root, [len(cl) - 1], [root])))
97 > print('%s: %r' % (root, cl.reachableroots(root, [len(cl) - 1], [root])))
98 > print('bad roots:')
98 > print('bad roots:')
99 > for root in [None]:
99 > for root in [None]:
100 > print('%s:' % root, end=' ')
100 > print('%s:' % root, end=' ')
101 > try:
101 > try:
102 > cl.reachableroots(root, [len(cl) - 1], [root])
102 > cl.reachableroots(root, [len(cl) - 1], [root])
103 > print('uncaught error?')
103 > print('uncaught error?')
104 > except TypeError as inst:
104 > except TypeError as inst:
105 > print(inst)
105 > print(inst)
106 > EOF
106 > EOF
107 good heads:
107 good heads:
108 0: [0]
108 0: [0]
109 1: [0]
109 1: [0]
110 -1: []
110 -1: []
111 bad heads:
111 bad heads:
112 2: head out of range
112 2: head out of range
113 10000: head out of range
113 10000: head out of range
114 -2: head out of range
114 -2: head out of range
115 -10000: head out of range
115 -10000: head out of range
116 None: an integer is required( .got type NoneType.)? (re)
116 None: an integer is required( .got type NoneType.)? (re)
117 good roots:
117 good roots:
118 0: [0]
118 0: [0]
119 1: [1]
119 1: [1]
120 -1: [-1]
120 -1: [-1]
121 out-of-range roots are ignored:
121 out-of-range roots are ignored:
122 2: []
122 2: []
123 10000: []
123 10000: []
124 -2: []
124 -2: []
125 -10000: []
125 -10000: []
126 bad roots:
126 bad roots:
127 None: an integer is required( .got type NoneType.)? (re)
127 None: an integer is required( .got type NoneType.)? (re)
128
128
129 $ cd ..
129 $ cd ..
130
130
131 Test corrupted p1/p2 fields that could cause SEGV at parsers.c:
131 Test corrupted p1/p2 fields that could cause SEGV at parsers.c:
132
132
133 $ mkdir invalidparent
133 $ mkdir invalidparent
134 $ cd invalidparent
134 $ cd invalidparent
135
135
136 $ hg clone --pull -q --config phases.publish=False ../a limit --config format.sparse-revlog=no
136 $ hg clone --pull -q --config phases.publish=False ../a limit --config format.sparse-revlog=no
137 $ hg clone --pull -q --config phases.publish=False ../a neglimit --config format.sparse-revlog=no
137 $ hg clone --pull -q --config phases.publish=False ../a neglimit --config format.sparse-revlog=no
138 $ hg clone --pull -q --config phases.publish=False ../a segv --config format.sparse-revlog=no
138 $ hg clone --pull -q --config phases.publish=False ../a segv --config format.sparse-revlog=no
139 $ rm -R limit/.hg/cache neglimit/.hg/cache segv/.hg/cache
139 $ rm -R limit/.hg/cache neglimit/.hg/cache segv/.hg/cache
140
140
141 $ "$PYTHON" <<EOF
141 $ "$PYTHON" <<EOF
142 > data = open("limit/.hg/store/00changelog.i", "rb").read()
142 > data = open("limit/.hg/store/00changelog.i", "rb").read()
143 > poisons = [
143 > poisons = [
144 > (b'limit', b'\0\0\0\x02'),
144 > (b'limit', b'\0\0\0\x02'),
145 > (b'neglimit', b'\xff\xff\xff\xfe'),
145 > (b'neglimit', b'\xff\xff\xff\xfe'),
146 > (b'segv', b'\0\x01\0\0'),
146 > (b'segv', b'\0\x01\0\0'),
147 > ]
147 > ]
148 > for n, p in poisons:
148 > for n, p in poisons:
149 > # corrupt p1 at rev0 and p2 at rev1
149 > # corrupt p1 at rev0 and p2 at rev1
150 > d = data[:24] + p + data[28:127 + 28] + p + data[127 + 32:]
150 > d = data[:24] + p + data[28:127 + 28] + p + data[127 + 32:]
151 > open(n + b"/.hg/store/00changelog.i", "wb").write(d)
151 > open(n + b"/.hg/store/00changelog.i", "wb").write(d)
152 > EOF
152 > EOF
153
153
154 $ hg -R limit debugrevlogindex -f1 -c
154 $ hg -R limit debugrevlogindex -f1 -c
155 rev flag size link p1 p2 nodeid
155 rev flag size link p1 p2 nodeid
156 0 0000 62 0 2 -1 7c31755bf9b5
156 0 0000 62 0 2 -1 7c31755bf9b5
157 1 0000 65 1 0 2 26333235a41c
157 1 0000 65 1 0 2 26333235a41c
158
158
159 $ hg -R limit debugdeltachain -c
159 $ hg -R limit debugdeltachain -c
160 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio
160 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio
161 0 1 1 -1 base 63 62 63 1.01613 63 0 0.00000
161 0 1 1 -1 base 63 62 63 1.01613 63 0 0.00000
162 1 2 1 -1 base 66 65 66 1.01538 66 0 0.00000
162 1 2 1 -1 base 66 65 66 1.01538 66 0 0.00000
163
163
164 $ hg -R neglimit debugrevlogindex -f1 -c
164 $ hg -R neglimit debugrevlogindex -f1 -c
165 rev flag size link p1 p2 nodeid
165 rev flag size link p1 p2 nodeid
166 0 0000 62 0 -2 -1 7c31755bf9b5
166 0 0000 62 0 -2 -1 7c31755bf9b5
167 1 0000 65 1 0 -2 26333235a41c
167 1 0000 65 1 0 -2 26333235a41c
168
168
169 $ hg -R segv debugrevlogindex -f1 -c
169 $ hg -R segv debugrevlogindex -f1 -c
170 rev flag size link p1 p2 nodeid
170 rev flag size link p1 p2 nodeid
171 0 0000 62 0 65536 -1 7c31755bf9b5
171 0 0000 62 0 65536 -1 7c31755bf9b5
172 1 0000 65 1 0 65536 26333235a41c
172 1 0000 65 1 0 65536 26333235a41c
173
173
174 $ hg -R segv debugdeltachain -c
174 $ hg -R segv debugdeltachain -c
175 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio
175 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio
176 0 1 1 -1 base 63 62 63 1.01613 63 0 0.00000
176 0 1 1 -1 base 63 62 63 1.01613 63 0 0.00000
177 1 2 1 -1 base 66 65 66 1.01538 66 0 0.00000
177 1 2 1 -1 base 66 65 66 1.01538 66 0 0.00000
178
178
179 $ cat <<EOF > test.py
179 $ cat <<EOF > test.py
180 > from __future__ import print_function
180 > from __future__ import print_function
181 > import sys
181 > import sys
182 > from mercurial import changelog, pycompat, vfs
182 > from mercurial import changelog, pycompat, vfs
183 > cl = changelog.changelog(vfs.vfs(pycompat.fsencode(sys.argv[1])))
183 > cl = changelog.changelog(vfs.vfs(pycompat.fsencode(sys.argv[1])))
184 > n0, n1 = cl.node(0), cl.node(1)
184 > n0, n1 = cl.node(0), cl.node(1)
185 > ops = [
185 > ops = [
186 > ('reachableroots',
186 > ('reachableroots',
187 > lambda: cl.index.reachableroots2(0, [1], [0], False)),
187 > lambda: cl.index.reachableroots2(0, [1], [0], False)),
188 > ('compute_phases_map_sets', lambda: cl.computephases([[0], []])),
188 > ('compute_phases_map_sets', lambda: cl.computephases({1: {cl.node(0)}})),
189 > ('index_headrevs', lambda: cl.headrevs()),
189 > ('index_headrevs', lambda: cl.headrevs()),
190 > ('find_gca_candidates', lambda: cl.commonancestorsheads(n0, n1)),
190 > ('find_gca_candidates', lambda: cl.commonancestorsheads(n0, n1)),
191 > ('find_deepest', lambda: cl.ancestor(n0, n1)),
191 > ('find_deepest', lambda: cl.ancestor(n0, n1)),
192 > ]
192 > ]
193 > for l, f in ops:
193 > for l, f in ops:
194 > print(l + ':', end=' ')
194 > print(l + ':', end=' ')
195 > try:
195 > try:
196 > f()
196 > f()
197 > print('uncaught buffer overflow?')
197 > print('uncaught buffer overflow?')
198 > except ValueError as inst:
198 > except ValueError as inst:
199 > print(inst)
199 > print(inst)
200 > EOF
200 > EOF
201
201
202 $ "$PYTHON" test.py limit/.hg/store
202 $ "$PYTHON" test.py limit/.hg/store
203 reachableroots: parent out of range
203 reachableroots: parent out of range
204 compute_phases_map_sets: parent out of range
204 compute_phases_map_sets: parent out of range
205 index_headrevs: parent out of range
205 index_headrevs: parent out of range
206 find_gca_candidates: parent out of range
206 find_gca_candidates: parent out of range
207 find_deepest: parent out of range
207 find_deepest: parent out of range
208 $ "$PYTHON" test.py neglimit/.hg/store
208 $ "$PYTHON" test.py neglimit/.hg/store
209 reachableroots: parent out of range
209 reachableroots: parent out of range
210 compute_phases_map_sets: parent out of range
210 compute_phases_map_sets: parent out of range
211 index_headrevs: parent out of range
211 index_headrevs: parent out of range
212 find_gca_candidates: parent out of range
212 find_gca_candidates: parent out of range
213 find_deepest: parent out of range
213 find_deepest: parent out of range
214 $ "$PYTHON" test.py segv/.hg/store
214 $ "$PYTHON" test.py segv/.hg/store
215 reachableroots: parent out of range
215 reachableroots: parent out of range
216 compute_phases_map_sets: parent out of range
216 compute_phases_map_sets: parent out of range
217 index_headrevs: parent out of range
217 index_headrevs: parent out of range
218 find_gca_candidates: parent out of range
218 find_gca_candidates: parent out of range
219 find_deepest: parent out of range
219 find_deepest: parent out of range
220
220
221 $ cd ..
221 $ cd ..
222
222
223 #endif
223 #endif
General Comments 0
You need to be logged in to leave comments. Login now