comparison mercurial/vfs.py @ 32748:ed66ec39933f

vfs: create copy at renaming to avoid file stat ambiguity if needed In order to fix issue5418, bff5ccbe5ead made vfs.rename(checkambig=True) omit advancing mtime of renamed file, if renamed file is owned by another (EPERM is raised in this case). But this omission causes rewinding mtime at restoration in such situation, and makes avoiding file stat ambiguity difficult, because ExactCacheValidationPlan assumes that mtime should be advanced, if a file is changed in same ctime. https://www.mercurial-scm.org/wiki/ExactCacheValidationPlan Ambiguity of file stat also requires issue5584 to be fixed with other than file stat, but "hash of file", "generation ID" and so on were already rejected ideas (please see original RFC linked from "Outline of issue" in ExactCacheValidationPlan page). This omission occurs: - only for non append-only files (dirstate, bookmarks, and phaseroots), and - only if previous transaction is rollbacked by another user The latter means "sharing a repository clone via group permission". This is reasonable usecase, but not ordinary for many users, IMHO. "hg rollback" itself has been deprecated since Mercurial 2.7, too. Therefore, increasing the cost at rollbacking previous transaction executed by another a little seems reasonable, for avoidance of file stat ambiguity. This patch does: - create copy of (already renamed) source file, if advancing mtime fails for EPERM - rename from copied file to destination file, and - advance mtime of renamed file, which is now owned by current user This patch also factors "self.join(src)" out to reduce redundancy.
author FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
date Fri, 09 Jun 2017 12:58:18 +0900
parents 1a79de471d56
children 7ad95626f6a7
comparison
equal deleted inserted replaced
32747:1a79de471d56 32748:ed66ec39933f
172 172
173 checkambig argument is used with util.filestat, and is useful 173 checkambig argument is used with util.filestat, and is useful
174 only if destination file is guarded by any lock 174 only if destination file is guarded by any lock
175 (e.g. repo.lock or repo.wlock). 175 (e.g. repo.lock or repo.wlock).
176 """ 176 """
177 srcpath = self.join(src)
177 dstpath = self.join(dst) 178 dstpath = self.join(dst)
178 oldstat = checkambig and util.filestat(dstpath) 179 oldstat = checkambig and util.filestat(dstpath)
179 if oldstat and oldstat.stat: 180 if oldstat and oldstat.stat:
180 def dorename(spath, dpath): 181 def dorename(spath, dpath):
181 ret = util.rename(spath, dpath) 182 ret = util.rename(spath, dpath)
182 newstat = util.filestat(dpath) 183 newstat = util.filestat(dpath)
183 if newstat.isambig(oldstat): 184 if newstat.isambig(oldstat):
184 # stat of renamed file is ambiguous to original one 185 # stat of renamed file is ambiguous to original one
185 return ret, newstat.avoidambig(dpath, oldstat) 186 return ret, newstat.avoidambig(dpath, oldstat)
186 return ret, True 187 return ret, True
187 ret, avoided = dorename(self.join(src), dstpath) 188 ret, avoided = dorename(srcpath, dstpath)
189 if not avoided:
190 # simply copy to change owner of srcpath (see issue5418)
191 util.copyfile(dstpath, srcpath)
192 ret, avoided = dorename(srcpath, dstpath)
188 return ret 193 return ret
189 return util.rename(self.join(src), dstpath) 194 return util.rename(srcpath, dstpath)
190 195
191 def readlink(self, path): 196 def readlink(self, path):
192 return os.readlink(self.join(path)) 197 return os.readlink(self.join(path))
193 198
194 def removedirs(self, path=None): 199 def removedirs(self, path=None):