# HG changeset patch # User FUJIWARA Katsunori # Date 1463584838 -32400 # Node ID ce2d81aafbae2e5dbcec4c47bf4c39fd9f17fe86 # Parent 731ced087a4b7f5f4d3ba05d30af2df961fe8632 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 diff -r 731ced087a4b -r ce2d81aafbae mercurial/util.py --- 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))