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