Mercurial > public > mercurial-scm > hg
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): |