diff --git a/application.py b/application.py index cbbd448..ed25b15 100755 --- a/application.py +++ b/application.py @@ -153,6 +153,7 @@ class Application(object): 'method.buffers', 'method.move', 'method.shell', 'method.introspect', 'method.help', 'method.numbers', 'method.spell', 'method.hg', 'method.utf8', 'method.tags', + 'method.git', ) for name in names: exec("import %s" % name) diff --git a/method/git.py b/method/git.py new file mode 100644 index 0000000..3aa6e52 --- /dev/null +++ b/method/git.py @@ -0,0 +1,212 @@ +import os, commands, 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, VcBase + +from method import Method, Argument, arg + +if os.system('which git >/dev/null 2>/dev/null') == 0: + has_git = True +else: + has_git = False + +class GitException(Exception): + pass + +class GitCommit(VcBase): + '''commit this file's changes''' + args = [Argument("msg", type=type(""), prompt="Commit Message: ")] + regex = re.compile(r'^Committed revision ([0-9]+)\.$') + def _execute(self, w, **vargs): + raise GitException("unimplemented") + +class GitStatus(VcBase): + _is_method = True + def _execute(self, w, **vargs): + path = self._get_path(w) + + cmd = "git status --porcelain %r" % path + status, data = commands.getstatusoutput(cmd) + status = status >> 8 + + if status != 0: + raise GitException("Problems with 'git status': %d" % status) + + if not data: + w.set_error("%s unchanged" % path) + return + + cx = data[0] + cy = data[1] + if cx == ' ': + w.set_error("%s working=%s" % (path, cy)) + elif cy == ' ': + w.set_error("%s index=%s" % (path, cx)) + else: + w.set_error("%s index=%s working=%s" % (path, cx, cy)) + + # TODO: handle metadata + #w.buffer.metadata['git-filename'] = filename + #w.buffer.metadata['git-status'] = status + #w.buffer.metadata['git-lrev'] = lrev + #w.buffer.metadata['git-rrev'] = rrev + #w.buffer.metadata['git-author'] = lauthor + #w.buffer.metadata['vc-info'] = '[git:%s/%s]' % (lrev, rrev) + +class GitLog(Method): + '''display the Git log for the current file''' + _commit_re = re.compile('^(commit) ([0-9a-f]+)$') + _log_re = re.compile('^([^ ]+:) (.*)$') + def _e(self, s): + return s.replace('\\', '\\\\').replace('[', '\\[').replace(']', '\\]') + def _em(self, m, i): + return self._e(m.group(i)) + def _execute(self, w, **vargs): + cmd = "git log %r" % w.buffer.path + status, data = commands.getstatusoutput(cmd) + entries = [] + for line in data.split('\n'): + m = self._commit_re.match(line) + if m: + entries.append('[b:d:*]commit [y:d:*]' + self._em(m, 2) + "\n") + continue + + m = self._log_re.match(line) + if m: + entries.append('[b:d:*]' + self._em(m, 1) + " [c:d:*]" + self._em(m, 2) + "\n") + continue + + entries.append(self._e(line) + "\n") + data2 = ''.join(entries) + + if status == 0 and data: + w.application.color_data_buffer("*Log*", data2, switch_to=True) + 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 GitDiff(Method): + '''diff the current file with the version in Git''' + def _execute(self, w, **vargs): + if not hasattr(w.buffer, 'path'): + w.set_error("Buffer has no corresponding file") + return + + cmd = "git diff %r" % w.buffer.path + status, data = commands.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 GitDiff2(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 = "git diff -r %s %r" % (rev, path) +# (status, data) = commands.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 GitDiff3(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 = "git diff -r %s:%s %r" % (rev1, rev2, path) +# (status, data) = commands.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 GitBlame(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'^([0-9a-f]+) \(([a-zA-Z0-9_]+|Not Committed Yet) +([-0-9]+) [:0-9]+ +[-\+]\d{4} +\d+\) (.*)\n$') + prefix_fmt = '[g:d:*]%*s [c:d:*]%-*s [b:d:*]%*s[d:d:*]' + pretest_err_msg = 'Git is not installed' + _is_method = True + def _filter(self, line): + m = self.line_re.match(line) + if not m: + raise VcException("couldn't parse %r" % line) + groups = m.groups() + fields = list(groups[:-1]) + if fields[1] == 'Not Committed Yet': + fields[1] = '' + return {'fields': fields, 'content': groups[-1], 'tokens': []} + def _pretest(self): + return has_git + def _open_pipe(self, w, **vargs): + cmd = ("git", 'blame', w.buffer.path) + return Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) + +#class GitBlame2(GitBlame): +# '''show file contents in SVN at revision''' +# args = [arg("revision", t=type(""), p="Revision: ", h="revision number")] +# def _open_pipe(self, w, **vargs): +# cmd = ("git", 'blame', '-v', '-r', vargs['revision'], w.buffer.path) +# return Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) +# +#class GitRevView(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 ("git", 'cat', '-r', vargs['revision'], path) diff --git a/method/vc.py b/method/vc.py index 8f97a18..1de5bef 100644 --- a/method/vc.py +++ b/method/vc.py @@ -6,6 +6,17 @@ import lex class VcException(Exception): pass +class VcBase(Method): + _is_method = False + def _get_path(self, w): + if not hasattr(w.buffer, 'path'): + raise VcException("buffer has no path") + cwd = os.getcwd() + os.path.sep + path = w.buffer.path + if path.startswith(cwd): + path = path[len(cwd):] + return path + class VcBlame(Method): _is_method = False line_re = None @@ -66,6 +77,7 @@ class VcBlame(Method): groups, gsizes = self._build_groups(w, **vargs) except Exception, e: w.set_error(str(e)) + return data = ''.join(self._build_lines(groups, gsizes, w, **vargs)) w.application.color_data_buffer("*Blame*", data, switch_to=True)