[PATCH] util.rename: use temporary file name for rename-targets on windows
Sune Foldager
cryo at cyanite.org
Wed Apr 29 07:31:32 UTC 2009
# HG changeset patch
# User Sune Foldager <cryo at cyanite.org>
# Date 1240990228 -7200
# Node ID 9107fa04ebfa3609d78e6b1dc326cbcdc38c394d
# Parent dc6f1c8e366dc653d7e7fd656636205e7d6e5d13
util.rename: use temporary file name for rename-targets on windows
Use a temporary file name as target for a forced rename on Windows. The
target file name is not opened at any time; just renamed into and then
unlinked. Using a temporary instead of a static name is necessary since
otherwise a hg crash can leave the file lying around, blocking future
attempts at renaming.
diff --git a/mercurial/util.py b/mercurial/util.py
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -15,7 +15,7 @@
from i18n import _
import cStringIO, errno, re, shutil, sys, tempfile, traceback, error
-import os, stat, threading, time, calendar, glob, osutil
+import os, stat, threading, time, calendar, glob, osutil, random
import imp
# Python compatibility
@@ -630,13 +630,28 @@
try:
os.rename(src, dst)
except OSError, err: # FIXME: check err (EEXIST ?)
- # on windows, rename to existing file is not allowed, so we
- # must delete destination first. but if file is open, unlink
- # schedules it for delete but does not delete it. rename
+
+ # On windows, rename to existing file is not allowed, so we
+ # must delete destination first. But if a file is open, unlink
+ # schedules it for delete but does not delete it. Rename
# happens immediately even for open files, so we rename
- # destination to a temporary name, then delete that. then
+ # destination to a temporary name, then delete that. Then
# rename is safe to do.
- temp = dst + "-force-rename"
+ # The temporary name is chosen at random to avoid the situation
+ # where a file is left lying around from a previous aborted run.
+ # The usual race condition this introduces can't be avoided as
+ # we need the name to rename into, and not the file itself. Due
+ # to the nature of the operation however, any races will at worst
+ # lead to the rename failing and the current operation aborting.
+
+ def tempname(prefix):
+ for tries in xrange(10):
+ temp = '%s-%08x' % (prefix, random.randint(0, 0xffffffff))
+ if not os.path.exists(temp):
+ return temp
+ raise IOError, (errno.EEXIST, "No usable temporary filename found")
+
+ temp = tempname(dst)
os.rename(dst, temp)
os.unlink(temp)
os.rename(src, dst)
More information about the Mercurial-devel
mailing list