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