comparison mercurial/parsers.c @ 26630:3111b45a2bbf

parsers: make pack_dirstate take now in integer for consistency On recent OS, 'stat.st_mtime' has a double precision floating point value to represent nano seconds, but it is not wide enough for actual file timestamp: nowadays, only 52 - 32 = 20 bit width is available for decimal places in sec. Therefore, casting it to 'int' may cause unexpected result. See also changeset 13272104bb07 fixing issue4836 for detail. For example, changed file A may be treated as "clean" unexpectedly in steps below. "rounded now" is the value gotten by rounding via 'int(st.st_mtime)' or so. ---------------------+--------------------+------------------------ "now" | | timestamp of A (time_t) float rounded time_t| action | FS dirstate ------ ------- ------+--------------------+-------- --------------- N+.nnn N N | | --- --- | update file A | N | dirstate.normal(A) | N N+.999 N+1 N | | | dirstate.write() | N (*1) | : | | change file A | N | : | N+1.00 N+1 N+1 | | | "hg status" (*2) | N N ------ ------- ------+--------------------+-------- --------------- Timestamp N of A in dirstate isn't dropped at (*1), because "rounded now" is N+1 at that time, even if 'st_mtime' in 'time_t' is still N. Then, file A is unexpectedly treated as "clean" at (*2) in this case. For consistent handling of 'stat.st_mtime', this patch makes 'pack_dirstate()' take 'now' argument not in floating point but in integer. This patch makes 'PyArg_ParseTuple()' in 'pack_dirstate()' use format 'i' (= checking type mismatch or overflow), even though it is ensured that 'now' is in the range of 32bit signed integer by masking with '_rangemask' (= 0x7fffffff) on caller side. It should be cheaper enough than packing itself, and useful to detect that legacy code invokes 'pack_dirstate()' with 'now' in floating point value.
author FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
date Wed, 14 Oct 2015 02:40:04 +0900
parents 042344313939
children 04ab2348efd1
comparison
equal deleted inserted replaced
26629:ae5f7be2b4ab 26630:3111b45a2bbf
549 PyObject *packobj = NULL; 549 PyObject *packobj = NULL;
550 PyObject *map, *copymap, *pl, *mtime_unset = NULL; 550 PyObject *map, *copymap, *pl, *mtime_unset = NULL;
551 Py_ssize_t nbytes, pos, l; 551 Py_ssize_t nbytes, pos, l;
552 PyObject *k, *v = NULL, *pn; 552 PyObject *k, *v = NULL, *pn;
553 char *p, *s; 553 char *p, *s;
554 double now; 554 int now;
555 555
556 if (!PyArg_ParseTuple(args, "O!O!Od:pack_dirstate", 556 if (!PyArg_ParseTuple(args, "O!O!Oi:pack_dirstate",
557 &PyDict_Type, &map, &PyDict_Type, &copymap, 557 &PyDict_Type, &map, &PyDict_Type, &copymap,
558 &pl, &now)) 558 &pl, &now))
559 return NULL; 559 return NULL;
560 560
561 if (!PySequence_Check(pl) || PySequence_Size(pl) != 2) { 561 if (!PySequence_Check(pl) || PySequence_Size(pl) != 2) {
620 620
621 state = tuple->state; 621 state = tuple->state;
622 mode = tuple->mode; 622 mode = tuple->mode;
623 size = tuple->size; 623 size = tuple->size;
624 mtime = tuple->mtime; 624 mtime = tuple->mtime;
625 if (state == 'n' && mtime == (uint32_t)now) { 625 if (state == 'n' && mtime == now) {
626 /* See pure/parsers.py:pack_dirstate for why we do 626 /* See pure/parsers.py:pack_dirstate for why we do
627 * this. */ 627 * this. */
628 mtime = -1; 628 mtime = -1;
629 mtime_unset = (PyObject *)make_dirstate_tuple( 629 mtime_unset = (PyObject *)make_dirstate_tuple(
630 state, mode, size, mtime); 630 state, mode, size, mtime);