Bläddra i källkod

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
Renaud Paquay 9 år sedan
förälder
incheckning
a65adf74f9
5 ändrade filer med 27 tillägg och 12 borttagningar
  1. 15 0
      platform_utils.py
  2. 6 6
      project.py
  3. 2 2
      subcmds/gitc_delete.py
  4. 2 2
      subcmds/init.py
  5. 2 2
      subcmds/sync.py

+ 15 - 0
platform_utils.py

@@ -16,6 +16,8 @@
 import os
 import platform
 import select
+import shutil
+import stat
 
 from Queue import Queue
 from threading import Thread
@@ -210,3 +212,16 @@ def _winpath_is_valid(path):
       return tail[0] == os.sep  # "x:foo" is invalid
   else:
     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)

+ 6 - 6
project.py

@@ -2299,10 +2299,10 @@ class Project(object):
             print("Retrying clone after deleting %s" %
                   self.gitdir, file=sys.stderr)
             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
                                                   (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)
             except:
               raise e
@@ -2344,9 +2344,9 @@ class Project(object):
           self.config.SetString('core.bare', None)
     except Exception:
       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):
-        shutil.rmtree(self.gitdir)
+        platform_utils.rmtree(self.gitdir)
       raise
 
   def _UpdateHooks(self):
@@ -2516,7 +2516,7 @@ class Project(object):
       except GitError as e:
         if force_sync:
           try:
-            shutil.rmtree(dotgit)
+            platform_utils.rmtree(dotgit)
             return self._InitWorkTree(force_sync=False, submodules=submodules)
           except:
             raise e
@@ -2536,7 +2536,7 @@ class Project(object):
         self._CopyAndLinkFiles()
     except Exception:
       if init_dotgit:
-        shutil.rmtree(dotgit)
+        platform_utils.rmtree(dotgit)
       raise
 
   def _gitdir_path(self, path):

+ 2 - 2
subcmds/gitc_delete.py

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

+ 2 - 2
subcmds/init.py

@@ -17,7 +17,6 @@ from __future__ import print_function
 import os
 import platform
 import re
-import shutil
 import sys
 
 from pyversion import is_python3
@@ -35,6 +34,7 @@ from error import ManifestParseError
 from project import SyncBuffer
 from git_config import GitConfig
 from git_command import git_require, MIN_GIT_VERSION
+import platform_utils
 
 class Init(InteractiveCommand, MirrorSafeCommand):
   common = True
@@ -252,7 +252,7 @@ to update the working directory files.
       # 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.
       if is_new:
-        shutil.rmtree(m.gitdir)
+        platform_utils.rmtree(m.gitdir)
       sys.exit(1)
 
     if opt.manifest_branch:

+ 2 - 2
subcmds/sync.py

@@ -19,7 +19,6 @@ import netrc
 from optparse import SUPPRESS_HELP
 import os
 import re
-import shutil
 import socket
 import subprocess
 import sys
@@ -73,6 +72,7 @@ from project import Project
 from project import RemoteSpec
 from command import Command, MirrorSafeCommand
 from error import RepoChangedException, GitError, ManifestParseError
+import platform_utils
 from project import SyncBuffer
 from progress import Progress
 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,
     # so rmtree works.
     try:
-      shutil.rmtree(os.path.join(path, '.git'))
+      platform_utils.rmtree(os.path.join(path, '.git'))
     except OSError:
       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)