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, VcBase, VcException 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 = subprocess.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 = subprocess.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 = 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 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 Git''' # 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$') line_re = re.compile(r'^\^*([0-9a-f]+) +(.*)\((.+) +([-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 = [s.decode('UTF-8') for s in groups[:-1]] del fields[1] if fields[1] == 'Not Committed Yet': fields[1] = '' if fields[0] == '00000000': fields[0] = '' 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 blame output for the specific revision in Git''' args = [arg("revision", t=type(""), p="Revision: ", h="revision number")] def _open_pipe(self, w, **vargs): cmd = ("git", 'blame', vargs['revision'], w.buffer.path) return Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) class GitRevView(VcRevView): '''show file contents in Git at revision''' args = [arg("revision", t=type(""), p="Revision: ", h="revision number")] namebase = 'Git' _is_method = True def _get_cmd(self, w, **vargs): path = self._get_path(w, **vargs) return ("git", 'show', vargs['revision'] + ':' + path)