Workaround shutil.rmtree limitation on Windows

By default, shutil.rmtree raises an exception when deleting readonly
files on Windows.

Replace all shutil.rmtree with platform_utils.rmtree, which adds an
error handler to make files read-write when they can't be deleted.

Change-Id: I9cfea9a7b3703fb16a82cf69331540c2c179ed53
This commit is contained in:
Renaud Paquay 2016-11-03 10:37:53 -07:00 committed by David Pursehouse
parent d5cec5e752
commit a65adf74f9
5 changed files with 27 additions and 12 deletions

View File

@ -16,6 +16,8 @@
import os import os
import platform import platform
import select import select
import shutil
import stat
from Queue import Queue from Queue import Queue
from threading import Thread from threading import Thread
@ -210,3 +212,16 @@ def _winpath_is_valid(path):
return tail[0] == os.sep # "x:foo" is invalid return tail[0] == os.sep # "x:foo" is invalid
else: else:
return not drive # "x:" is invalid return not drive # "x:" is invalid
def rmtree(path):
if isWindows():
shutil.rmtree(path, onerror=handle_rmtree_error)
else:
shutil.rmtree(path)
def handle_rmtree_error(function, path, excinfo):
# Allow deleting read-only files
os.chmod(path, stat.S_IWRITE)
function(path)

View File

@ -2299,10 +2299,10 @@ class Project(object):
print("Retrying clone after deleting %s" % print("Retrying clone after deleting %s" %
self.gitdir, file=sys.stderr) self.gitdir, file=sys.stderr)
try: try:
shutil.rmtree(os.path.realpath(self.gitdir)) platform_utils.rmtree(os.path.realpath(self.gitdir))
if self.worktree and os.path.exists(os.path.realpath if self.worktree and os.path.exists(os.path.realpath
(self.worktree)): (self.worktree)):
shutil.rmtree(os.path.realpath(self.worktree)) platform_utils.rmtree(os.path.realpath(self.worktree))
return self._InitGitDir(mirror_git=mirror_git, force_sync=False) return self._InitGitDir(mirror_git=mirror_git, force_sync=False)
except: except:
raise e raise e
@ -2344,9 +2344,9 @@ class Project(object):
self.config.SetString('core.bare', None) self.config.SetString('core.bare', None)
except Exception: except Exception:
if init_obj_dir and os.path.exists(self.objdir): if init_obj_dir and os.path.exists(self.objdir):
shutil.rmtree(self.objdir) platform_utils.rmtree(self.objdir)
if init_git_dir and os.path.exists(self.gitdir): if init_git_dir and os.path.exists(self.gitdir):
shutil.rmtree(self.gitdir) platform_utils.rmtree(self.gitdir)
raise raise
def _UpdateHooks(self): def _UpdateHooks(self):
@ -2516,7 +2516,7 @@ class Project(object):
except GitError as e: except GitError as e:
if force_sync: if force_sync:
try: try:
shutil.rmtree(dotgit) platform_utils.rmtree(dotgit)
return self._InitWorkTree(force_sync=False, submodules=submodules) return self._InitWorkTree(force_sync=False, submodules=submodules)
except: except:
raise e raise e
@ -2536,7 +2536,7 @@ class Project(object):
self._CopyAndLinkFiles() self._CopyAndLinkFiles()
except Exception: except Exception:
if init_dotgit: if init_dotgit:
shutil.rmtree(dotgit) platform_utils.rmtree(dotgit)
raise raise
def _gitdir_path(self, path): def _gitdir_path(self, path):

View File

@ -14,10 +14,10 @@
# limitations under the License. # limitations under the License.
from __future__ import print_function from __future__ import print_function
import shutil
import sys import sys
from command import Command, GitcClientCommand from command import Command, GitcClientCommand
import platform_utils
from pyversion import is_python3 from pyversion import is_python3
if not is_python3(): if not is_python3():
@ -50,4 +50,4 @@ and all locally downloaded sources.
if not response == 'yes': if not response == 'yes':
print('Response was not "yes"\n Exiting...') print('Response was not "yes"\n Exiting...')
sys.exit(1) sys.exit(1)
shutil.rmtree(self.gitc_manifest.gitc_client_dir) platform_utils.rmtree(self.gitc_manifest.gitc_client_dir)

View File

@ -17,7 +17,6 @@ from __future__ import print_function
import os import os
import platform import platform
import re import re
import shutil
import sys import sys
from pyversion import is_python3 from pyversion import is_python3
@ -35,6 +34,7 @@ from error import ManifestParseError
from project import SyncBuffer from project import SyncBuffer
from git_config import GitConfig from git_config import GitConfig
from git_command import git_require, MIN_GIT_VERSION from git_command import git_require, MIN_GIT_VERSION
import platform_utils
class Init(InteractiveCommand, MirrorSafeCommand): class Init(InteractiveCommand, MirrorSafeCommand):
common = True common = True
@ -252,7 +252,7 @@ to update the working directory files.
# Better delete the manifest git dir if we created it; otherwise next # Better delete the manifest git dir if we created it; otherwise next
# time (when user fixes problems) we won't go through the "is_new" logic. # time (when user fixes problems) we won't go through the "is_new" logic.
if is_new: if is_new:
shutil.rmtree(m.gitdir) platform_utils.rmtree(m.gitdir)
sys.exit(1) sys.exit(1)
if opt.manifest_branch: if opt.manifest_branch:

View File

@ -19,7 +19,6 @@ import netrc
from optparse import SUPPRESS_HELP from optparse import SUPPRESS_HELP
import os import os
import re import re
import shutil
import socket import socket
import subprocess import subprocess
import sys import sys
@ -73,6 +72,7 @@ from project import Project
from project import RemoteSpec from project import RemoteSpec
from command import Command, MirrorSafeCommand from command import Command, MirrorSafeCommand
from error import RepoChangedException, GitError, ManifestParseError from error import RepoChangedException, GitError, ManifestParseError
import platform_utils
from project import SyncBuffer from project import SyncBuffer
from progress import Progress from progress import Progress
from wrapper import Wrapper from wrapper import Wrapper
@ -473,7 +473,7 @@ later is required to fix a server side protocol bug.
# working git repository around. There shouldn't be any git projects here, # working git repository around. There shouldn't be any git projects here,
# so rmtree works. # so rmtree works.
try: try:
shutil.rmtree(os.path.join(path, '.git')) platform_utils.rmtree(os.path.join(path, '.git'))
except OSError: except OSError:
print('Failed to remove %s' % os.path.join(path, '.git'), file=sys.stderr) print('Failed to remove %s' % os.path.join(path, '.git'), file=sys.stderr)
print('error: Failed to delete obsolete path %s' % path, file=sys.stderr) print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)