changeset 89:edeec6829d80

SubversionRepo: remember svn.diff3() does not work Calling it repeatedly is expensive, files and directories are created, svn transport layer is reset.
author Patrick Mezard <pmezard@gmail.com>
date Fri, 14 Nov 2008 16:18:24 -0600
parents 3b60f223893a
children 4c419603d41b
files fetch_command.py svnwrap/svn_swig_wrapper.py
diffstat 2 files changed, 44 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- a/fetch_command.py
+++ b/fetch_command.py
@@ -368,28 +368,20 @@ def stupid_svn_server_pull_rev(ui, svn, 
         link_files = {}
         exec_files = {}
         try:
-            try:
-                if br_p == b:
-                    # letting patch handle binaries sounded
-                    # cool, but it breaks patch in sad ways
-                    d = svn.get_unified_diff(diff_path, r.revnum, deleted=False,
-                                             ignore_type=False)
-                else:
-                    d = svn.get_unified_diff(diff_path, r.revnum,
-                                             other_path=make_diff_path(br_p),
-                                             other_rev=parent_rev,
-                                             deleted=True, ignore_type=True)
-                    if d:
-                        ui.status('Branch creation with mods, pulling full rev.\n')
-                        raise BadPatchApply()
-            except core.SubversionException, e:
-                # "Can't write to stream: The handle is invalid."
-                # This error happens systematically under Windows, possibly
-                # related to file handles being non-write shareable by default.
-                if e.apr_err != 720006:
-                    raise
-                raise BadPatchApply()
-            
+            if br_p == b:
+                # letting patch handle binaries sounded
+                # cool, but it breaks patch in sad ways
+                d = svn.get_unified_diff(diff_path, r.revnum, deleted=False,
+                                         ignore_type=False)
+            else:
+                d = svn.get_unified_diff(diff_path, r.revnum,
+                                         other_path=make_diff_path(br_p),
+                                         other_rev=parent_rev,
+                                         deleted=True, ignore_type=True)
+                if d:
+                    ui.status('Branch creation with mods, pulling full rev.\n')
+                    raise BadPatchApply()
+
             for m in binary_file_re.findall(d):
                 # we have to pull each binary file by hand as a fulltext,
                 # which sucks but we've got no choice
@@ -517,7 +509,9 @@ def stupid_svn_server_pull_rev(ui, svn, 
                 os.remove(path)
                 link_files[m] = link_path
                 files_touched.add(m)
-        except (core.SubversionException, BadPatchApply), e:
+        except (core.SubversionException,
+                BadPatchApply,
+                svnwrap.SubversionRepoCanNotDiff), e:
             if (hasattr(e, 'apr_err') and e.apr_err != 160013):
                 raise
             # Either this revision or the previous one does not exist.
--- a/svnwrap/svn_swig_wrapper.py
+++ b/svnwrap/svn_swig_wrapper.py
@@ -14,6 +14,14 @@ from svn import ra
 if (core.SVN_VER_MAJOR, core.SVN_VER_MINOR, core.SVN_VER_MICRO) < (1, 5, 0): #pragma: no cover
     raise ImportError, 'You must have Subversion 1.5.0 or newer and matching SWIG bindings.'
 
+class SubversionRepoCanNotReplay(Exception):
+    """Exception raised when the svn server is too old to have replay.
+    """
+
+class SubversionRepoCanNotDiff(Exception):
+    """Exception raised when the svn API diff3() command cannot be used
+    """
+
 def optrev(revnum):
     optrev = core.svn_opt_revision_t()
     optrev.kind = core.svn_opt_revision_number
@@ -111,6 +119,7 @@ class SubversionRepo(object):
         self.subdir = url[len(repo_root):]
         if not self.subdir or self.subdir[-1] != '/':
             self.subdir += '/'
+        self.hasdiff3 = True
 
     def init_ra_and_client(self):
         """Initializes the RA and client layers, because sometimes getting
@@ -367,6 +376,8 @@ class SubversionRepo(object):
                          deleted=True, ignore_type=False):
         """Gets a unidiff of path at revision against revision-1.
         """
+        if not self.hasdiff3:
+            raise SubversionRepoCanNotDiff()
         # works around an svn server keeping too many open files (observed
         # in an svnserve from the 1.2 era)
         self.init_ra_and_client()
@@ -380,24 +391,34 @@ class SubversionRepo(object):
             other_rev = revision - 1
         old_cwd = os.getcwd()
         tmpdir = tempfile.mkdtemp('svnwrap_temp')
+        out, err = None, None
         try:
             # hot tip: the swig bridge doesn't like StringIO for these bad boys
             out_path = os.path.join(tmpdir, 'diffout')
             error_path = os.path.join(tmpdir, 'differr')
-            out, err = None, None
+            out = open(out_path, 'w')
+            err = open(error_path, 'w')
             try:
-                out = open(out_path, 'w')
-                err = open(error_path, 'w')
                 client.diff3([], url2, optrev(other_rev), url, optrev(revision),
                              True, True, deleted, ignore_type, 'UTF-8', out, err,
                              self.client_context, self.pool)
-            finally:
-                if out: out.close()
-                if err: err.close()
+            except core.SubversionException, e:
+                # "Can't write to stream: The handle is invalid."
+                # This error happens systematically under Windows, possibly
+                # related to file handles being non-write shareable by default.
+                if e.apr_err != 720006:
+                    raise
+                self.hasdiff3 = False
+                raise SubversionRepoCanNotDiff()
+            out.close()
+            err.close()
+            out, err = None, None
             assert len(open(error_path).read()) == 0
             diff = open(out_path).read()
             return diff
         finally:
+            if out: out.close()
+            if err: err.close()
             shutil.rmtree(tmpdir)
             os.chdir(old_cwd)
 
@@ -473,7 +494,3 @@ class SubversionRepo(object):
         """
         kind = ra.check_path(self.ra, path.strip('/'), revision)
         return _svntypes.get(kind)
-
-class SubversionRepoCanNotReplay(Exception):
-    """Exception raised when the svn server is too old to have replay.
-    """