comparison mercurial/util.py @ 8255:ea82a23cf887

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.
author Sune Foldager <cryo@cyanite.org>
date Wed, 29 Apr 2009 09:30:28 +0200
parents bc027d72c289
children e68e149f4d44 592c454477c6
comparison
equal deleted inserted replaced
8246:965b11c1bd82 8255:ea82a23cf887
735 def rename(src, dst): 735 def rename(src, dst):
736 """forcibly rename a file""" 736 """forcibly rename a file"""
737 try: 737 try:
738 os.rename(src, dst) 738 os.rename(src, dst)
739 except OSError, err: # FIXME: check err (EEXIST ?) 739 except OSError, err: # FIXME: check err (EEXIST ?)
740 # on windows, rename to existing file is not allowed, so we 740
741 # must delete destination first. but if file is open, unlink 741 # On windows, rename to existing file is not allowed, so we
742 # schedules it for delete but does not delete it. rename 742 # must delete destination first. But if a file is open, unlink
743 # schedules it for delete but does not delete it. Rename
743 # happens immediately even for open files, so we rename 744 # happens immediately even for open files, so we rename
744 # destination to a temporary name, then delete that. then 745 # destination to a temporary name, then delete that. Then
745 # rename is safe to do. 746 # rename is safe to do.
746 temp = dst + "-force-rename" 747 # The temporary name is chosen at random to avoid the situation
748 # where a file is left lying around from a previous aborted run.
749 # The usual race condition this introduces can't be avoided as
750 # we need the name to rename into, and not the file itself. Due
751 # to the nature of the operation however, any races will at worst
752 # lead to the rename failing and the current operation aborting.
753
754 def tempname(prefix):
755 for tries in xrange(10):
756 temp = '%s-%08x' % (prefix, random.randint(0, 0xffffffff))
757 if not os.path.exists(temp):
758 return temp
759 raise IOError, (errno.EEXIST, "No usable temporary filename found")
760
761 temp = tempname(dst)
747 os.rename(dst, temp) 762 os.rename(dst, temp)
748 os.unlink(temp) 763 os.unlink(temp)
749 os.rename(src, dst) 764 os.rename(src, dst)
750 765
751 def unlink(f): 766 def unlink(f):