comparison svnwrap/svn_swig_wrapper.py @ 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 b033d74be76b
children 7486c6f6cccc
comparison
equal deleted inserted replaced
88:3b60f223893a 89:edeec6829d80
11 from svn import delta 11 from svn import delta
12 from svn import ra 12 from svn import ra
13 13
14 if (core.SVN_VER_MAJOR, core.SVN_VER_MINOR, core.SVN_VER_MICRO) < (1, 5, 0): #pragma: no cover 14 if (core.SVN_VER_MAJOR, core.SVN_VER_MINOR, core.SVN_VER_MICRO) < (1, 5, 0): #pragma: no cover
15 raise ImportError, 'You must have Subversion 1.5.0 or newer and matching SWIG bindings.' 15 raise ImportError, 'You must have Subversion 1.5.0 or newer and matching SWIG bindings.'
16
17 class SubversionRepoCanNotReplay(Exception):
18 """Exception raised when the svn server is too old to have replay.
19 """
20
21 class SubversionRepoCanNotDiff(Exception):
22 """Exception raised when the svn API diff3() command cannot be used
23 """
16 24
17 def optrev(revnum): 25 def optrev(revnum):
18 optrev = core.svn_opt_revision_t() 26 optrev = core.svn_opt_revision_t()
19 optrev.kind = core.svn_opt_revision_number 27 optrev.kind = core.svn_opt_revision_number
20 optrev.value.number = revnum 28 optrev.value.number = revnum
109 repo_root = ra.get_repos_root(self.ra, self.pool) 117 repo_root = ra.get_repos_root(self.ra, self.pool)
110 # *will* have a leading '/', would not if we used get_repos_root2 118 # *will* have a leading '/', would not if we used get_repos_root2
111 self.subdir = url[len(repo_root):] 119 self.subdir = url[len(repo_root):]
112 if not self.subdir or self.subdir[-1] != '/': 120 if not self.subdir or self.subdir[-1] != '/':
113 self.subdir += '/' 121 self.subdir += '/'
122 self.hasdiff3 = True
114 123
115 def init_ra_and_client(self): 124 def init_ra_and_client(self):
116 """Initializes the RA and client layers, because sometimes getting 125 """Initializes the RA and client layers, because sometimes getting
117 unified diffs runs the remote server out of open files. 126 unified diffs runs the remote server out of open files.
118 """ 127 """
365 374
366 def get_unified_diff(self, path, revision, other_path=None, other_rev=None, 375 def get_unified_diff(self, path, revision, other_path=None, other_rev=None,
367 deleted=True, ignore_type=False): 376 deleted=True, ignore_type=False):
368 """Gets a unidiff of path at revision against revision-1. 377 """Gets a unidiff of path at revision against revision-1.
369 """ 378 """
379 if not self.hasdiff3:
380 raise SubversionRepoCanNotDiff()
370 # works around an svn server keeping too many open files (observed 381 # works around an svn server keeping too many open files (observed
371 # in an svnserve from the 1.2 era) 382 # in an svnserve from the 1.2 era)
372 self.init_ra_and_client() 383 self.init_ra_and_client()
373 384
374 assert path[0] != '/' 385 assert path[0] != '/'
378 url2 = self.svn_url + '/' + other_path 389 url2 = self.svn_url + '/' + other_path
379 if other_rev is None: 390 if other_rev is None:
380 other_rev = revision - 1 391 other_rev = revision - 1
381 old_cwd = os.getcwd() 392 old_cwd = os.getcwd()
382 tmpdir = tempfile.mkdtemp('svnwrap_temp') 393 tmpdir = tempfile.mkdtemp('svnwrap_temp')
394 out, err = None, None
383 try: 395 try:
384 # hot tip: the swig bridge doesn't like StringIO for these bad boys 396 # hot tip: the swig bridge doesn't like StringIO for these bad boys
385 out_path = os.path.join(tmpdir, 'diffout') 397 out_path = os.path.join(tmpdir, 'diffout')
386 error_path = os.path.join(tmpdir, 'differr') 398 error_path = os.path.join(tmpdir, 'differr')
387 out, err = None, None 399 out = open(out_path, 'w')
400 err = open(error_path, 'w')
388 try: 401 try:
389 out = open(out_path, 'w')
390 err = open(error_path, 'w')
391 client.diff3([], url2, optrev(other_rev), url, optrev(revision), 402 client.diff3([], url2, optrev(other_rev), url, optrev(revision),
392 True, True, deleted, ignore_type, 'UTF-8', out, err, 403 True, True, deleted, ignore_type, 'UTF-8', out, err,
393 self.client_context, self.pool) 404 self.client_context, self.pool)
394 finally: 405 except core.SubversionException, e:
395 if out: out.close() 406 # "Can't write to stream: The handle is invalid."
396 if err: err.close() 407 # This error happens systematically under Windows, possibly
408 # related to file handles being non-write shareable by default.
409 if e.apr_err != 720006:
410 raise
411 self.hasdiff3 = False
412 raise SubversionRepoCanNotDiff()
413 out.close()
414 err.close()
415 out, err = None, None
397 assert len(open(error_path).read()) == 0 416 assert len(open(error_path).read()) == 0
398 diff = open(out_path).read() 417 diff = open(out_path).read()
399 return diff 418 return diff
400 finally: 419 finally:
420 if out: out.close()
421 if err: err.close()
401 shutil.rmtree(tmpdir) 422 shutil.rmtree(tmpdir)
402 os.chdir(old_cwd) 423 os.chdir(old_cwd)
403 424
404 def get_file(self, path, revision): 425 def get_file(self, path, revision):
405 """Return content and mode of file at given path and revision. 426 """Return content and mode of file at given path and revision.
471 """Return the entry type at the given revision, 'f', 'd' or None 492 """Return the entry type at the given revision, 'f', 'd' or None
472 if the entry does not exist. 493 if the entry does not exist.
473 """ 494 """
474 kind = ra.check_path(self.ra, path.strip('/'), revision) 495 kind = ra.check_path(self.ra, path.strip('/'), revision)
475 return _svntypes.get(kind) 496 return _svntypes.get(kind)
476
477 class SubversionRepoCanNotReplay(Exception):
478 """Exception raised when the svn server is too old to have replay.
479 """