Finish Bug #1309545 ("tip of the branch" URLs don't work with 'bincvs' module)
* viewcvs/lib/vclib/bincvs/__init__.py (COMalformedOutput, COMissingRevision): New exception classes. (BinCVSRepository._get_tip_revision): New helper. (BinCVSRepository.openfile): If a COMissingRevision exception is raised, use rlog to try to resolve to a real (existing) revision. (_parse_co_header): Stop writing HTML error messages, and use the new exceptions. git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@1115 8cb11bc2-c004-0410-86c3-e597b4017df7remotes/tags/1.0.0-rc1
parent
0881ded7c0
commit
54c1b4f760
|
@ -109,50 +109,70 @@ class BinCVSRepository(CVSRepository):
|
||||||
CVSRepository.__init__(self, name, rootpath)
|
CVSRepository.__init__(self, name, rootpath)
|
||||||
self.rcs_paths = rcs_paths
|
self.rcs_paths = rcs_paths
|
||||||
|
|
||||||
|
def _get_tip_revision(self, rcs_file, rev=None):
|
||||||
|
"""Get the (basically) youngest revision (filtered by REV)."""
|
||||||
|
args = rcs_file,
|
||||||
|
fp = self.rcs_popen('rlog', args, 'rt', 0)
|
||||||
|
filename, default_branch, tags, msg, eof = _parse_log_header(fp)
|
||||||
|
revs = []
|
||||||
|
while not eof:
|
||||||
|
revision, eof = _parse_log_entry(fp)
|
||||||
|
if revision:
|
||||||
|
revs.append(revision)
|
||||||
|
revs = _file_log(revs, tags, default_branch, rev)
|
||||||
|
if revs:
|
||||||
|
return revs[-1]
|
||||||
|
return None
|
||||||
|
|
||||||
def openfile(self, path_parts, rev=None):
|
def openfile(self, path_parts, rev=None):
|
||||||
if not rev or rev == 'HEAD' or rev == 'MAIN':
|
if not rev or rev == 'HEAD' or rev == 'MAIN':
|
||||||
rev_flag = '-p'
|
rev_flag = '-p'
|
||||||
else:
|
else:
|
||||||
rev_flag = '-p' + rev
|
rev_flag = '-p' + rev
|
||||||
|
|
||||||
full_name = self.rcsfile(path_parts, root=1, v=0)
|
full_name = self.rcsfile(path_parts, root=1, v=0)
|
||||||
|
|
||||||
fp = self.rcs_popen('co', (rev_flag, full_name), 'rb')
|
used_rlog = 0
|
||||||
|
tip_rev = None # used only if we have to fallback to using rlog
|
||||||
|
|
||||||
filename, revision = _parse_co_header(fp)
|
fp = self.rcs_popen('co', (rev_flag, full_name), 'rb')
|
||||||
|
try:
|
||||||
|
filename, revision = _parse_co_header(fp)
|
||||||
|
except COMissingRevision:
|
||||||
|
# We got a "revision X.Y.Z absent" error from co. This could be
|
||||||
|
# because we were asked to find a tip of a branch, which co
|
||||||
|
# doesn't seem to handle. So we do rlog-gy stuff to figure out
|
||||||
|
# which revision the tip of the branch currently maps to.
|
||||||
|
### TODO: Only do this when 'rev' is a branch symbol name?
|
||||||
|
if not used_rlog:
|
||||||
|
tip_rev = self._get_tip_revision(full_name + ',v', rev)
|
||||||
|
used_rlog = 1
|
||||||
|
if not tip_rev:
|
||||||
|
raise vclib.Error("Unable to find valid revision")
|
||||||
|
fp = self.rcs_popen('co', ('-p' + tip_rev.string, full_name), 'rb')
|
||||||
|
filename, revision = _parse_co_header(fp)
|
||||||
|
|
||||||
if filename is None:
|
if filename is None:
|
||||||
# CVSNT's co exits without any output if a dead revision is requested.
|
# CVSNT's co exits without any output if a dead revision is requested.
|
||||||
# Bug at http://www.cvsnt.org/cgi-bin/bugzilla/show_bug.cgi?id=190
|
# Bug at http://www.cvsnt.org/cgi-bin/bugzilla/show_bug.cgi?id=190
|
||||||
# As a workaround, we invoke rlog to find the first non-dead revision
|
# As a workaround, we invoke rlog to find the first non-dead revision
|
||||||
# that precedes it and check out that revision instead
|
# that precedes it and check out that revision instead. Of course,
|
||||||
args = full_name + ',v',
|
# if we've already invoked rlog above, we just reuse its output.
|
||||||
fp = self.rcs_popen('rlog', args, 'rt', 0)
|
if not used_rlog:
|
||||||
filename, default_branch, tags, msg, eof = _parse_log_header(fp)
|
tip_rev = self._get_tip_revision(full_name + ',v', rev)
|
||||||
|
used_rlog = 1
|
||||||
# Retrieve revision objects
|
if not (tip_rev and tip_rev.undead):
|
||||||
revs = []
|
raise vclib.Error(
|
||||||
while not eof:
|
'Could not find non-dead revision preceding "%s"' % rev)
|
||||||
revision, eof = _parse_log_entry(fp)
|
fp = self.rcs_popen('co', ('-p' + tip_rev.undead.string,
|
||||||
if revision:
|
full_name), 'rb')
|
||||||
revs.append(revision)
|
filename, revision = _parse_co_header(fp)
|
||||||
|
|
||||||
revs = _file_log(revs, tags, default_branch, rev)
|
|
||||||
|
|
||||||
# if we find a good revision, invoke co again, otherwise error out
|
|
||||||
if revs and revs[-1].undead:
|
|
||||||
rev_flag = '-p' + revs[-1].undead.string
|
|
||||||
fp = self.rcs_popen('co', (rev_flag, full_name), 'rb')
|
|
||||||
filename, revision = _parse_co_header(fp)
|
|
||||||
else:
|
|
||||||
raise vclib.Error("CVSNT co workaround could not find non-dead "
|
|
||||||
"revision preceding \"%s\"" % rev)
|
|
||||||
|
|
||||||
if filename is None:
|
if filename is None:
|
||||||
raise vclib.Error('Missing output from co.<br />fname="%s".' % full_name)
|
raise vclib.Error('Missing output from co (filename = "%s")' % full_name)
|
||||||
|
|
||||||
if not _paths_eq(filename, full_name):
|
if not _paths_eq(filename, full_name):
|
||||||
raise vclib.Error(
|
raise vclib.Error(
|
||||||
'The filename from co did not match. Found "%s". Wanted "%s"<br />'
|
'The filename from co ("%s") did not match (expected "%s")'
|
||||||
% (filename, full_name))
|
% (filename, full_name))
|
||||||
|
|
||||||
return fp, revision
|
return fp, revision
|
||||||
|
@ -487,9 +507,16 @@ def _dict_list_add(dict, idx, elem):
|
||||||
# ======================================================================
|
# ======================================================================
|
||||||
# Functions for parsing output from RCS utilities
|
# Functions for parsing output from RCS utilities
|
||||||
|
|
||||||
|
|
||||||
|
class COMalformedOutput(vclib.Error):
|
||||||
|
pass
|
||||||
|
class COMissingRevision(vclib.Error):
|
||||||
|
pass
|
||||||
|
|
||||||
### suck up other warnings in _re_co_warning?
|
### suck up other warnings in _re_co_warning?
|
||||||
_re_co_filename = re.compile(r'^(.*),v\s+-->\s+standard output\s*\n$')
|
_re_co_filename = re.compile(r'^(.*),v\s+-->\s+standard output\s*\n$')
|
||||||
_re_co_warning = re.compile(r'^.*co: .*,v: warning: Unknown phrases like .*\n$')
|
_re_co_warning = re.compile(r'^.*co: .*,v: warning: Unknown phrases like .*\n$')
|
||||||
|
_re_co_missing_rev = re.compile(r'^.*co: .*,v: revision.*absent\n$')
|
||||||
_re_co_revision = re.compile(r'^revision\s+([\d\.]+)\s*\n$')
|
_re_co_revision = re.compile(r'^revision\s+([\d\.]+)\s*\n$')
|
||||||
|
|
||||||
def _parse_co_header(fp):
|
def _parse_co_header(fp):
|
||||||
|
@ -509,50 +536,40 @@ def _parse_co_header(fp):
|
||||||
#co: INSTALL,v: warning: Unknown phrases like `permissions ...;' are present.
|
#co: INSTALL,v: warning: Unknown phrases like `permissions ...;' are present.
|
||||||
|
|
||||||
# parse the output header
|
# parse the output header
|
||||||
filename = revision = None
|
filename = None
|
||||||
|
|
||||||
|
# look for a filename in the first line (if there is a first line).
|
||||||
line = fp.readline()
|
line = fp.readline()
|
||||||
if not line:
|
if not line:
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
match = _re_co_filename.match(line)
|
match = _re_co_filename.match(line)
|
||||||
if not match:
|
if not match:
|
||||||
raise vclib.Error(
|
raise COMalformedOutput, "Unable to find filename in co output stream"
|
||||||
'First line of co output is not the filename.<br />'
|
|
||||||
'Line was: %s' % (line))
|
|
||||||
filename = match.group(1)
|
filename = match.group(1)
|
||||||
|
|
||||||
|
# look for a revision in the second line.
|
||||||
line = fp.readline()
|
line = fp.readline()
|
||||||
if not line:
|
if not line:
|
||||||
raise vclib.Error(
|
raise COMalformedOutput, "Missing second line from co output stream"
|
||||||
'Missing second line of output from co.<br />'
|
|
||||||
'fname="%s".' % (filename))
|
|
||||||
match = _re_co_revision.match(line)
|
match = _re_co_revision.match(line)
|
||||||
if not match:
|
if match:
|
||||||
match = _re_co_warning.match(line)
|
return filename, match.group(1)
|
||||||
if not match:
|
elif _re_co_missing_rev.match(line):
|
||||||
raise vclib.Error(
|
raise COMissingRevision, "Got missing revision error from co output stream"
|
||||||
'Second line of co output is not the revision.<br />'
|
elif _re_co_warning.match(line):
|
||||||
'Line was: %s<br />'
|
pass
|
||||||
'fname="%s".' % (line, filename))
|
else:
|
||||||
|
raise COMalformedOutput, "Unable to find revision in co output stream"
|
||||||
# second line was a warning. ignore it and move along.
|
|
||||||
line = fp.readline()
|
# if we get here, the second line wasn't a revision, but it was a
|
||||||
if not line:
|
# warning we can ignore. look for a revision in the third line.
|
||||||
raise vclib.Error(
|
line = fp.readline()
|
||||||
'Missing third line of output from co (after a warning).<br />'
|
if not line:
|
||||||
'fname="%s".' % (filename))
|
raise COMalformedOutput, "Missing third line from co output stream"
|
||||||
match = _re_co_revision.match(line)
|
match = _re_co_revision.match(line)
|
||||||
if not match:
|
if match:
|
||||||
raise vclib.Error(
|
return filename, match.group(1)
|
||||||
'Third line of co output is not the revision.<br />'
|
raise COMalformedOutput, "Unable to find revision in co output stream"
|
||||||
'Line was: %s<br />'
|
|
||||||
'fname="%s".' % (line, filename))
|
|
||||||
|
|
||||||
# one of the above cases matches the revision. grab it.
|
|
||||||
revision = match.group(1)
|
|
||||||
|
|
||||||
return filename, revision
|
|
||||||
|
|
||||||
|
|
||||||
# if your rlog doesn't use 77 '=' characters, then this must change
|
# if your rlog doesn't use 77 '=' characters, then this must change
|
||||||
|
|
Loading…
Reference in New Issue