diff --git a/gclient_scm.py b/gclient_scm.py index 546aa08cd7..2d773a355f 100644 --- a/gclient_scm.py +++ b/gclient_scm.py @@ -954,6 +954,11 @@ class SVNWrapper(SCMWrapper): print(os.path.join(self.checkout_path, file_status[1])) scm.SVN.Revert(self.checkout_path, callback=printcb) + # Revert() may delete the directory altogether. + if not os.path.isdir(self.checkout_path): + # Don't reuse the args. + return self.update(options, [], file_list) + try: # svn revert is so broken we don't even use it. Using # "svn up --revision BASE" achieve the same effect. diff --git a/scm.py b/scm.py index 6281643346..261cb64917 100644 --- a/scm.py +++ b/scm.py @@ -985,6 +985,9 @@ class SVN(object): not file_status[0][1:].isspace()): # Added, deleted file requires manual intervention and require calling # revert, like for properties. + if not os.path.isdir(repo_root): + # '.' was deleted. It's not worth continuing. + return try: SVN.Capture(['revert', file_status[1]], cwd=repo_root) except subprocess2.CalledProcessError: diff --git a/tests/gclient_scm_test.py b/tests/gclient_scm_test.py index 44d23aa30b..908b322b2d 100755 --- a/tests/gclient_scm_test.py +++ b/tests/gclient_scm_test.py @@ -196,6 +196,7 @@ class SVNWrapperTestCase(BaseTestCase): gclient_scm.os.path.isdir(self.base_path).AndReturn(True) gclient_scm.os.path.isdir(join(self.base_path, '.svn')).AndReturn(True) gclient_scm.scm.SVN.CaptureStatus(self.base_path).AndReturn([]) + gclient_scm.os.path.isdir(self.base_path).AndReturn(True) gclient_scm.scm.SVN.RunAndGetFileList( options.verbose, ['update', '--revision', 'BASE', '--ignore-externals'], @@ -222,6 +223,7 @@ class SVNWrapperTestCase(BaseTestCase): gclient_scm.os.path.islink(file_path).AndReturn(False) gclient_scm.os.path.isdir(file_path).AndReturn(True) gclient_scm.gclient_utils.RemoveDirectory(file_path) + gclient_scm.os.path.isdir(self.base_path).AndReturn(True) gclient_scm.scm.SVN.RunAndGetFileList( options.verbose, ['update', '--revision', 'BASE', '--ignore-externals'], @@ -235,6 +237,33 @@ class SVNWrapperTestCase(BaseTestCase): scm.revert(options, self.args, file_list2) self.checkstdout(('%s\n' % file_path)) + def testRevertDot(self): + self.mox.StubOutWithMock(gclient_scm.SVNWrapper, 'update') + options = self.Options(verbose=True) + gclient_scm.os.path.isdir(self.base_path).AndReturn(True) + gclient_scm.os.path.isdir(join(self.base_path, '.svn')).AndReturn(True) + items = [ + ('~ ', '.'), + ] + gclient_scm.scm.SVN.CaptureStatus(self.base_path).AndReturn(items) + file_path = join(self.base_path, '.') + gclient_scm.os.path.exists(file_path).AndReturn(True) + gclient_scm.os.path.isfile(file_path).AndReturn(False) + gclient_scm.os.path.islink(file_path).AndReturn(False) + gclient_scm.os.path.isdir(file_path).AndReturn(True) + gclient_scm.gclient_utils.RemoveDirectory(file_path) + gclient_scm.os.path.isdir(self.base_path).AndReturn(False) + # The mock is unbound so self is not necessary. + # pylint: disable=E1120 + gclient_scm.SVNWrapper.update(options, [], ['.']) + + self.mox.ReplayAll() + scm = self._scm_wrapper(url=self.url, root_dir=self.root_dir, + relpath=self.relpath) + file_list2 = [] + scm.revert(options, self.args, file_list2) + self.checkstdout(('%s\n' % file_path)) + def testStatus(self): options = self.Options(verbose=True) gclient_scm.os.path.isdir(self.base_path).AndReturn(True)