import os, subprocess, re, tempfile from subprocess import Popen, PIPE, STDOUT import buffer, default, dirutil, lex, regex, util, window from point import Point import buffer.colors from method.vc import VcBlame, VcRevView from method import Method, Argument, arg if os.system('which svn >/dev/null 2>/dev/null') == 0: has_svn = True else: has_svn = False class SvnException(Exception): pass statuses = { ' ': 'Unmodified', 'A': 'Added', 'C': 'Conflicted', 'D': 'Deleted', 'I': 'Ignored', 'M': 'Modified', 'R': 'Replaced', 'X': 'External', '?': 'Unknown', '!': 'Missing', '~': 'Obstructed', } def get_status(path, base=None): if base is None: base = os.getcwd() + os.path.sep if path.startswith(base): path = path[len(base):] cmd = "svn status -v %r" % path status, data = subprocess.getstatusoutput(cmd) status = status >> 8 if status != 0: raise SvnException("Problems with 'svn status': %d" % status) c = data[0] status = statuses.get(c, 'Error (%s)' % c) fields = data[6:].split() try: rrev, lrev, lauthor, filename = fields except: raise Exception('%r %r' % (fields, data[6:])) return { 'svn-filename': filename, 'svn-status': status, 'svn-lrev': lrev, 'svn-rrev': rrev, 'svn-author': lauthor, } class SvnCommit(Method): '''diff the current file with the version in SVN''' args = [Argument("msg", type=type(""), prompt="Commit Message: ")] regex = re.compile(r'^Committed revision ([0-9]+)\.$') def _execute(self, w, **vargs): if not hasattr(w.buffer, 'path'): w.set_error("Buffer has no corresponding file") return cwd = os.getcwd() + os.path.sep path = w.buffer.path if path.startswith(cwd): path = path[len(cwd):] cmd = "svn ci -m %r %r" % (vargs['msg'], path) (status, data) = subprocess.getstatusoutput(cmd) status = status >> 8 lines = data.split('\n') if status == 0: try: for line in lines: m = self.regex.match(line) if m: rev = m.group(1) w.buffer.metadata['svn-lrev'] = rev w.buffer.metadata['svn-rrev'] = rev w.set_error("Committed [%s]" % rev) return except: pass w.set_error("Problems with SVN commit: %d" % status) w.application.data_buffer("*Commit*", repr(lines), switch_to=True) class SvnStatus(Method): column = { ' ': 'Unmodified', 'A': 'Added', 'C': 'Conflicted', 'D': 'Deleted', 'I': 'Ignored', 'M': 'Modified', 'R': 'Replaced', 'X': 'External', '?': 'Unknown', '!': 'Missing', '~': 'Obstructed', } def _execute(self, w, **vargs): if not hasattr(w.buffer, 'path'): w.set_error("Buffer has no corresponding file") return cwd = os.getcwd() + os.path.sep path = w.buffer.path if path.startswith(cwd): path = path[len(cwd):] cmd = "svn status -v %r" % path (status, data) = subprocess.getstatusoutput(cmd) status = status >> 8 if status != 0: w.set_error("Problems with 'svn status': %d" % status) return c = data[0] status = self.column.get(c, 'Error (%s)' % c) fields = data[6:].split() if len(fields) == 4: rrev, lrev, lauthor, filename = fields w.buffer.metadata['svn-filename'] = filename w.buffer.metadata['svn-status'] = status w.buffer.metadata['svn-lrev'] = lrev w.buffer.metadata['svn-rrev'] = rrev w.buffer.metadata['svn-author'] = lauthor w.buffer.metadata['vc-info'] = '[svn:%s/%s]' % (lrev, rrev) w.set_error('%s %s %s/%s [%s]' % (filename, status, rrev, lrev, lauthor)) else: w.buffer.metadata['svn-filename'] = path w.buffer.metadata['svn-status'] = status w.buffer.metadata['vc-info'] = '[svn:%s]' % status.lower() w.set_error('%s %s' % (path, status)) class SvnLog(Method): '''display the SVN log for the current file''' sep_re = re.compile('^-+$') log_re = re.compile(r'^(.+?) \| (.+?) \| (.{25}) .+? \| (.+)$') def _build_entry(self, log_line, mesg_lines): log_data = '[c:d:*]%s [g:d:*]%s [b:d:*]%s [c:d:*]%s' % log_line mesg_data = '\n'.join(mesg_lines).strip() if mesg_data: mesg_data += '\n' mesg_data = mesg_data.replace('[', '\\[') mesg_data = mesg_data.replace(']', '\\]') return '[b:d:*]' + log_data + '\n' + mesg_data def _execute(self, w, **vargs): cmd = "svn log %r" % w.buffer.path (status, data) = subprocess.getstatusoutput(cmd) entries = [] log_line, mesg_lines = None, [] for line in data.split('\n'): if self.sep_re.match(line): if log_line is not None: entries.append(self._build_entry(log_line, mesg_lines)) log_line = None else: m = self.log_re.match(line) if m: log_line = m.groups() mesg_lines = [] else: assert log_line is not None, '%r %r' % (entries, line) mesg_lines.append(line) data2 = ''.join(entries) if status == 0 and data: w.application.color_data_buffer("*Log*", data2, switch_to=True) w.set_error("%s: logfile" % self.name) elif status == 0: w.set_error("%s: There was no data" % self.name) else: w.set_error("%s: There was an error (%s)" % (self.name, status)) class SvnDiff(Method): '''diff the current file with the version in SVN''' def _execute(self, w, **vargs): if not hasattr(w.buffer, 'path'): w.set_error("Buffer has no corresponding file") return cmd = "svn diff %r" % w.buffer.path (status, data) = subprocess.getstatusoutput(cmd) if status == 0: if data: w.application.data_buffer("*Diff*", data, switch_to=True, modename='diff') w.set_error("Differences were found") else: w.set_error("No difference found") else: w.set_error("There was an error (%s)" % (status)) class SvnDiff2(Method): '''diff the current file with the version in SVN''' rev_regex = re.compile('^[0-9]+$') args = [Argument("revision", type=type(""), prompt="Old Revision: ")] def _execute(self, w, **vargs): if not hasattr(w.buffer, 'path'): w.set_error("Buffer has no corresponding file") return rev = vargs['revision'] if not self.rev_regex.match(rev): w.set_error("Could not parse revision: %r" % rev) return cwd = os.getcwd() + os.path.sep path = w.buffer.path if path.startswith(cwd): path = path[len(cwd):] cmd = "svn diff -r %s %r" % (rev, path) (status, data) = subprocess.getstatusoutput(cmd) status = status >> 8 if data: w.application.data_buffer("*Diff*", data, switch_to=True, modename='diff') w.set_error("Differences were found") else: w.set_error("No difference found") class SvnDiff3(Method): '''diff the current file with the version in SVN''' rev_regex = re.compile('^[0-9]+$') args = [Argument("revision1", type=type(""), prompt="Old Revision: "), Argument("revision2", type=type(""), prompt="New Revision: ")] def _execute(self, w, **vargs): if not hasattr(w.buffer, 'path'): w.set_error("Buffer has no corresponding file") return rev1 = vargs['revision1'] if not self.rev_regex.match(rev1): w.set_error("Could not parse revision1: %r" % rev) return rev2 = vargs['revision2'] if not self.rev_regex.match(rev2): w.set_error("Could not parse revision2: %r" % rev) return cwd = os.getcwd() + os.path.sep path = w.buffer.path if path.startswith(cwd): path = path[len(cwd):] cmd = "svn diff -r %s:%s %r" % (rev1, rev2, path) (status, data) = subprocess.getstatusoutput(cmd) status = status >> 8 if data: w.application.data_buffer("*Diff*", data, switch_to=True, modename='diff') w.set_error("Differences were found") else: w.set_error("No difference found") class SvnBlame(VcBlame): '''show blame output for the current version in SVN''' # rev, user, date, [time], [timezone], [date-str], content num_fields = 3 line_re = re.compile(r'^ *(\d+) *([a-zA-Z0-9_]+) *([-0-9]+) *[:0-9]+ *-\d{4} *\(.+?\) (.*)\n$') prefix_fmt = '[g:d:*]%*s [c:d:*]%-*s [b:d:*]%*s[d:d:*]' pretest_err_msg = 'Subversion is not installed' _is_method = True def _pretest(self): return has_svn def _open_pipe(self, w, **vargs): cmd = ("svn", 'blame', '-v', w.buffer.path) return Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) class SvnBlame2(SvnBlame): '''show file contents in SVN at revision''' args = [arg("revision", t=type(""), p="Revision: ", h="revision number")] def _open_pipe(self, w, **vargs): cmd = ("svn", 'blame', '-v', '-r', vargs['revision'], w.buffer.path) return Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) class SvnRevView(VcRevView): '''show file contents in SVN at date''' args = [arg("revision", t=type(""), p="Revision: ", h="revision number")] namebase = 'SVN' _is_method = True def _get_cmd(self, w, **vargs): path = self._get_path(w, **vargs) return ("svn", 'cat', '-r', vargs['revision'], path)