Mercurial > hg
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, ©map, | 557 &PyDict_Type, &map, &PyDict_Type, ©map, |
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); |