util: make copyfile avoid ambiguity of file stat if needed
In some cases below, copying from backup is used to restore original
contents of a file. If copying keeps ctime, mtime and size of a file,
restoring is overlooked, and old contents cached before restoring
isn't invalidated as expected.
- failure of transaction before closing (from '.hg/journal.backup.*')
- rollback of previous transaction (from '.hg/undo.backup.*')
To avoid such problem, this patch makes copyfile() avoid ambiguity of
file stat, if needed.
Ambiguity check is executed, only if:
- checkambig=True is specified (not all copying needs ambiguity check), and
- destination file exists before copying
This patch also adds 'not (copystat and checkambig)' assertion,
because combination of copystat and checkambig is meaningless.
This patch is a part of preparation for "Exact Cache Validation Plan":
https://www.mercurial-scm.org/wiki/ExactCacheValidationPlan
--- a/mercurial/util.py Thu May 19 00:20:38 2016 +0900
+++ b/mercurial/util.py Thu May 19 00:20:38 2016 +0900
@@ -1010,10 +1010,14 @@
return check
-def copyfile(src, dest, hardlink=False, copystat=False):
+def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False):
'''copy a file, preserving mode and optionally other stat info like
atime/mtime'''
+ assert not (copystat and checkambig)
+ oldstat = None
if os.path.lexists(dest):
+ if checkambig:
+ oldstat = checkambig and filestat(dest)
unlink(dest)
# hardlinks are problematic on CIFS, quietly ignore this flag
# until we find a way to work around it cleanly (issue4546)
@@ -1035,6 +1039,12 @@
shutil.copystat(src, dest)
else:
shutil.copymode(src, dest)
+ if oldstat and oldstat.stat:
+ newstat = filestat(dest)
+ if newstat.isambig(oldstat):
+ # stat of copied file is ambiguous to original one
+ advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
+ os.utime(dest, (advanced, advanced))
except shutil.Error as inst:
raise Abort(str(inst))