190 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			190 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import functools
 | |
| import re
 | |
| 
 | |
| import sublime
 | |
| from git import GitTextCommand, GitWindowCommand, plugin_file
 | |
| 
 | |
| 
 | |
| class GitBlameCommand(GitTextCommand):
 | |
|     def run(self, edit):
 | |
|         # somewhat custom blame command:
 | |
|         # -w: ignore whitespace changes
 | |
|         # -M: retain blame when moving lines
 | |
|         # -C: retain blame when copying lines between files
 | |
|         command = ['git', 'blame', '-w', '-M', '-C']
 | |
| 
 | |
|         s = sublime.load_settings("Git.sublime-settings")
 | |
|         selection = self.view.sel()[0]  # todo: multi-select support?
 | |
|         if not selection.empty() or not s.get('blame_whole_file'):
 | |
|             # just the lines we have a selection on
 | |
|             begin_line, begin_column = self.view.rowcol(selection.begin())
 | |
|             end_line, end_column = self.view.rowcol(selection.end())
 | |
|             # blame will fail if last line is empty and is included in the selection
 | |
|             if end_line > begin_line and end_column == 0:
 | |
|                 end_line -= 1
 | |
|             lines = str(begin_line + 1) + ',' + str(end_line + 1)
 | |
|             command.extend(('-L', lines))
 | |
|             callback = self.blame_done
 | |
|         else:
 | |
|             callback = functools.partial(self.blame_done,
 | |
|                     position=self.view.viewport_position())
 | |
| 
 | |
|         command.append(self.get_file_name())
 | |
|         self.run_command(command, callback)
 | |
| 
 | |
|     def blame_done(self, result, position=None):
 | |
|         self.scratch(result, title="Git Blame", position=position,
 | |
|                 syntax=plugin_file("syntax/Git Blame.tmLanguage"))
 | |
| 
 | |
| 
 | |
| class GitLog(object):
 | |
|     def run(self, edit=None):
 | |
|         fn = self.get_file_name()
 | |
|         return self.run_log(fn != '', '--', fn)
 | |
| 
 | |
|     def run_log(self, follow, *args):
 | |
|         # the ASCII bell (\a) is just a convenient character I'm pretty sure
 | |
|         # won't ever come up in the subject of the commit (and if it does then
 | |
|         # you positively deserve broken output...)
 | |
|         # 9000 is a pretty arbitrarily chosen limit; picked entirely because
 | |
|         # it's about the size of the largest repo I've tested this on... and
 | |
|         # there's a definite hiccup when it's loading that
 | |
|         command = ['git', 'log', '--pretty=%s\a%h %an <%aE>\a%ad (%ar)',
 | |
|             '--date=local', '--max-count=9000', '--follow' if follow else None]
 | |
|         command.extend(args)
 | |
|         self.run_command(
 | |
|             command,
 | |
|             self.log_done)
 | |
| 
 | |
|     def log_done(self, result):
 | |
|         self.results = [r.split('\a', 2) for r in result.strip().split('\n')]
 | |
|         self.quick_panel(self.results, self.log_panel_done)
 | |
| 
 | |
|     def log_panel_done(self, picked):
 | |
|         if 0 > picked < len(self.results):
 | |
|             return
 | |
|         item = self.results[picked]
 | |
|         # the commit hash is the first thing on the second line
 | |
|         self.log_result(item[1].split(' ')[0])
 | |
| 
 | |
|     def log_result(self, ref):
 | |
|         # I'm not certain I should have the file name here; it restricts the
 | |
|         # details to just the current file. Depends on what the user expects...
 | |
|         # which I'm not sure of.
 | |
|         self.run_command(
 | |
|             ['git', 'log', '-p', '-1', ref, '--', self.get_file_name()],
 | |
|             self.details_done)
 | |
| 
 | |
|     def details_done(self, result):
 | |
|         self.scratch(result, title="Git Commit Details", syntax=plugin_file("syntax/Git Commit Message.tmLanguage"))
 | |
| 
 | |
| 
 | |
| class GitLogCommand(GitLog, GitTextCommand):
 | |
|     pass
 | |
| 
 | |
| 
 | |
| class GitLogAllCommand(GitLog, GitWindowCommand):
 | |
|     pass
 | |
| 
 | |
| 
 | |
| class GitShow(object):
 | |
|     def run(self, edit=None):
 | |
|         # GitLog Copy-Past
 | |
|         self.run_command(
 | |
|             ['git', 'log', '--pretty=%s\a%h %an <%aE>\a%ad (%ar)',
 | |
|             '--date=local', '--max-count=9000', '--', self.get_file_name()],
 | |
|             self.show_done)
 | |
| 
 | |
|     def show_done(self, result):
 | |
|         # GitLog Copy-Past
 | |
|         self.results = [r.split('\a', 2) for r in result.strip().split('\n')]
 | |
|         self.quick_panel(self.results, self.panel_done)
 | |
| 
 | |
|     def panel_done(self, picked):
 | |
|         if 0 > picked < len(self.results):
 | |
|             return
 | |
|         item = self.results[picked]
 | |
|         # the commit hash is the first thing on the second line
 | |
|         ref = item[1].split(' ')[0]
 | |
|         self.run_command(
 | |
|             ['git', 'show', '%s:%s' % (ref, self.get_relative_file_name())],
 | |
|             self.details_done,
 | |
|             ref=ref)
 | |
| 
 | |
|     def details_done(self, result, ref):
 | |
|         syntax = self.view.settings().get('syntax')
 | |
|         self.scratch(result, title="%s:%s" % (ref, self.get_file_name()), syntax=syntax)
 | |
| 
 | |
| 
 | |
| class GitShowCommand(GitShow, GitTextCommand):
 | |
|     pass
 | |
| 
 | |
| 
 | |
| class GitShowAllCommand(GitShow, GitWindowCommand):
 | |
|     pass
 | |
| 
 | |
| 
 | |
| class GitGraph(object):
 | |
|     def run(self, edit=None):
 | |
|         filename = self.get_file_name()
 | |
|         self.run_command(
 | |
|             ['git', 'log', '--graph', '--pretty=%h -%d (%cr) (%ci) <%an> %s', '--abbrev-commit', '--no-color', '--decorate', '--date=relative', '--follow' if filename else None, '--', filename],
 | |
|             self.log_done
 | |
|         )
 | |
| 
 | |
|     def log_done(self, result):
 | |
|         self.scratch(result, title="Git Log Graph", syntax=plugin_file("syntax/Git Graph.tmLanguage"))
 | |
| 
 | |
| 
 | |
| class GitGraphCommand(GitGraph, GitTextCommand):
 | |
|     pass
 | |
| 
 | |
| 
 | |
| class GitGraphAllCommand(GitGraph, GitWindowCommand):
 | |
|     pass
 | |
| 
 | |
| 
 | |
| class GitOpenFileCommand(GitLog, GitWindowCommand):
 | |
|     def run(self):
 | |
|         self.run_command(['git', 'branch', '-a', '--no-color'], self.branch_done)
 | |
| 
 | |
|     def branch_done(self, result):
 | |
|         self.results = result.rstrip().split('\n')
 | |
|         self.quick_panel(self.results, self.branch_panel_done,
 | |
|             sublime.MONOSPACE_FONT)
 | |
| 
 | |
|     def branch_panel_done(self, picked):
 | |
|         if 0 > picked < len(self.results):
 | |
|             return
 | |
|         self.branch = self.results[picked].split(' ')[-1]
 | |
|         self.run_log(False, self.branch)
 | |
| 
 | |
|     def log_result(self, result_hash):
 | |
|         # the commit hash is the first thing on the second line
 | |
|         self.ref = result_hash
 | |
|         self.run_command(
 | |
|             ['git', 'ls-tree', '-r', '--full-tree', self.ref],
 | |
|             self.ls_done)
 | |
| 
 | |
|     def ls_done(self, result):
 | |
|         # Last two items are the ref and the file name
 | |
|         # p.s. has to be a list of lists; tuples cause errors later
 | |
|         self.results = [[match.group(2), match.group(1)] for match in re.finditer(r"\S+\s(\S+)\t(.+)", result)]
 | |
| 
 | |
|         self.quick_panel(self.results, self.ls_panel_done)
 | |
| 
 | |
|     def ls_panel_done(self, picked):
 | |
|         if 0 > picked < len(self.results):
 | |
|             return
 | |
|         item = self.results[picked]
 | |
| 
 | |
|         self.filename = item[0]
 | |
|         self.fileRef = item[1]
 | |
| 
 | |
|         self.run_command(
 | |
|             ['git', 'show', self.fileRef],
 | |
|             self.show_done)
 | |
| 
 | |
|     def show_done(self, result):
 | |
|         self.scratch(result, title="%s:%s" % (self.fileRef, self.filename))
 |