Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/util.py @ 29200:ca4065028e00
util: add filestat class to detect ambiguity of file stat
Current posix.cachestat implementation might overlook change of a
file, if changing keeps ctime, mtime and size of file. Comparison of
inode number also overlooks changing in such situation, because inode
number is rapidly reused.
Contents of a file cached before changing isn't invalidated as
expected, if change of a file is overlooked for this "ambiguity" of
file stat.
This patch adds filestat class to detect ambiguity of file stat.
This patch is a part of preparation for "Exact Cache Validation Plan":
https://www.mercurial-scm.org/wiki/ExactCacheValidationPlan
author | FUJIWARA Katsunori <foozy@lares.dti.ne.jp> |
---|---|
date | Thu, 19 May 2016 00:20:37 +0900 |
parents | 07be86828e79 |
children | a109bf7e0dc2 |
comparison
equal
deleted
inserted
replaced
29199:daff05dcd184 | 29200:ca4065028e00 |
---|---|
1379 try: os.unlink(temp) | 1379 try: os.unlink(temp) |
1380 except OSError: pass | 1380 except OSError: pass |
1381 raise | 1381 raise |
1382 return temp | 1382 return temp |
1383 | 1383 |
1384 class filestat(object): | |
1385 """help to exactly detect change of a file | |
1386 | |
1387 'stat' attribute is result of 'os.stat()' if specified 'path' | |
1388 exists. Otherwise, it is None. This can avoid preparative | |
1389 'exists()' examination on client side of this class. | |
1390 """ | |
1391 def __init__(self, path): | |
1392 try: | |
1393 self.stat = os.stat(path) | |
1394 except OSError as err: | |
1395 if err.errno != errno.ENOENT: | |
1396 raise | |
1397 self.stat = None | |
1398 | |
1399 __hash__ = object.__hash__ | |
1400 | |
1401 def __eq__(self, old): | |
1402 try: | |
1403 # if ambiguity between stat of new and old file is | |
1404 # avoided, comparision of size, ctime and mtime is enough | |
1405 # to exactly detect change of a file regardless of platform | |
1406 return (self.stat.st_size == old.stat.st_size and | |
1407 self.stat.st_ctime == old.stat.st_ctime and | |
1408 self.stat.st_mtime == old.stat.st_mtime) | |
1409 except AttributeError: | |
1410 return False | |
1411 | |
1412 def isambig(self, old): | |
1413 """Examine whether new (= self) stat is ambiguous against old one | |
1414 | |
1415 "S[N]" below means stat of a file at N-th change: | |
1416 | |
1417 - S[n-1].ctime < S[n].ctime: can detect change of a file | |
1418 - S[n-1].ctime == S[n].ctime | |
1419 - S[n-1].ctime < S[n].mtime: means natural advancing (*1) | |
1420 - S[n-1].ctime == S[n].mtime: is ambiguous (*2) | |
1421 - S[n-1].ctime > S[n].mtime: never occurs naturally (don't care) | |
1422 - S[n-1].ctime > S[n].ctime: never occurs naturally (don't care) | |
1423 | |
1424 Case (*2) above means that a file was changed twice or more at | |
1425 same time in sec (= S[n-1].ctime), and comparison of timestamp | |
1426 is ambiguous. | |
1427 | |
1428 Base idea to avoid such ambiguity is "advance mtime 1 sec, if | |
1429 timestamp is ambiguous". | |
1430 | |
1431 But advancing mtime only in case (*2) doesn't work as | |
1432 expected, because naturally advanced S[n].mtime in case (*1) | |
1433 might be equal to manually advanced S[n-1 or earlier].mtime. | |
1434 | |
1435 Therefore, all "S[n-1].ctime == S[n].ctime" cases should be | |
1436 treated as ambiguous regardless of mtime, to avoid overlooking | |
1437 by confliction between such mtime. | |
1438 | |
1439 Advancing mtime "if isambig(oldstat)" ensures "S[n-1].mtime != | |
1440 S[n].mtime", even if size of a file isn't changed. | |
1441 """ | |
1442 try: | |
1443 return (self.stat.st_ctime == old.stat.st_ctime) | |
1444 except AttributeError: | |
1445 return False | |
1446 | |
1384 class atomictempfile(object): | 1447 class atomictempfile(object): |
1385 '''writable file object that atomically updates a file | 1448 '''writable file object that atomically updates a file |
1386 | 1449 |
1387 All writes will go to a temporary copy of the original file. Call | 1450 All writes will go to a temporary copy of the original file. Call |
1388 close() when you are done writing, and atomictempfile will rename | 1451 close() when you are done writing, and atomictempfile will rename |