From c2e581450d2afbbabb2c753a463ab61c2792d80c Mon Sep 17 00:00:00 2001 From: David Lynch Date: Wed, 30 Jan 2013 21:13:35 -0600 Subject: [PATCH 01/80] Basicly working in Python3 for ST3. Not everything has even been tested. However, I know that status, add, commit, diff, and log all do work. --- add.py | 11 +++-- annotate.py | 2 +- commit.py | 6 +-- diff.py | 2 +- flow.py | 3 +- git.py | 130 +++++++++++++++++++++++++++++++++++---------------- history.py | 2 +- repo.py | 2 +- stash.py | 2 +- status.py | 6 ++- statusbar.py | 2 +- 11 files changed, 111 insertions(+), 57 deletions(-) diff --git a/add.py b/add.py index 6c0b3ad1..db17454a 100644 --- a/add.py +++ b/add.py @@ -2,16 +2,19 @@ import re import sublime -from git import GitTextCommand, GitWindowCommand, git_root -import status +from .git import GitTextCommand, GitWindowCommand, git_root +from .status import GitStatusCommand -class GitAddChoiceCommand(status.GitStatusCommand): +class GitAddChoiceCommand(GitStatusCommand): def status_filter(self, item): return super(GitAddChoiceCommand, self).status_filter(item) and not item[1].isspace() def show_status_list(self): - self.results = [[" + All Files", "apart from untracked files"], [" + All Files", "including untracked files"]] + self.results + self.results = [ + [" + All Files", "apart from untracked files"], + [" + All Files", "including untracked files"], + ] + [[a,''] for a in self.results] return super(GitAddChoiceCommand, self).show_status_list() def panel_followup(self, picked_status, picked_file, picked_index): diff --git a/annotate.py b/annotate.py index 84667451..81b528b6 100644 --- a/annotate.py +++ b/annotate.py @@ -4,7 +4,7 @@ import sublime import sublime_plugin -from git import git_root, GitTextCommand +from .git import git_root, GitTextCommand class GitClearAnnotationCommand(GitTextCommand): diff --git a/commit.py b/commit.py index 972e97c8..531c4d1a 100644 --- a/commit.py +++ b/commit.py @@ -4,8 +4,8 @@ import sublime import sublime_plugin -from git import GitTextCommand, GitWindowCommand, plugin_file, view_contents, _make_text_safeish -import add +from .git import GitTextCommand, GitWindowCommand, plugin_file, view_contents, _make_text_safeish +from .add import GitAddSelectedHunkCommand history = [] @@ -163,7 +163,7 @@ def panel_done(self, index): self.view.replace(self.edit, self.view.sel()[0], history[index] + '\n') -class GitCommitSelectedHunk(add.GitAddSelectedHunkCommand): +class GitCommitSelectedHunk(GitAddSelectedHunkCommand): def run(self, edit): self.run_command(['git', 'diff', '--no-color', self.get_file_name()], self.cull_diff) self.get_window().run_command('git_commit') diff --git a/diff.py b/diff.py index c9ea51b0..bec36dd9 100644 --- a/diff.py +++ b/diff.py @@ -1,5 +1,5 @@ import sublime -from git import GitTextCommand, GitWindowCommand +from .git import GitTextCommand, GitWindowCommand class GitDiff (object): diff --git a/flow.py b/flow.py index b6ba9d39..b3c5284e 100644 --- a/flow.py +++ b/flow.py @@ -1,5 +1,5 @@ import sublime -from git import GitWindowCommand +from .git import GitWindowCommand class GitFlowCommand(GitWindowCommand): @@ -7,6 +7,7 @@ def is_visible(self): s = sublime.load_settings("Git.sublime-settings") if s.get('flow'): return True + return False class GitFlowFeatureStartCommand(GitFlowCommand): diff --git a/git.py b/git.py index e4a7f5e5..b8786771 100644 --- a/git.py +++ b/git.py @@ -7,11 +7,11 @@ import os.path import time -# when sublime loads a plugin it's cd'd into the plugin directory. Thus -# __file__ is useless for my purposes. What I want is "Packages/Git", but -# allowing for the possibility that someone has renamed the file. -# Fun discovery: Sublime on windows still requires posix path separators. -PLUGIN_DIRECTORY = os.getcwd().replace(os.path.normpath(os.path.join(os.getcwd(), '..', '..')) + os.path.sep, '').replace(os.path.sep, '/') +# In a complete inversion from ST2, in ST3 when a plugin is loaded we +# actually can trust __file__. +# Goal is to get: "Packages/Git", allowing for people who rename things +FULL_PLUGIN_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) +PLUGIN_DIRECTORY = FULL_PLUGIN_DIRECTORY.replace(os.path.normpath(os.path.join(FULL_PLUGIN_DIRECTORY, '..', '..')) + os.path.sep, '').replace(os.path.sep, '/') git_root_cache = {} @@ -83,9 +83,49 @@ def _make_text_safeish(text, fallback_encoding, method='decode'): unitext = getattr(text, method)('utf-8') except (UnicodeEncodeError, UnicodeDecodeError): unitext = getattr(text, method)(fallback_encoding) + except AttributeError: + # strongly implies we're already unicode, but just in case let's cast + # to string + unitext = str(text) return unitext +def _test_paths_for_executable(paths, test_file): + for directory in paths: + file_path = os.path.join(directory, test_file) + if os.path.exists(file_path) and os.access(file_path, os.X_OK): + return file_path +def find_git(): + # It turns out to be difficult to reliably run git, with varying paths + # and subprocess environments across different platforms. So. Let's hack + # this a bit. + # (Yes, I could fall back on a hardline "set your system path properly" + # attitude. But that involves a lot more arguing with people.) + path = os.environ.get('PATH', '').split(os.pathsep) + if os.name == 'nt': + git_cmd = 'git.exe' + else: + git_cmd = 'git' + + git_path = _test_paths_for_executable(path, git_cmd) + + if not git_path: + # /usr/local/bin:/usr/local/git/bin + if os.name == 'nt': + extra_paths = ( + os.path.join(os.environ["ProgramFiles"], "Git", "bin"), + os.path.join(os.environ["ProgramFiles(x86)"], "Git", "bin"), + ) + else: + extra_paths = ( + '/usr/local/bin', + '/usr/local/git/bin', + ) + git_path = _test_paths_for_executable(extra_paths, git_cmd) + return git_path +GIT = find_git() + + class CommandThread(threading.Thread): def __init__(self, command, on_done, working_dir="", fallback_encoding="", **kwargs): threading.Thread.__init__(self) @@ -93,7 +133,7 @@ def __init__(self, command, on_done, working_dir="", fallback_encoding="", **kwa self.on_done = on_done self.working_dir = working_dir if "stdin" in kwargs: - self.stdin = kwargs["stdin"] + self.stdin = kwargs["stdin"].encode() else: self.stdin = None if "stdout" in kwargs: @@ -105,37 +145,41 @@ def __init__(self, command, on_done, working_dir="", fallback_encoding="", **kwa def run(self): try: - # Ignore directories that no longer exist - if os.path.isdir(self.working_dir): - - # Per http://bugs.python.org/issue8557 shell=True is required to - # get $PATH on Windows. Yay portable code. - shell = os.name == 'nt' - if self.working_dir != "": - os.chdir(self.working_dir) - - proc = subprocess.Popen(self.command, - stdout=self.stdout, stderr=subprocess.STDOUT, - stdin=subprocess.PIPE, - shell=shell, universal_newlines=True) - output = proc.communicate(self.stdin)[0] - if not output: - output = '' - # if sublime's python gets bumped to 2.7 we can just do: - # output = subprocess.check_output(self.command) - main_thread(self.on_done, - _make_text_safeish(output, self.fallback_encoding), **self.kwargs) - - except subprocess.CalledProcessError, e: + if not os.path.isdir(self.working_dir): + return + + if self.working_dir != "": + os.chdir(self.working_dir) + + # universal_newlines seems to break `log` in python3 + proc = subprocess.Popen(self.command, + stdout=self.stdout, stderr=subprocess.STDOUT, + stdin=subprocess.PIPE, + shell=False, universal_newlines=False) + output = proc.communicate(self.stdin)[0] + if not output: + output = '' + + main_thread(self.on_done, + _make_text_safeish(output, self.fallback_encoding), **self.kwargs) + except subprocess.CalledProcessError as e: main_thread(self.on_done, e.returncode) - except OSError, e: + except OSError as e: if e.errno == 2: main_thread(sublime.error_message, "Git binary could not be found in PATH\n\nConsider using the git_command setting for the Git plugin\n\nPATH is: %s" % os.environ['PATH']) else: raise e +class GitScratchOutputCommand(sublime_plugin.TextCommand): + def run(self, edit, output = '', output_file = None, clear = False): + if clear: + region = sublime.Region(0, self.view.size()) + self.view.erase(edit, region) + self.view.insert(edit, 0, output) + + # A base for all commands class GitCommand(object): may_change_files = False @@ -152,8 +196,11 @@ def run_command(self, command, callback=None, show_status=True, s = sublime.load_settings("Git.sublime-settings") if s.get('save_first') and self.active_view() and self.active_view().is_dirty() and not no_save: self.active_view().run_command('save') - if command[0] == 'git' and s.get('git_command'): - command[0] = s.get('git_command') + if command[0] == 'git': + if s.get('git_command'): + command[0] = s.get('git_command') + elif GIT: + command[0] = GIT if command[0] == 'git-flow' and s.get('git_flow_command'): command[0] = s.get('git_flow_command') if not callback: @@ -172,7 +219,7 @@ def generic_done(self, result): result = "WARNING: Current view is dirty.\n\n" else: # just asking the current file to be re-opened doesn't do anything - print "reverting" + print("reverting") position = self.active_view().viewport_position() self.active_view().run_command('revert') do_when(lambda: not self.active_view().is_loading(), lambda: self.active_view().set_viewport_position(position, False)) @@ -189,12 +236,11 @@ def generic_done(self, result): def _output_to_view(self, output_file, output, clear=False, syntax="Packages/Diff/Diff.tmLanguage", **kwargs): output_file.set_syntax_file(syntax) - edit = output_file.begin_edit() - if clear: - region = sublime.Region(0, self.output_view.size()) - output_file.erase(edit, region) - output_file.insert(edit, 0, output) - output_file.end_edit(edit) + args = { + 'output': output, + 'clear': clear + } + output_file.run_command('git_scratch_output', args) def scratch(self, output, title=False, position=None, **kwargs): scratch_file = self.get_window().new_file() @@ -240,7 +286,8 @@ def fallback_encoding(self): # only one. def is_enabled(self): if self._active_file_name() or len(self.window.folders()) == 1: - return git_root(self.get_working_dir()) + return bool(git_root(self.get_working_dir())) + return False def get_file_name(self): return '' @@ -273,7 +320,8 @@ def active_view(self): def is_enabled(self): # First, is this actually a file on the file system? if self.view.file_name() and len(self.view.file_name()) > 0: - return git_root(self.get_working_dir()) + return bool(git_root(self.get_working_dir())) + return False def get_file_name(self): return os.path.basename(self.view.file_name()) @@ -317,7 +365,7 @@ def on_input(self, command): return import shlex command_splitted = ['git'] + shlex.split(command) - print command_splitted + print(command_splitted) self.run_command(command_splitted) diff --git a/history.py b/history.py index 733c4aa5..57ab643f 100644 --- a/history.py +++ b/history.py @@ -2,7 +2,7 @@ import re import sublime -from git import GitTextCommand, GitWindowCommand, plugin_file +from .git import GitTextCommand, GitWindowCommand, plugin_file class GitBlameCommand(GitTextCommand): diff --git a/repo.py b/repo.py index 1a293a64..519f4587 100644 --- a/repo.py +++ b/repo.py @@ -1,7 +1,7 @@ import os import sublime -from git import GitTextCommand, GitWindowCommand, git_root_exist +from .git import GitTextCommand, GitWindowCommand, git_root_exist class GitInit(object): diff --git a/stash.py b/stash.py index d76fed89..842e027b 100644 --- a/stash.py +++ b/stash.py @@ -1,4 +1,4 @@ -from git import GitWindowCommand +from .git import GitWindowCommand class GitStashCommand(GitWindowCommand): diff --git a/status.py b/status.py index 3f490af8..bb0139af 100644 --- a/status.py +++ b/status.py @@ -2,7 +2,7 @@ import re import sublime -from git import GitWindowCommand, git_root +from .git import GitWindowCommand, git_root class GitStatusCommand(GitWindowCommand): @@ -12,7 +12,7 @@ def run(self): self.run_command(['git', 'status', '--porcelain'], self.status_done) def status_done(self, result): - self.results = filter(self.status_filter, result.rstrip().split('\n')) + self.results = list(filter(self.status_filter, result.rstrip().split('\n'))) if len(self.results): self.show_status_list() else: @@ -32,6 +32,8 @@ def panel_done(self, picked): if 0 > picked < len(self.results): return picked_file = self.results[picked] + if isinstance(picked_file, (list, tuple)): + picked_file = picked_file[0] # first 2 characters are status codes, the third is a space picked_status = picked_file[:2] picked_file = picked_file[3:] diff --git a/statusbar.py b/statusbar.py index 11b6a8d7..0e90693d 100644 --- a/statusbar.py +++ b/statusbar.py @@ -2,7 +2,7 @@ import sublime import sublime_plugin -from git import GitTextCommand +from .git import GitTextCommand class GitBranchStatusListener(sublime_plugin.EventListener): From 4662f5434f44727ac5c03a806ded4df67fce24be Mon Sep 17 00:00:00 2001 From: David Lynch Date: Wed, 30 Jan 2013 21:44:48 -0600 Subject: [PATCH 02/80] README title --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 908e756a..73eba1fa 100644 --- a/README.markdown +++ b/README.markdown @@ -1,4 +1,4 @@ -# Sublime Text 2 plugin: git +# Sublime Text 3 plugin: git Git integration: it's pretty handy. Who knew, right? From 7a2e72822aea2e4a0520c4df47080f43f635677e Mon Sep 17 00:00:00 2001 From: Luka Kladaric Date: Sun, 17 Feb 2013 04:35:10 +0100 Subject: [PATCH 03/80] fix for ST3 --- annotate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/annotate.py b/annotate.py index 81b528b6..318360c6 100644 --- a/annotate.py +++ b/annotate.py @@ -57,7 +57,7 @@ def run(self, view): self.run_command(['git', 'show', 'HEAD:{0}'.format(repo_file)], show_status=False, no_save=True, callback=self.compare_tmp, stdout=self.tmp) def compare_tmp(self, result, stdout=None): - all_text = self.view.substr(sublime.Region(0, self.view.size())).encode("utf-8") + all_text = self.view.substr(sublime.Region(0, self.view.size())) self.run_command(['diff', '-u', self.tmp.name, '-'], stdin=all_text, no_save=True, show_status=False, callback=self.parse_diff) # This is where the magic happens. At the moment, only one chunk format is supported. While From ecfa538dba1235c77b84f544a844b60664f7931b Mon Sep 17 00:00:00 2001 From: Marshall Thornton Date: Tue, 19 Feb 2013 19:28:24 -0800 Subject: [PATCH 04/80] Added startupinfo to the Popen call when on Windows since it requires some options that only exist for Windows in order to start subprocesses in background --- git.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/git.py b/git.py index b8786771..f3d57406 100644 --- a/git.py +++ b/git.py @@ -152,10 +152,16 @@ def run(self): if self.working_dir != "": os.chdir(self.working_dir) + # Windows needs startupinfo in order to start process in background + startupinfo = None + if os.name == 'nt': + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + # universal_newlines seems to break `log` in python3 proc = subprocess.Popen(self.command, stdout=self.stdout, stderr=subprocess.STDOUT, - stdin=subprocess.PIPE, + stdin=subprocess.PIPE, startupinfo=startupinfo, shell=False, universal_newlines=False) output = proc.communicate(self.stdin)[0] if not output: From d0144bbe06b6e86409ba0a3f59d9fa81c19f7a8b Mon Sep 17 00:00:00 2001 From: David Lynch Date: Wed, 27 Feb 2013 12:47:35 -0600 Subject: [PATCH 05/80] Allow diff syntax to be overridden --- Git.sublime-settings | 3 +++ diff.py | 9 ++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Git.sublime-settings b/Git.sublime-settings index b0aa30ab..06e73cd5 100644 --- a/Git.sublime-settings +++ b/Git.sublime-settings @@ -39,4 +39,7 @@ // Symbols for quick git status in status bar ,"statusbar_status": true ,"statusbar_status_symbols" : {"modified": "≠", "added": "+", "deleted": "×", "untracked": "?", "conflicts": "‼", "renamed":"R", "copied":"C", "clean": "√", "separator": " "} + + // e.g. "Packages/Git/syntax/Git Commit Message.tmLanguage" + ,"diff_syntax": "Packages/Diff/Diff.tmLanguage" } diff --git a/diff.py b/diff.py index bec36dd9..555e71e0 100644 --- a/diff.py +++ b/diff.py @@ -12,10 +12,11 @@ def diff_done(self, result): self.panel("No output") return s = sublime.load_settings("Git.sublime-settings") + syntax = s.get("diff_syntax", "Packages/Diff/Diff.tmLanguage") if s.get('diff_panel'): - view = self.panel(result) + view = self.panel(result, syntax=syntax) else: - view = self.scratch(result, title="Git Diff") + view = self.scratch(result, title="Git Diff", syntax=syntax) lines_inserted = view.find_all(r'^\+[^+]{2} ') lines_deleted = view.find_all(r'^-[^-]{2} ') @@ -33,7 +34,9 @@ def diff_done(self, result): if not result.strip(): self.panel("No output") return - self.scratch(result, title="Git Diff") + s = sublime.load_settings("Git.sublime-settings") + syntax = s.get("diff_syntax", "Packages/Diff/Diff.tmLanguage") + self.scratch(result, title="Git Diff", syntax=syntax) class GitDiffCommand(GitDiff, GitTextCommand): From 562a5738fbbc0e44922c07bd32139c5d7d694439 Mon Sep 17 00:00:00 2001 From: David Lynch Date: Wed, 20 Mar 2013 12:26:11 -0500 Subject: [PATCH 06/80] Add a tmPreferences for commit messages Makes toggle-comments work --- syntax/Git Commit Message.tmPreferences | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 syntax/Git Commit Message.tmPreferences diff --git a/syntax/Git Commit Message.tmPreferences b/syntax/Git Commit Message.tmPreferences new file mode 100644 index 00000000..5fa9c62b --- /dev/null +++ b/syntax/Git Commit Message.tmPreferences @@ -0,0 +1,22 @@ + + + + + name + Git Commit Message + scope + text.git-commit + settings + + shellVariables + + + name + TM_COMMENT_START + value + # + + + + + From c277eb6a121a2d67a6e92bcc15537bbd969d41a2 Mon Sep 17 00:00:00 2001 From: Timothy Elliott Date: Mon, 25 Mar 2013 16:14:50 -0700 Subject: [PATCH 07/80] xrange no longer exists in Python 3. range now behaves like xrange did in Python 2. http://docs.python.org/3.1/whatsnew/3.0.html --- annotate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/annotate.py b/annotate.py index 318360c6..45357067 100644 --- a/annotate.py +++ b/annotate.py @@ -115,7 +115,7 @@ def annotate(self, diff): if change_type == '-': full_region = self.view.full_line(self.view.text_point(line - 1, 0)) position = full_region.begin() - for i in xrange(full_region.size()): + for i in range(full_region.size()): typed_diff[change_type].append(sublime.Region(position + i)) else: point = self.view.text_point(line, 0) From 5918cc168d51cce73bc06c3fd37c02f000658c1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20B=C3=B6hm?= Date: Tue, 7 May 2013 18:43:13 +0200 Subject: [PATCH 08/80] Added a gitk_command configuration value to specify the path for the gitk binary. --- git.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/git.py b/git.py index f3d57406..3e3b7e8d 100644 --- a/git.py +++ b/git.py @@ -207,6 +207,8 @@ def run_command(self, command, callback=None, show_status=True, command[0] = s.get('git_command') elif GIT: command[0] = GIT + if command[0] == 'gitk' and s.get('gitk_command'): + command[0] = s.get('gitk_command') if command[0] == 'git-flow' and s.get('git_flow_command'): command[0] = s.get('git_flow_command') if not callback: From 2872453afabc2295a3183d945dbb581e385ca77f Mon Sep 17 00:00:00 2001 From: Chris--- Date: Thu, 15 Aug 2013 17:18:44 +0100 Subject: [PATCH 09/80] add delete and show tag command --- Default.sublime-commands | 8 ++++++++ repo.py | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/Default.sublime-commands b/Default.sublime-commands index 85f740c2..25600650 100644 --- a/Default.sublime-commands +++ b/Default.sublime-commands @@ -10,6 +10,10 @@ "caption": "Git: New Tag", "command": "git_new_tag" } + ,{ + "caption": "Git: Delete Tag", + "command": "git_delete_tag" + } ,{ "caption": "Git: Show Tags", "command": "git_show_tags" @@ -18,6 +22,10 @@ "caption": "Git: Push Tags", "command": "git_push_tags" } + ,{ + "caption": "Git: Checkout Tag", + "command": "git_checkout_tag" + } ,{ "caption": "Git: Log Current File", "command": "git_log" diff --git a/repo.py b/repo.py index 519f4587..7ed1ac4f 100644 --- a/repo.py +++ b/repo.py @@ -87,6 +87,25 @@ def on_input(self, tagname): self.run_command(['git', 'tag', tagname]) +class GitDeleteTagCommand(GitWindowCommand): + def run(self): + self.run_command(['git', 'tag'], self.fetch_tag) + + def fetch_tag(self, result): + if result.strip() == "": + sublime.status_message("No Tags provided.") + return + self.results = result.rstrip().split('\n') + self.quick_panel(self.results, self.panel_done) + + def panel_done(self, picked): + if 0 > picked < len(self.results): + return + picked_tag = self.results[picked] + picked_tag = picked_tag.strip() + self.run_command(['git', 'tag', '-d', picked_tag]) + + class GitShowTagsCommand(GitWindowCommand): def run(self): self.run_command(['git', 'tag'], self.fetch_tag) @@ -108,6 +127,25 @@ def run(self): self.run_command(['git', 'push', '--tags']) +class GitCheckoutTagCommand(GitWindowCommand): + def run(self): + self.run_command(['git', 'tag'], self.fetch_tag) + + def fetch_tag(self, result): + if result.strip() == "": + sublime.status_message("No Tags provided.") + return + self.results = result.rstrip().split('\n') + self.quick_panel(self.results, self.panel_done) + + def panel_done(self, picked): + if 0 > picked < len(self.results): + return + picked_tag = self.results[picked] + picked_tag = picked_tag.strip() + self.run_command(['git', 'checkout', "tags/%s" % picked_tag]) + + class GitCheckoutCommand(GitTextCommand): may_change_files = True From 996b5096377cdfec066840d08e969b731c0d47b8 Mon Sep 17 00:00:00 2001 From: Chris--- Date: Fri, 16 Aug 2013 00:23:34 +0100 Subject: [PATCH 10/80] ok_cancel_dialog for delete tag command --- repo.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/repo.py b/repo.py index 7ed1ac4f..6c199e43 100644 --- a/repo.py +++ b/repo.py @@ -103,7 +103,8 @@ def panel_done(self, picked): return picked_tag = self.results[picked] picked_tag = picked_tag.strip() - self.run_command(['git', 'tag', '-d', picked_tag]) + if sublime.ok_cancel_dialog("Delete \"%s\" Tag?" % picked_tag, "Delete"): + self.run_command(['git', 'tag', '-d', picked_tag]) class GitShowTagsCommand(GitWindowCommand): From 4da23840791bf6146aac29f11bceab94890b0e24 Mon Sep 17 00:00:00 2001 From: David Lynch Date: Wed, 28 Aug 2013 16:52:09 -0500 Subject: [PATCH 11/80] Check for a git_binary global preference Just like GitGutter. --- git.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/git.py b/git.py index f3d57406..5ad31989 100644 --- a/git.py +++ b/git.py @@ -203,8 +203,9 @@ def run_command(self, command, callback=None, show_status=True, if s.get('save_first') and self.active_view() and self.active_view().is_dirty() and not no_save: self.active_view().run_command('save') if command[0] == 'git': - if s.get('git_command'): - command[0] = s.get('git_command') + us = sublime.load_settings('Preferences.sublime-settings') + if s.get('git_command') or us.get('git_binary'): + command[0] = s.get('git_command') or us.get('git_binary') elif GIT: command[0] = GIT if command[0] == 'git-flow' and s.get('git_flow_command'): From 1549ed9f04f4c88624a6bc52a1b2e2e7d6fbc569 Mon Sep 17 00:00:00 2001 From: David Capello Date: Sat, 29 Dec 2012 02:52:51 -0300 Subject: [PATCH 12/80] Add git_goto_diff command to jump to the diff line with Enter key --- Default.sublime-keymap | 6 ++++ diff.py | 72 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 Default.sublime-keymap diff --git a/Default.sublime-keymap b/Default.sublime-keymap new file mode 100644 index 00000000..92ee9df7 --- /dev/null +++ b/Default.sublime-keymap @@ -0,0 +1,6 @@ +[ + {"keys": ["enter"], "command": "git_goto_diff", + "context": [{"key": "selector", "operand": "markup.inserted.diff"}]}, + {"keys": ["enter"], "command": "git_goto_diff", + "context": [{"key": "selector", "operand": "markup.deleted.diff"}]} +] diff --git a/diff.py b/diff.py index 555e71e0..dc9e6ade 100644 --- a/diff.py +++ b/diff.py @@ -1,5 +1,30 @@ import sublime +import re from .git import GitTextCommand, GitWindowCommand +import functools + +# Stores the path related to a "Git Diff" view. +git_diff_path = {} + + +def do_when(conditional, callback, *args, **kwargs): + if conditional(): + return callback(*args, **kwargs) + sublime.set_timeout(functools.partial(do_when, conditional, callback, *args, **kwargs), 50) + + +def goto_xy(view, line, col): + view.run_command("goto_line", {"line": line}) + for i in range(col): + view.run_command("move", {"by": "characters", "forward": True}) + + +class GitDiffListener(sublime_plugin.EventListener): + def on_close(self, view): + global git_diff_path + if view.name() != "Git Diff": + return + git_diff_path.pop(view.id()) class GitDiff (object): @@ -8,6 +33,8 @@ def run(self, edit=None): self.diff_done) def diff_done(self, result): + global git_diff_path + if not result.strip(): self.panel("No output") return @@ -24,6 +51,8 @@ def diff_done(self, result): view.add_regions("inserted", lines_inserted, "markup.inserted.diff", "dot", sublime.HIDDEN) view.add_regions("deleted", lines_deleted, "markup.deleted.diff", "dot", sublime.HIDDEN) + git_diff_path[view.id()] = self.get_working_dir() + class GitDiffCommit (object): def run(self, edit=None): @@ -63,3 +92,46 @@ class GitDiffToolCommand(GitDiffTool, GitTextCommand): class GitDiffToolAll(GitWindowCommand): def run(self): self.run_command(['git', 'difftool']) + + +class GitGotoDiff(sublime_plugin.TextCommand): + def run(self, edit): + v = self.view + if v.name() != "Git Diff": + return + + beg = v.sel()[0].a # Current position in selection + pt = v.line(beg).a # First position in the current diff line + column = beg - pt - 1 # The current column (-1 because the first char in diff file) + + file_name = None + hunk_line = None + line_offset = 0 + + while pt > 0: + line = v.line(pt) + lineContent = v.substr(line) + if lineContent.startswith("@@"): + if not hunk_line: + hunk_line = lineContent + elif lineContent.startswith("+++ b/"): + file_name = v.substr(sublime.Region(line.a+6, line.b)) + break + elif not hunk_line and not lineContent.startswith("-"): + line_offset = line_offset+1 + + pt = v.line(pt-1).a + + hunk = re.match(r"^@@ -(\d+),(\d+) \+(\d+),(\d+) @@.*", hunk_line) + if not hunk: + sublime.status_message("No hunk info") + return + + hunk_start_line = hunk.group(3) + goto_line = int(hunk_start_line) + line_offset - 1 + + file_name = os.path.join(git_diff_path[v.id()], file_name) + + new_view = self.view.window().open_file(file_name) + do_when(lambda: not new_view.is_loading(), + lambda: goto_xy(new_view, goto_line, column)) From 02a635b8b96f73046d065dc2118422d4ea904cb1 Mon Sep 17 00:00:00 2001 From: David Capello Date: Sat, 29 Dec 2012 13:38:00 -0300 Subject: [PATCH 13/80] Store git_working_dir in the view settings instead of using a global var --- diff.py | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/diff.py b/diff.py index dc9e6ade..56506585 100644 --- a/diff.py +++ b/diff.py @@ -3,9 +3,6 @@ from .git import GitTextCommand, GitWindowCommand import functools -# Stores the path related to a "Git Diff" view. -git_diff_path = {} - def do_when(conditional, callback, *args, **kwargs): if conditional(): @@ -19,22 +16,12 @@ def goto_xy(view, line, col): view.run_command("move", {"by": "characters", "forward": True}) -class GitDiffListener(sublime_plugin.EventListener): - def on_close(self, view): - global git_diff_path - if view.name() != "Git Diff": - return - git_diff_path.pop(view.id()) - - class GitDiff (object): def run(self, edit=None): self.run_command(['git', 'diff', '--no-color', '--', self.get_file_name()], self.diff_done) def diff_done(self, result): - global git_diff_path - if not result.strip(): self.panel("No output") return @@ -51,7 +38,9 @@ def diff_done(self, result): view.add_regions("inserted", lines_inserted, "markup.inserted.diff", "dot", sublime.HIDDEN) view.add_regions("deleted", lines_deleted, "markup.deleted.diff", "dot", sublime.HIDDEN) - git_diff_path[view.id()] = self.get_working_dir() + # Store the working directory in the view so we can resolve relative paths + # when the user presses Enter key. + view.settings().set("git_working_dir", self.get_working_dir()) class GitDiffCommit (object): @@ -130,7 +119,7 @@ def run(self, edit): hunk_start_line = hunk.group(3) goto_line = int(hunk_start_line) + line_offset - 1 - file_name = os.path.join(git_diff_path[v.id()], file_name) + file_name = os.path.join(v.settings().get("git_working_dir"), file_name) new_view = self.view.window().open_file(file_name) do_when(lambda: not new_view.is_loading(), From bc19851eb40a8de6b90961a380ce84fb4c5c4463 Mon Sep 17 00:00:00 2001 From: David Capello Date: Sun, 17 Mar 2013 12:35:54 -0300 Subject: [PATCH 14/80] Use git root directory to create absolute path names in GitGotoDiff Problem reported by @vanrijn (Jason Kasper) --- diff.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/diff.py b/diff.py index 56506585..eaf3764a 100644 --- a/diff.py +++ b/diff.py @@ -1,6 +1,6 @@ import sublime import re -from .git import GitTextCommand, GitWindowCommand +from .git import git_root, GitTextCommand, GitWindowCommand import functools @@ -38,9 +38,9 @@ def diff_done(self, result): view.add_regions("inserted", lines_inserted, "markup.inserted.diff", "dot", sublime.HIDDEN) view.add_regions("deleted", lines_deleted, "markup.deleted.diff", "dot", sublime.HIDDEN) - # Store the working directory in the view so we can resolve relative paths + # Store the git root directory in the view so we can resolve relative paths # when the user presses Enter key. - view.settings().set("git_working_dir", self.get_working_dir()) + view.settings().set("git_root_dir", git_root(self.get_working_dir())) class GitDiffCommit (object): @@ -119,7 +119,7 @@ def run(self, edit): hunk_start_line = hunk.group(3) goto_line = int(hunk_start_line) + line_offset - 1 - file_name = os.path.join(v.settings().get("git_working_dir"), file_name) + file_name = os.path.join(v.settings().get("git_root_dir"), file_name) new_view = self.view.window().open_file(file_name) do_when(lambda: not new_view.is_loading(), From 98e12d73fc1458542f7b9ee345adea6157ecdfca Mon Sep 17 00:00:00 2001 From: Jason 'vanRijn' Kasper Date: Tue, 19 Mar 2013 18:32:08 -0400 Subject: [PATCH 15/80] Adding double-click mousemap file so we can navigate git diff buffer that way too. --- Default.sublime-mousemap | 9 +++++++++ diff.py | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 Default.sublime-mousemap diff --git a/Default.sublime-mousemap b/Default.sublime-mousemap new file mode 100644 index 00000000..975d5535 --- /dev/null +++ b/Default.sublime-mousemap @@ -0,0 +1,9 @@ +[ + // Double-click to go to results + { + "button": "button1", "count": 2, + "press_command": "drag_select", + "press_args": {"by": "words"}, + "command": "git_goto_diff" + } +] diff --git a/diff.py b/diff.py index eaf3764a..738a1de9 100644 --- a/diff.py +++ b/diff.py @@ -39,7 +39,7 @@ def diff_done(self, result): view.add_regions("deleted", lines_deleted, "markup.deleted.diff", "dot", sublime.HIDDEN) # Store the git root directory in the view so we can resolve relative paths - # when the user presses Enter key. + # when the user wants to navigate to the source file. view.settings().set("git_root_dir", git_root(self.get_working_dir())) From 0edf464fc0eb2a7e6dd0fc67692a24ca0d117743 Mon Sep 17 00:00:00 2001 From: Jason 'vanRijn' Kasper Date: Tue, 19 Mar 2013 20:50:32 -0400 Subject: [PATCH 16/80] removed mousemap --- Default.sublime-mousemap | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 Default.sublime-mousemap diff --git a/Default.sublime-mousemap b/Default.sublime-mousemap deleted file mode 100644 index 975d5535..00000000 --- a/Default.sublime-mousemap +++ /dev/null @@ -1,9 +0,0 @@ -[ - // Double-click to go to results - { - "button": "button1", "count": 2, - "press_command": "drag_select", - "press_args": {"by": "words"}, - "command": "git_goto_diff" - } -] From 3cb11be969c95002b9d2680f699bcc4591697e52 Mon Sep 17 00:00:00 2001 From: Jason 'vanRijn' Kasper Date: Thu, 21 Mar 2013 16:48:27 -0400 Subject: [PATCH 17/80] Fix incorrect assumption that git_root_dir is populated and allow GitGotoDiff to work with Git Custom commands --- diff.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/diff.py b/diff.py index 738a1de9..e982e1b9 100644 --- a/diff.py +++ b/diff.py @@ -86,7 +86,11 @@ def run(self): class GitGotoDiff(sublime_plugin.TextCommand): def run(self, edit): v = self.view - if v.name() != "Git Diff": + view_scope_name = v.scope_name(v.sel()[0].a) + scope_markup_inserted = ("markup.inserted.diff" in view_scope_name) + scope_markup_deleted = ("markup.deleted.diff" in view_scope_name) + + if not scope_markup_inserted and not scope_markup_deleted: return beg = v.sel()[0].a # Current position in selection @@ -119,7 +123,9 @@ def run(self, edit): hunk_start_line = hunk.group(3) goto_line = int(hunk_start_line) + line_offset - 1 - file_name = os.path.join(v.settings().get("git_root_dir"), file_name) + git_root_dir = v.settings().get("git_root_dir") + if git_root_dir: + file_name = os.path.join(git_root_dir, file_name) new_view = self.view.window().open_file(file_name) do_when(lambda: not new_view.is_loading(), From 9b86aa332514f94afd9ff2d984bddbc678e4b37d Mon Sep 17 00:00:00 2001 From: Jason 'vanRijn' Kasper Date: Thu, 21 Mar 2013 18:59:32 -0400 Subject: [PATCH 18/80] Sanity-checking git_root_dir and if it's not valid for the current hunk's file, prompt the user to fix it rather than just failing. --- diff.py | 46 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/diff.py b/diff.py index e982e1b9..882e7172 100644 --- a/diff.py +++ b/diff.py @@ -93,11 +93,11 @@ def run(self, edit): if not scope_markup_inserted and not scope_markup_deleted: return - beg = v.sel()[0].a # Current position in selection - pt = v.line(beg).a # First position in the current diff line - column = beg - pt - 1 # The current column (-1 because the first char in diff file) + beg = v.sel()[0].a # Current position in selection + pt = v.line(beg).a # First position in the current diff line + self.column = beg - pt - 1 # The current column (-1 because the first char in diff file) - file_name = None + self.file_name = None hunk_line = None line_offset = 0 @@ -108,7 +108,7 @@ def run(self, edit): if not hunk_line: hunk_line = lineContent elif lineContent.startswith("+++ b/"): - file_name = v.substr(sublime.Region(line.a+6, line.b)) + self.file_name = v.substr(sublime.Region(line.a+6, line.b)) break elif not hunk_line and not lineContent.startswith("-"): line_offset = line_offset+1 @@ -121,12 +121,40 @@ def run(self, edit): return hunk_start_line = hunk.group(3) - goto_line = int(hunk_start_line) + line_offset - 1 + self.goto_line = int(hunk_start_line) + line_offset - 1 git_root_dir = v.settings().get("git_root_dir") + + # Sanity check and see if the file we're going to try to open even + # exists. If it does not, prompt the user for the correct base directory + # to use for their diff. + full_path_file_name = self.file_name if git_root_dir: - file_name = os.path.join(git_root_dir, file_name) + full_path_file_name = os.path.join(git_root_dir, self.file_name) + else: + git_root_dir = "" + + if not os.path.isfile(full_path_file_name): + caption = "Enter base directory for file '%s':" % self.file_name + v.window().show_input_panel(caption, + git_root_dir, + self.on_path_confirmed, + None, + None) + else: + self.on_path_confirmed(git_root_dir) + + def on_path_confirmed(self, git_root_dir): + v = self.view + old_git_root_dir = v.settings().get("git_root_dir") + + # If the user provided a new git_root_dir, save it in the view settings + # so they only have to fix it once + if old_git_root_dir != git_root_dir: + v.settings().set("git_root_dir", git_root_dir) + + full_path_file_name = os.path.join(git_root_dir, self.file_name) - new_view = self.view.window().open_file(file_name) + new_view = v.window().open_file(full_path_file_name) do_when(lambda: not new_view.is_loading(), - lambda: goto_xy(new_view, goto_line, column)) + lambda: goto_xy(new_view, self.goto_line, self.column)) From 3e570efbfad87c4a91ca20c1e75db2444e2bc9b8 Mon Sep 17 00:00:00 2001 From: Jason 'vanRijn' Kasper Date: Thu, 28 Mar 2013 16:22:40 -0400 Subject: [PATCH 19/80] Stripping whitespace off filename to fix problem with filename with spaces. --- diff.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diff.py b/diff.py index 882e7172..fcccce99 100644 --- a/diff.py +++ b/diff.py @@ -108,7 +108,7 @@ def run(self, edit): if not hunk_line: hunk_line = lineContent elif lineContent.startswith("+++ b/"): - self.file_name = v.substr(sublime.Region(line.a+6, line.b)) + self.file_name = v.substr(sublime.Region(line.a+6, line.b)).strip() break elif not hunk_line and not lineContent.startswith("-"): line_offset = line_offset+1 From 51b2ee716d8ee8a2d5053cd006467cd17ac3f230 Mon Sep 17 00:00:00 2001 From: "German M. Bravo" Date: Sun, 17 Feb 2013 11:40:49 -0600 Subject: [PATCH 20/80] Open all diffs in diff_panel --- diff.py | 7 +++++-- status.py | 6 +++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/diff.py b/diff.py index fcccce99..e9461ea0 100644 --- a/diff.py +++ b/diff.py @@ -53,8 +53,11 @@ def diff_done(self, result): self.panel("No output") return s = sublime.load_settings("Git.sublime-settings") - syntax = s.get("diff_syntax", "Packages/Diff/Diff.tmLanguage") - self.scratch(result, title="Git Diff", syntax=syntax) + if s.get('diff_panel'): + self.panel(result) + else: + syntax = s.get("diff_syntax", "Packages/Diff/Diff.tmLanguage") + self.scratch(result, title="Git Diff", syntax=syntax) class GitDiffCommand(GitDiff, GitTextCommand): diff --git a/status.py b/status.py index bb0139af..1bf2f2e7 100644 --- a/status.py +++ b/status.py @@ -54,7 +54,11 @@ def panel_followup(self, picked_status, picked_file, picked_index): def diff_done(self, result): if not result.strip(): return - self.scratch(result, title="Git Diff") + s = sublime.load_settings("Git.sublime-settings") + if s.get('diff_panel'): + self.panel(result) + else: + self.scratch(result, title="Git Diff") class GitOpenModifiedFilesCommand(GitStatusCommand): From c7cd8956a4127001dc08f856b6492e1c08239686 Mon Sep 17 00:00:00 2001 From: "German M. Bravo" Date: Sun, 17 Feb 2013 11:42:53 -0600 Subject: [PATCH 21/80] Split optimizations --- history.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/history.py b/history.py index 57ab643f..07c05aa5 100644 --- a/history.py +++ b/history.py @@ -65,7 +65,8 @@ def log_panel_done(self, picked): return item = self.results[picked] # the commit hash is the first thing on the second line - self.log_result(item[1].split(' ')[0]) + ref = item[0].split(' ', 1)[0] + self.log_result(ref) def log_result(self, ref): # I'm not certain I should have the file name here; it restricts the @@ -105,7 +106,7 @@ def panel_done(self, picked): return item = self.results[picked] # the commit hash is the first thing on the second line - ref = item[1].split(' ')[0] + ref = item[1].split(' ', 1)[0] self.run_command( ['git', 'show', '%s:%s' % (ref, self.get_relative_file_name())], self.details_done, @@ -156,7 +157,7 @@ def branch_done(self, result): def branch_panel_done(self, picked): if 0 > picked < len(self.results): return - self.branch = self.results[picked].split(' ')[-1] + self.branch = self.results[picked].rsplit(' ', 1)[-1] self.run_log(False, self.branch) def log_result(self, result_hash): From 40b03651ac1502cb19ed59c224c0a5b2ab573f63 Mon Sep 17 00:00:00 2001 From: "German M. Bravo" Date: Sun, 17 Feb 2013 11:43:48 -0600 Subject: [PATCH 22/80] Added --follow to diffs and added revision numbers --- diff.py | 54 ++++++++++++++++++++++++++++++++++++++++++++++++----- history.py | 55 ++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 98 insertions(+), 11 deletions(-) diff --git a/diff.py b/diff.py index e9461ea0..d0a8fcca 100644 --- a/diff.py +++ b/diff.py @@ -18,10 +18,54 @@ def goto_xy(view, line, col): class GitDiff (object): def run(self, edit=None): - self.run_command(['git', 'diff', '--no-color', '--', self.get_file_name()], - self.diff_done) - - def diff_done(self, result): + command = ['git', 'log', '--name-only', '--pretty=%s\a%h %an <%aE>\a%ad (%ar)', + '--date=local', '--max-count=9000'] + file_name = self.get_file_name() + if file_name: + command.extend(['--follow', '--', file_name]) + self.run_command( + command, + self.show_done) + + def show_done(self, result): + # GitLog Copy-Past + import os + self.results = [] + self.files = {} + relative = None + for r in result.strip().split('\n'): + if r: + _result = r.strip().split('\a', 2) + if len(_result) == 1: + if relative is None: + # Find relative path + relative = os.sep.join(['..'] * (len(os.path.normpath(_result[0]).split(os.sep)) - 1)) + if relative: + relative += os.sep + ref = result[1].split(' ', 1)[0] + self.files[ref] = relative + _result[0] + else: + result = _result + ref = result[1].split(' ', 1) + result[0] = u"%s - %s" % (ref[0], result[0]) + result[1] = ref[1] + self.results.append(result) + 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[0].split(' ', 1)[0] + command = ['git', 'diff', '-C', '--no-color', ref, '--'] + command.extend(set(self.files.values() + [self.get_file_name()])) + self.run_command( + command, + self.diff_done, + ref=ref) + + def diff_done(self, result, ref): if not result.strip(): self.panel("No output") return @@ -45,7 +89,7 @@ def diff_done(self, result): class GitDiffCommit (object): def run(self, edit=None): - self.run_command(['git', 'diff', '--cached', '--no-color'], + self.run_command(['git', 'diff', '-C', '--no-color', '--cached'], self.diff_done) def diff_done(self, result): diff --git a/history.py b/history.py index 07c05aa5..3f9f4dc8 100644 --- a/history.py +++ b/history.py @@ -57,7 +57,27 @@ def run_log(self, follow, *args): self.log_done) def log_done(self, result): - self.results = [r.split('\a', 2) for r in result.strip().split('\n')] + import os + self.results = [] + self.files = {} + relative = None + for r in result.strip().split('\n'): + if r: + _result = r.strip().split('\a', 2) + if len(_result) == 1: + if relative is None: + # Find relative path + relative = os.sep.join(['..'] * (len(os.path.normpath(_result[0]).split(os.sep)) - 1)) + if relative: + relative += os.sep + ref = result[1].split(' ', 1)[0] + self.files[ref] = relative + _result[0] + else: + result = _result + ref = result[1].split(' ', 1) + result[0] = u"%s - %s" % (ref[0], result[0]) + result[1] = ref[1] + self.results.append(result) self.quick_panel(self.results, self.log_panel_done) def log_panel_done(self, picked): @@ -66,14 +86,17 @@ def log_panel_done(self, picked): item = self.results[picked] # the commit hash is the first thing on the second line ref = item[0].split(' ', 1)[0] - self.log_result(ref) + file_name = self.files.get(ref, self.get_file_name()) + self.log_result(ref, file_name) - def log_result(self, ref): + def log_result(self, ref, file_name): # 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. + command = ['git', 'log', '--follow', '-p', '-1', ref] + command.extend(['--', file_name]) self.run_command( - ['git', 'log', '-p', '-1', ref, '--', self.get_file_name()], + command, self.details_done) def details_done(self, result): @@ -92,13 +115,33 @@ 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)', + ['git', 'log', '--follow', '--name-only', '--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')] + import os + self.results = [] + self.files = {} + relative = None + for r in result.strip().split('\n'): + if r: + _result = r.strip().split('\a', 2) + if len(_result) == 1: + if relative is None: + # Find relative path + relative = os.sep.join(['..'] * (len(os.path.normpath(_result[0]).split(os.sep)) - 1)) + if relative: + relative += os.sep + ref = result[1].split(' ', 1)[0] + self.files[ref] = relative + _result[0] + else: + result = _result + ref = result[1].split(' ', 1) + result[0] = u"%s - %s" % (ref[0], result[0]) + result[1] = ref[1] + self.results.append(result) self.quick_panel(self.results, self.panel_done) def panel_done(self, picked): From 368629173bbd3e39a14426df966b06fa96b87748 Mon Sep 17 00:00:00 2001 From: "German M. Bravo" Date: Sun, 17 Feb 2013 11:48:50 -0600 Subject: [PATCH 23/80] Fixed marking of added/removed lines --- diff.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/diff.py b/diff.py index d0a8fcca..b7fcc7fb 100644 --- a/diff.py +++ b/diff.py @@ -76,8 +76,8 @@ def diff_done(self, result, ref): else: view = self.scratch(result, title="Git Diff", syntax=syntax) - lines_inserted = view.find_all(r'^\+[^+]{2} ') - lines_deleted = view.find_all(r'^-[^-]{2} ') + lines_inserted = view.find_all(r'^\+(?!\+{2} )') + lines_deleted = view.find_all(r'^-(?!-{2} )') view.add_regions("inserted", lines_inserted, "markup.inserted.diff", "dot", sublime.HIDDEN) view.add_regions("deleted", lines_deleted, "markup.deleted.diff", "dot", sublime.HIDDEN) From 1acdc6a032f025317318db008c2304c89ea5369b Mon Sep 17 00:00:00 2001 From: "German M. Bravo" Date: Wed, 20 Feb 2013 12:48:18 -0600 Subject: [PATCH 24/80] Do not --follow for Log All --- history.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/history.py b/history.py index 3f9f4dc8..b6f338bd 100644 --- a/history.py +++ b/history.py @@ -86,14 +86,15 @@ def log_panel_done(self, picked): item = self.results[picked] # the commit hash is the first thing on the second line ref = item[0].split(' ', 1)[0] - file_name = self.files.get(ref, self.get_file_name()) - self.log_result(ref, file_name) + fn = self.get_file_name() + file_name = self.files.get(ref, fn) + self.log_result(ref, file_name, fn != '') - def log_result(self, ref, file_name): + def log_result(self, ref, file_name, follow): # 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. - command = ['git', 'log', '--follow', '-p', '-1', ref] + command = ['git', 'log', '--follow' if follow else None, '-p', '-1', ref] command.extend(['--', file_name]) self.run_command( command, From f2e6b7a4e5fb5160ec75edcd5a89c0f16e424dc1 Mon Sep 17 00:00:00 2001 From: "German M. Bravo" Date: Wed, 17 Apr 2013 10:10:25 -0500 Subject: [PATCH 25/80] Remove added regions for inserts/deletes (doesn't always work) --- diff.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/diff.py b/diff.py index b7fcc7fb..dab9112a 100644 --- a/diff.py +++ b/diff.py @@ -76,11 +76,11 @@ def diff_done(self, result, ref): else: view = self.scratch(result, title="Git Diff", syntax=syntax) - lines_inserted = view.find_all(r'^\+(?!\+{2} )') - lines_deleted = view.find_all(r'^-(?!-{2} )') + # lines_inserted = view.find_all(r'^\+(?!\+{2} )') + # lines_deleted = view.find_all(r'^-(?!-{2} )') - view.add_regions("inserted", lines_inserted, "markup.inserted.diff", "dot", sublime.HIDDEN) - view.add_regions("deleted", lines_deleted, "markup.deleted.diff", "dot", sublime.HIDDEN) + # view.add_regions("inserted", lines_inserted, "markup.inserted.diff", "dot", sublime.HIDDEN) + # view.add_regions("deleted", lines_deleted, "markup.deleted.diff", "dot", sublime.HIDDEN) # Store the git root directory in the view so we can resolve relative paths # when the user wants to navigate to the source file. From 0b1d861b1288b5f12cce29229b9c7b9df284dd4e Mon Sep 17 00:00:00 2001 From: "German M. Bravo" Date: Fri, 6 Sep 2013 11:42:27 -0500 Subject: [PATCH 26/80] Fixed some commands. Simplified blame output --- diff.py | 5 +++-- history.py | 11 +++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/diff.py b/diff.py index dab9112a..7f4a4ddf 100644 --- a/diff.py +++ b/diff.py @@ -1,4 +1,6 @@ import sublime + +import os import re from .git import git_root, GitTextCommand, GitWindowCommand import functools @@ -29,7 +31,6 @@ def run(self, edit=None): def show_done(self, result): # GitLog Copy-Past - import os self.results = [] self.files = {} relative = None @@ -42,7 +43,7 @@ def show_done(self, result): relative = os.sep.join(['..'] * (len(os.path.normpath(_result[0]).split(os.sep)) - 1)) if relative: relative += os.sep - ref = result[1].split(' ', 1)[0] + ref = result[0].split(' ', 1)[0] self.files[ref] = relative + _result[0] else: result = _result diff --git a/history.py b/history.py index b6f338bd..ac2e707d 100644 --- a/history.py +++ b/history.py @@ -1,4 +1,5 @@ import functools +import os import re import sublime @@ -11,7 +12,7 @@ def run(self, edit): # -w: ignore whitespace changes # -M: retain blame when moving lines # -C: retain blame when copying lines between files - command = ['git', 'blame', '-w', '-M', '-C'] + command = ['git', 'blame', '-w', '-M', '-C', '--date=short', '--abbrev=6'] s = sublime.load_settings("Git.sublime-settings") selection = self.view.sel()[0] # todo: multi-select support? @@ -57,7 +58,6 @@ def run_log(self, follow, *args): self.log_done) def log_done(self, result): - import os self.results = [] self.files = {} relative = None @@ -70,7 +70,7 @@ def log_done(self, result): relative = os.sep.join(['..'] * (len(os.path.normpath(_result[0]).split(os.sep)) - 1)) if relative: relative += os.sep - ref = result[1].split(' ', 1)[0] + ref = result[0].split(' ', 1)[0] self.files[ref] = relative + _result[0] else: result = _result @@ -122,7 +122,6 @@ def run(self, edit=None): def show_done(self, result): # GitLog Copy-Past - import os self.results = [] self.files = {} relative = None @@ -135,7 +134,7 @@ def show_done(self, result): relative = os.sep.join(['..'] * (len(os.path.normpath(_result[0]).split(os.sep)) - 1)) if relative: relative += os.sep - ref = result[1].split(' ', 1)[0] + ref = result[0].split(' ', 1)[0] self.files[ref] = relative + _result[0] else: result = _result @@ -150,7 +149,7 @@ def panel_done(self, picked): return item = self.results[picked] # the commit hash is the first thing on the second line - ref = item[1].split(' ', 1)[0] + ref = item[0].split(' ', 1)[0] self.run_command( ['git', 'show', '%s:%s' % (ref, self.get_relative_file_name())], self.details_done, From cb205fe7e7ee18490848bec1fd5e15d388a21f6d Mon Sep 17 00:00:00 2001 From: "German M. Bravo" Date: Thu, 12 Sep 2013 18:04:39 -0500 Subject: [PATCH 27/80] Added missing import --- diff.py | 1 + 1 file changed, 1 insertion(+) diff --git a/diff.py b/diff.py index 7f4a4ddf..2ac2e62b 100644 --- a/diff.py +++ b/diff.py @@ -1,4 +1,5 @@ import sublime +import sublime_plugin import os import re From c12b7d6afeaa820fc7015699fbd29dfbeade59de Mon Sep 17 00:00:00 2001 From: David Lynch Date: Thu, 26 Sep 2013 18:16:33 -0500 Subject: [PATCH 28/80] Change how PLUGIN_DIRECTORY is generated --- git.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/git.py b/git.py index 5ad31989..c921f3e9 100644 --- a/git.py +++ b/git.py @@ -10,11 +10,14 @@ # In a complete inversion from ST2, in ST3 when a plugin is loaded we # actually can trust __file__. # Goal is to get: "Packages/Git", allowing for people who rename things -FULL_PLUGIN_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) -PLUGIN_DIRECTORY = FULL_PLUGIN_DIRECTORY.replace(os.path.normpath(os.path.join(FULL_PLUGIN_DIRECTORY, '..', '..')) + os.path.sep, '').replace(os.path.sep, '/') git_root_cache = {} +def find_plugin_directory(f): + dirname = os.path.split(os.path.dirname(f))[-1] + return "Packages/" + dirname.replace(".sublime-package", "") +PLUGIN_DIRECTORY = find_plugin_directory(__file__) +print("PLUGIN_DIRECTORY", PLUGIN_DIRECTORY) def main_thread(callback, *args, **kwargs): # sublime.set_timeout gets used to send things onto the main thread From 5f26e7eadb1a2cdcb9b451c0749ba16489bd618a Mon Sep 17 00:00:00 2001 From: David Lynch Date: Thu, 26 Sep 2013 18:28:51 -0500 Subject: [PATCH 29/80] Remove debug message --- git.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git.py b/git.py index c921f3e9..121dee04 100644 --- a/git.py +++ b/git.py @@ -17,7 +17,7 @@ def find_plugin_directory(f): dirname = os.path.split(os.path.dirname(f))[-1] return "Packages/" + dirname.replace(".sublime-package", "") PLUGIN_DIRECTORY = find_plugin_directory(__file__) -print("PLUGIN_DIRECTORY", PLUGIN_DIRECTORY) + def main_thread(callback, *args, **kwargs): # sublime.set_timeout gets used to send things onto the main thread From 85a11c5c6539d17fb43f299d4a16dcc79dca2c7d Mon Sep 17 00:00:00 2001 From: "German M. Bravo" Date: Fri, 27 Sep 2013 17:00:54 -0500 Subject: [PATCH 30/80] Python3 compatibility --- diff.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diff.py b/diff.py index 2ac2e62b..fb152370 100644 --- a/diff.py +++ b/diff.py @@ -61,7 +61,7 @@ def panel_done(self, picked): # the commit hash is the first thing on the second line ref = item[0].split(' ', 1)[0] command = ['git', 'diff', '-C', '--no-color', ref, '--'] - command.extend(set(self.files.values() + [self.get_file_name()])) + command.extend(set(list(self.files.values()) + [self.get_file_name()])) self.run_command( command, self.diff_done, From 9c939465d932c02601094d920026576ad12210cb Mon Sep 17 00:00:00 2001 From: David Lynch Date: Mon, 7 Oct 2013 18:38:48 -0500 Subject: [PATCH 31/80] I believe platform-independence to not be needed here. --- git.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git.py b/git.py index 06a9ab6f..463baab0 100644 --- a/git.py +++ b/git.py @@ -68,7 +68,7 @@ def view_contents(view): def plugin_file(name): - return os.path.join(PLUGIN_DIRECTORY, name) + return PLUGIN_DIRECTORY + '/' + name def do_when(conditional, callback, *args, **kwargs): From 5a9858e56d107b4f25c204960ea58f33e47b6477 Mon Sep 17 00:00:00 2001 From: David Lynch Date: Mon, 7 Oct 2013 18:44:30 -0500 Subject: [PATCH 32/80] Revert "Merge pull request #294 from Kronuz/python3" This reverts commit 0ac1d6622e52be65d8babe065efd851b9bbb0ac7, reversing changes made to 5f26e7eadb1a2cdcb9b451c0749ba16489bd618a. Clearly I didn't investigate this pull request enough. It does crazy things to diff. --- Default.sublime-keymap | 6 -- diff.py | 168 +++-------------------------------------- history.py | 62 +++------------ status.py | 6 +- 4 files changed, 22 insertions(+), 220 deletions(-) delete mode 100644 Default.sublime-keymap diff --git a/Default.sublime-keymap b/Default.sublime-keymap deleted file mode 100644 index 92ee9df7..00000000 --- a/Default.sublime-keymap +++ /dev/null @@ -1,6 +0,0 @@ -[ - {"keys": ["enter"], "command": "git_goto_diff", - "context": [{"key": "selector", "operand": "markup.inserted.diff"}]}, - {"keys": ["enter"], "command": "git_goto_diff", - "context": [{"key": "selector", "operand": "markup.deleted.diff"}]} -] diff --git a/diff.py b/diff.py index fb152370..555e71e0 100644 --- a/diff.py +++ b/diff.py @@ -1,73 +1,13 @@ import sublime -import sublime_plugin - -import os -import re -from .git import git_root, GitTextCommand, GitWindowCommand -import functools - - -def do_when(conditional, callback, *args, **kwargs): - if conditional(): - return callback(*args, **kwargs) - sublime.set_timeout(functools.partial(do_when, conditional, callback, *args, **kwargs), 50) - - -def goto_xy(view, line, col): - view.run_command("goto_line", {"line": line}) - for i in range(col): - view.run_command("move", {"by": "characters", "forward": True}) +from .git import GitTextCommand, GitWindowCommand class GitDiff (object): def run(self, edit=None): - command = ['git', 'log', '--name-only', '--pretty=%s\a%h %an <%aE>\a%ad (%ar)', - '--date=local', '--max-count=9000'] - file_name = self.get_file_name() - if file_name: - command.extend(['--follow', '--', file_name]) - self.run_command( - command, - self.show_done) - - def show_done(self, result): - # GitLog Copy-Past - self.results = [] - self.files = {} - relative = None - for r in result.strip().split('\n'): - if r: - _result = r.strip().split('\a', 2) - if len(_result) == 1: - if relative is None: - # Find relative path - relative = os.sep.join(['..'] * (len(os.path.normpath(_result[0]).split(os.sep)) - 1)) - if relative: - relative += os.sep - ref = result[0].split(' ', 1)[0] - self.files[ref] = relative + _result[0] - else: - result = _result - ref = result[1].split(' ', 1) - result[0] = u"%s - %s" % (ref[0], result[0]) - result[1] = ref[1] - self.results.append(result) - 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[0].split(' ', 1)[0] - command = ['git', 'diff', '-C', '--no-color', ref, '--'] - command.extend(set(list(self.files.values()) + [self.get_file_name()])) - self.run_command( - command, - self.diff_done, - ref=ref) - - def diff_done(self, result, ref): + self.run_command(['git', 'diff', '--no-color', '--', self.get_file_name()], + self.diff_done) + + def diff_done(self, result): if not result.strip(): self.panel("No output") return @@ -78,20 +18,16 @@ def diff_done(self, result, ref): else: view = self.scratch(result, title="Git Diff", syntax=syntax) - # lines_inserted = view.find_all(r'^\+(?!\+{2} )') - # lines_deleted = view.find_all(r'^-(?!-{2} )') - - # view.add_regions("inserted", lines_inserted, "markup.inserted.diff", "dot", sublime.HIDDEN) - # view.add_regions("deleted", lines_deleted, "markup.deleted.diff", "dot", sublime.HIDDEN) + lines_inserted = view.find_all(r'^\+[^+]{2} ') + lines_deleted = view.find_all(r'^-[^-]{2} ') - # Store the git root directory in the view so we can resolve relative paths - # when the user wants to navigate to the source file. - view.settings().set("git_root_dir", git_root(self.get_working_dir())) + view.add_regions("inserted", lines_inserted, "markup.inserted.diff", "dot", sublime.HIDDEN) + view.add_regions("deleted", lines_deleted, "markup.deleted.diff", "dot", sublime.HIDDEN) class GitDiffCommit (object): def run(self, edit=None): - self.run_command(['git', 'diff', '-C', '--no-color', '--cached'], + self.run_command(['git', 'diff', '--cached', '--no-color'], self.diff_done) def diff_done(self, result): @@ -99,11 +35,8 @@ def diff_done(self, result): self.panel("No output") return s = sublime.load_settings("Git.sublime-settings") - if s.get('diff_panel'): - self.panel(result) - else: - syntax = s.get("diff_syntax", "Packages/Diff/Diff.tmLanguage") - self.scratch(result, title="Git Diff", syntax=syntax) + syntax = s.get("diff_syntax", "Packages/Diff/Diff.tmLanguage") + self.scratch(result, title="Git Diff", syntax=syntax) class GitDiffCommand(GitDiff, GitTextCommand): @@ -130,80 +63,3 @@ class GitDiffToolCommand(GitDiffTool, GitTextCommand): class GitDiffToolAll(GitWindowCommand): def run(self): self.run_command(['git', 'difftool']) - - -class GitGotoDiff(sublime_plugin.TextCommand): - def run(self, edit): - v = self.view - view_scope_name = v.scope_name(v.sel()[0].a) - scope_markup_inserted = ("markup.inserted.diff" in view_scope_name) - scope_markup_deleted = ("markup.deleted.diff" in view_scope_name) - - if not scope_markup_inserted and not scope_markup_deleted: - return - - beg = v.sel()[0].a # Current position in selection - pt = v.line(beg).a # First position in the current diff line - self.column = beg - pt - 1 # The current column (-1 because the first char in diff file) - - self.file_name = None - hunk_line = None - line_offset = 0 - - while pt > 0: - line = v.line(pt) - lineContent = v.substr(line) - if lineContent.startswith("@@"): - if not hunk_line: - hunk_line = lineContent - elif lineContent.startswith("+++ b/"): - self.file_name = v.substr(sublime.Region(line.a+6, line.b)).strip() - break - elif not hunk_line and not lineContent.startswith("-"): - line_offset = line_offset+1 - - pt = v.line(pt-1).a - - hunk = re.match(r"^@@ -(\d+),(\d+) \+(\d+),(\d+) @@.*", hunk_line) - if not hunk: - sublime.status_message("No hunk info") - return - - hunk_start_line = hunk.group(3) - self.goto_line = int(hunk_start_line) + line_offset - 1 - - git_root_dir = v.settings().get("git_root_dir") - - # Sanity check and see if the file we're going to try to open even - # exists. If it does not, prompt the user for the correct base directory - # to use for their diff. - full_path_file_name = self.file_name - if git_root_dir: - full_path_file_name = os.path.join(git_root_dir, self.file_name) - else: - git_root_dir = "" - - if not os.path.isfile(full_path_file_name): - caption = "Enter base directory for file '%s':" % self.file_name - v.window().show_input_panel(caption, - git_root_dir, - self.on_path_confirmed, - None, - None) - else: - self.on_path_confirmed(git_root_dir) - - def on_path_confirmed(self, git_root_dir): - v = self.view - old_git_root_dir = v.settings().get("git_root_dir") - - # If the user provided a new git_root_dir, save it in the view settings - # so they only have to fix it once - if old_git_root_dir != git_root_dir: - v.settings().set("git_root_dir", git_root_dir) - - full_path_file_name = os.path.join(git_root_dir, self.file_name) - - new_view = v.window().open_file(full_path_file_name) - do_when(lambda: not new_view.is_loading(), - lambda: goto_xy(new_view, self.goto_line, self.column)) diff --git a/history.py b/history.py index ac2e707d..57ab643f 100644 --- a/history.py +++ b/history.py @@ -1,5 +1,4 @@ import functools -import os import re import sublime @@ -12,7 +11,7 @@ def run(self, edit): # -w: ignore whitespace changes # -M: retain blame when moving lines # -C: retain blame when copying lines between files - command = ['git', 'blame', '-w', '-M', '-C', '--date=short', '--abbrev=6'] + command = ['git', 'blame', '-w', '-M', '-C'] s = sublime.load_settings("Git.sublime-settings") selection = self.view.sel()[0] # todo: multi-select support? @@ -58,26 +57,7 @@ def run_log(self, follow, *args): self.log_done) def log_done(self, result): - self.results = [] - self.files = {} - relative = None - for r in result.strip().split('\n'): - if r: - _result = r.strip().split('\a', 2) - if len(_result) == 1: - if relative is None: - # Find relative path - relative = os.sep.join(['..'] * (len(os.path.normpath(_result[0]).split(os.sep)) - 1)) - if relative: - relative += os.sep - ref = result[0].split(' ', 1)[0] - self.files[ref] = relative + _result[0] - else: - result = _result - ref = result[1].split(' ', 1) - result[0] = u"%s - %s" % (ref[0], result[0]) - result[1] = ref[1] - self.results.append(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): @@ -85,19 +65,14 @@ def log_panel_done(self, picked): return item = self.results[picked] # the commit hash is the first thing on the second line - ref = item[0].split(' ', 1)[0] - fn = self.get_file_name() - file_name = self.files.get(ref, fn) - self.log_result(ref, file_name, fn != '') + self.log_result(item[1].split(' ')[0]) - def log_result(self, ref, file_name, follow): + 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. - command = ['git', 'log', '--follow' if follow else None, '-p', '-1', ref] - command.extend(['--', file_name]) self.run_command( - command, + ['git', 'log', '-p', '-1', ref, '--', self.get_file_name()], self.details_done) def details_done(self, result): @@ -116,32 +91,13 @@ class GitShow(object): def run(self, edit=None): # GitLog Copy-Past self.run_command( - ['git', 'log', '--follow', '--name-only', '--pretty=%s\a%h %an <%aE>\a%ad (%ar)', + ['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 = [] - self.files = {} - relative = None - for r in result.strip().split('\n'): - if r: - _result = r.strip().split('\a', 2) - if len(_result) == 1: - if relative is None: - # Find relative path - relative = os.sep.join(['..'] * (len(os.path.normpath(_result[0]).split(os.sep)) - 1)) - if relative: - relative += os.sep - ref = result[0].split(' ', 1)[0] - self.files[ref] = relative + _result[0] - else: - result = _result - ref = result[1].split(' ', 1) - result[0] = u"%s - %s" % (ref[0], result[0]) - result[1] = ref[1] - self.results.append(result) + 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): @@ -149,7 +105,7 @@ def panel_done(self, picked): return item = self.results[picked] # the commit hash is the first thing on the second line - ref = item[0].split(' ', 1)[0] + ref = item[1].split(' ')[0] self.run_command( ['git', 'show', '%s:%s' % (ref, self.get_relative_file_name())], self.details_done, @@ -200,7 +156,7 @@ def branch_done(self, result): def branch_panel_done(self, picked): if 0 > picked < len(self.results): return - self.branch = self.results[picked].rsplit(' ', 1)[-1] + self.branch = self.results[picked].split(' ')[-1] self.run_log(False, self.branch) def log_result(self, result_hash): diff --git a/status.py b/status.py index 1bf2f2e7..bb0139af 100644 --- a/status.py +++ b/status.py @@ -54,11 +54,7 @@ def panel_followup(self, picked_status, picked_file, picked_index): def diff_done(self, result): if not result.strip(): return - s = sublime.load_settings("Git.sublime-settings") - if s.get('diff_panel'): - self.panel(result) - else: - self.scratch(result, title="Git Diff") + self.scratch(result, title="Git Diff") class GitOpenModifiedFilesCommand(GitStatusCommand): From de9a1ac8b1cc9318372b04c8565884e01bf0aa4a Mon Sep 17 00:00:00 2001 From: Romain Guerin Date: Thu, 19 Dec 2013 22:59:48 +0100 Subject: [PATCH 33/80] fix gitk command for Windows --- git.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/git.py b/git.py index 463baab0..969cffa9 100644 --- a/git.py +++ b/git.py @@ -161,11 +161,15 @@ def run(self): startupinfo = subprocess.STARTUPINFO() startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + shell = False + if sublime.platform() == 'windows': + shell = True + # universal_newlines seems to break `log` in python3 proc = subprocess.Popen(self.command, stdout=self.stdout, stderr=subprocess.STDOUT, stdin=subprocess.PIPE, startupinfo=startupinfo, - shell=False, universal_newlines=False) + shell=shell, universal_newlines=False) output = proc.communicate(self.stdin)[0] if not output: output = '' From e4894826387e0885058a6d94c1111e0bc8d7487b Mon Sep 17 00:00:00 2001 From: Romain Guerin Date: Fri, 20 Dec 2013 00:06:10 +0100 Subject: [PATCH 34/80] add the ability to execute a custom command Example usage: put the following JSON in a ".sublime-commands" (or ".sublime-menu") file: { "caption": "Git: Gitk all branches", "command": "git_raw", "args": {"command": "gitk --all"} } --- git.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/git.py b/git.py index 463baab0..e3e3b21e 100644 --- a/git.py +++ b/git.py @@ -380,6 +380,20 @@ def on_input(self, command): print(command_splitted) self.run_command(command_splitted) +class GitRawCommand(GitWindowCommand): + may_change_files = True + + def run(self, **args): + + command = str(args.get('command', '')) + if command.strip() == "": + self.panel("No git command provided") + return + import shlex + command_splitted = shlex.split(command) + print(command_splitted) + self.run_command(command_splitted) + class GitGuiCommand(GitTextCommand): def run(self, edit): From 9a1b9e5764e2d4135ef84f931ad73872902bce6a Mon Sep 17 00:00:00 2001 From: Romain Guerin Date: Fri, 20 Dec 2013 00:48:52 +0100 Subject: [PATCH 35/80] add option to choose where to display git raw command output Example usage: put the following JSON in a ".sublime-commands" (or ".sublime-menu") file: { "caption": "Git: show git log in default viewer", "command": "git_raw", "args": {"command": "git log --oneline"} }, { "caption": "Git: show git log in a pane below", "command": "git_raw", "args": {"command": "git log --oneline", "show_in": "pane_below"} }, { "caption": "Git: show git log in the quick panel", "command": "git_raw", "args": {"command": "git log --oneline", "show_in": "quick_panel"} }, { "caption": "Git: show git log in a new tab", "command": "git_raw", "args": {"command": "git log --oneline", "show_in": "new_tab"} } --- git.py | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/git.py b/git.py index e3e3b21e..1057bd28 100644 --- a/git.py +++ b/git.py @@ -385,14 +385,41 @@ class GitRawCommand(GitWindowCommand): def run(self, **args): - command = str(args.get('command', '')) - if command.strip() == "": + self.command = str(args.get('command', '')) + show_in = str(args.get('show_in', 'pane_below')) + + if self.command.strip() == "": self.panel("No git command provided") return import shlex - command_splitted = shlex.split(command) + command_splitted = shlex.split(self.command) print(command_splitted) - self.run_command(command_splitted) + + if show_in == 'pane_below': + self.run_command(command_splitted) + elif show_in == 'quick_panel': + self.run_command(command_splitted, self.show_in_quick_panel) + elif show_in == 'new_tab': + self.run_command(command_splitted, self.show_in_new_tab) + + def show_in_quick_panel(self, result): + self.results = list(result.rstrip().split('\n')) + if len(self.results): + self.quick_panel(self.results, + self.do_nothing, sublime.MONOSPACE_FONT) + else: + sublime.status_message("Nothing to show") + + def do_nothing(self, picked): + return + + def show_in_new_tab(self, result): + msg = self.window.new_file() + msg.set_scratch(True) + msg.set_name(self.command) + self._output_to_view(msg, result) + msg.sel().clear() + msg.sel().add(sublime.Region(0, 0)) class GitGuiCommand(GitTextCommand): From 77827580b98a0a222aa5146d80763129a88deaeb Mon Sep 17 00:00:00 2001 From: David Lynch Date: Tue, 11 Feb 2014 01:41:02 -0600 Subject: [PATCH 36/80] The blame_whole_file setting is pointless and broken --- Git.sublime-settings | 5 ----- history.py | 3 +-- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/Git.sublime-settings b/Git.sublime-settings index 06e73cd5..acd87e81 100644 --- a/Git.sublime-settings +++ b/Git.sublime-settings @@ -12,11 +12,6 @@ // use the panel for diff output, rather than a new scratch window (new tab) ,"diff_panel": false - // affects blame command when no selection is made - // true: blame whole file - // false: blame only current line - ,"blame_whole_file": true - // If you'd rather have your status command open files instead of show you a // diff, set this to true. You can still do `Git: Status` followed by // 'Git: Diff Current File' to get a file diff diff --git a/history.py b/history.py index 57ab643f..66de0395 100644 --- a/history.py +++ b/history.py @@ -13,9 +13,8 @@ def run(self, edit): # -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'): + if not selection.empty(): # 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()) From 85651f8f71f93add7073d433f4c4d7dac67c7b8a Mon Sep 17 00:00:00 2001 From: David Lynch Date: Tue, 11 Feb 2014 02:41:45 -0600 Subject: [PATCH 37/80] Add a "Git Document" command to show commit messages for the line Idea from: http://mislav.uniqpath.com/2014/02/hidden-documentation/ Theory is that if you find something inexplicable, maybe the commit messages attached to that line will help explain what was going on. --- Default.sublime-commands | 4 +++ history.py | 57 +++++++++++++++++++++++++++++++++------- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/Default.sublime-commands b/Default.sublime-commands index 25600650..f135b802 100644 --- a/Default.sublime-commands +++ b/Default.sublime-commands @@ -6,6 +6,10 @@ "caption": "Git: Blame", "command": "git_blame" } + ,{ + "caption": "Git: Document Selection", + "command": "git_document" + } ,{ "caption": "Git: New Tag", "command": "git_new_tag" diff --git a/history.py b/history.py index 66de0395..59ee921e 100644 --- a/history.py +++ b/history.py @@ -1,5 +1,6 @@ import functools import re +from operator import methodcaller import sublime from .git import GitTextCommand, GitWindowCommand, plugin_file @@ -13,16 +14,9 @@ def run(self, edit): # -C: retain blame when copying lines between files command = ['git', 'blame', '-w', '-M', '-C'] - selection = self.view.sel()[0] # todo: multi-select support? - if not selection.empty(): - # 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)) + lines = self.get_lines() + if lines: + command.extend(('-L', str(lines[0]) + ',' + str(lines[1]))) callback = self.blame_done else: callback = functools.partial(self.blame_done, @@ -31,6 +25,19 @@ def run(self, edit): command.append(self.get_file_name()) self.run_command(command, callback) + def get_lines(self): + selection = self.view.sel()[0] # todo: multi-select support? + if selection.empty(): + return False + # 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 + # add one to each, to line up sublime's index with git's + return begin_line + 1, end_line + 1 + def blame_done(self, result, position=None): self.scratch(result, title="Git Blame", position=position, syntax=plugin_file("syntax/Git Blame.tmLanguage")) @@ -186,3 +193,33 @@ def ls_panel_done(self, picked): def show_done(self, result): self.scratch(result, title="%s:%s" % (self.fileRef, self.filename)) + +class GitDocumentCommand(GitBlameCommand): + def get_lines(self): + selection = self.view.sel()[0] # todo: multi-select support? + # 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 + # add one to each, to line up sublime's index with git's + return begin_line + 1, end_line + 1 + + def blame_done(self, result, position=None): + shas = set((sha for sha in re.findall(r'^[0-9a-f]+', result, re.MULTILINE) if not re.match(r'^0+$', sha))) + command = ['git', 'show', '-s', '-z', '--no-color', '--date=iso'] + command.extend(shas) + + self.run_command(command, self.show_done) + + def show_done(self, result): + commits = [] + for commit in result.split('\0'): + match = re.search(r'^Date:\s+(.+)$', commit, re.MULTILINE) + if match: + commits.append((match.group(1), commit)) + commits.sort(reverse=True) + commits = [commit for d, commit in commits] + + self.scratch('\n\n'.join(commits), title="Git Commit Documentation") From 880bee2a44435a21ec871f1cd56ba7c7aa251471 Mon Sep 17 00:00:00 2001 From: Shashank Agarwal Date: Fri, 28 Mar 2014 15:01:36 -0400 Subject: [PATCH 38/80] Bringing pull rebase option to python3 Similar to - https://github.com/kemayo/sublime-text-git/commit/278d13fb27559e62ab0de6377f631fca1b363001 --- Default.sublime-commands | 4 ++++ repo.py | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/Default.sublime-commands b/Default.sublime-commands index f135b802..dab2d607 100644 --- a/Default.sublime-commands +++ b/Default.sublime-commands @@ -138,6 +138,10 @@ "caption": "Git: Pull", "command": "git_pull" } + ,{ + "caption": "Git: Pull Using Rebase", + "command": "git_pull_rebase" + } ,{ "caption": "Git: Pull Current Branch", "command": "git_pull_current_branch" diff --git a/repo.py b/repo.py index 6c199e43..f4f6d52f 100644 --- a/repo.py +++ b/repo.py @@ -128,6 +128,11 @@ def run(self): self.run_command(['git', 'push', '--tags']) +class GitPullRebaseCommand(GitWindowCommand): + def run(self): + self.run_command(['git', 'pull', '--rebase'], callback=self.panel) + + class GitCheckoutTagCommand(GitWindowCommand): def run(self): self.run_command(['git', 'tag'], self.fetch_tag) From 9bd6f69209ee11b9847ddfd8df603f49f65fd6a8 Mon Sep 17 00:00:00 2001 From: David Lynch Date: Thu, 1 May 2014 15:10:33 -0500 Subject: [PATCH 39/80] Make history less inexplicable --- commit.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/commit.py b/commit.py index 531c4d1a..19bea66c 100644 --- a/commit.py +++ b/commit.py @@ -156,7 +156,10 @@ def on_close(self, view): class GitCommitHistoryCommand(sublime_plugin.TextCommand): def run(self, edit): self.edit = edit - self.view.window().show_quick_panel(history, self.panel_done, sublime.MONOSPACE_FONT) + if history: + self.view.window().show_quick_panel(history, self.panel_done, sublime.MONOSPACE_FONT) + else: + sublime.message_dialog("You have no commit history.\n\nCommit history is just a quick list of messages you've used in this session.") def panel_done(self, index): if index > -1: From e481e701cc59a0f6e803d9bb6123502a97088833 Mon Sep 17 00:00:00 2001 From: Jason 'vanRijn' Kasper Date: Tue, 13 May 2014 15:28:08 -0400 Subject: [PATCH 40/80] Add functionality from python2 branch for navigating to diff hunk via diff viewer --- diff.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/diff.py b/diff.py index 4182dd24..08606637 100644 --- a/diff.py +++ b/diff.py @@ -1,4 +1,5 @@ -import sublime +import sublime, sublime_plugin +import os import re from .git import git_root, GitTextCommand, GitWindowCommand import functools @@ -83,10 +84,12 @@ def run(self): self.run_command(['git', 'difftool']) -class GitGotoDiff(GitTextCommand): +class GitGotoDiff(sublime_plugin.TextCommand): + def __init__(self, view): + self.view = view + def run(self, edit): v = self.view - print("got here") view_scope_name = v.scope_name(v.sel()[0].a) scope_markup_inserted = ("markup.inserted.diff" in view_scope_name) scope_markup_deleted = ("markup.deleted.diff" in view_scope_name) From e10755c9d341accc27b950b3ee5d233d4061f77c Mon Sep 17 00:00:00 2001 From: David Lynch Date: Sun, 18 May 2014 15:43:12 -0500 Subject: [PATCH 41/80] Add missing tmpreference cache to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b591a13f..56c6b572 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.pyc *.tmLanguage.cache +*.tmPreferences.cache .DS_Store package-metadata.json \ No newline at end of file From dd0767670bbf0c6c6aa3f0171e3eb16e9f32f58e Mon Sep 17 00:00:00 2001 From: David Lynch Date: Sun, 18 May 2014 15:55:03 -0500 Subject: [PATCH 42/80] Avoid some duplication introduced by pull request 344. --- diff.py | 15 +-------------- git.py | 6 ++++++ 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/diff.py b/diff.py index 08606637..4d0b155a 100644 --- a/diff.py +++ b/diff.py @@ -1,20 +1,7 @@ import sublime, sublime_plugin import os import re -from .git import git_root, GitTextCommand, GitWindowCommand -import functools - - -def do_when(conditional, callback, *args, **kwargs): - if conditional(): - return callback(*args, **kwargs) - sublime.set_timeout(functools.partial(do_when, conditional, callback, *args, **kwargs), 50) - - -def goto_xy(view, line, col): - view.run_command("goto_line", {"line": line}) - for i in range(col): - view.run_command("move", {"by": "characters", "forward": True}) +from .git import git_root, GitTextCommand, GitWindowCommand, do_when, goto_xy class GitDiff (object): diff --git a/git.py b/git.py index 1057bd28..b2111711 100644 --- a/git.py +++ b/git.py @@ -77,6 +77,12 @@ def do_when(conditional, callback, *args, **kwargs): sublime.set_timeout(functools.partial(do_when, conditional, callback, *args, **kwargs), 50) +def goto_xy(view, line, col): + view.run_command("goto_line", {"line": line}) + for i in range(col): + view.run_command("move", {"by": "characters", "forward": True}) + + def _make_text_safeish(text, fallback_encoding, method='decode'): # The unicode decode here is because sublime converts to unicode inside # insert in such a way that unknown characters will cause errors, which is From b3112fbc19187a5ddc12de9d158365a823d8f417 Mon Sep 17 00:00:00 2001 From: David Lynch Date: Sun, 18 May 2014 19:25:52 -0500 Subject: [PATCH 43/80] Use git_raw as much as possible for commands A lot of the commands that are implemented through code can be replaced with some git_raw calls instead. Less boilerplate in code. --- Default.sublime-commands | 26 +++++++++++++------------- Main.sublime-menu | 16 ++++++++-------- add.py | 5 ----- diff.py | 14 -------------- git.py | 20 ++++++++++++++------ history.py | 1 + repo.py | 36 ++---------------------------------- stash.py | 12 ------------ 8 files changed, 38 insertions(+), 92 deletions(-) diff --git a/Default.sublime-commands b/Default.sublime-commands index dab2d607..c1743ae8 100644 --- a/Default.sublime-commands +++ b/Default.sublime-commands @@ -24,7 +24,7 @@ } ,{ "caption": "Git: Push Tags", - "command": "git_push_tags" + "command": "git_raw", "args": { "command": "git push --tags", "may_change_files": false } } ,{ "caption": "Git: Checkout Tag", @@ -60,11 +60,11 @@ } ,{ "caption": "Git: Diff Tool Current File", - "command": "git_diff_tool" + "command": "git_raw", "args": { "command": "git difftool", "append_current_file": true, "may_change_files": false } } ,{ "caption": "Git: Diff Tool All", - "command": "git_diff_tool_all" + "command": "git_raw", "args": { "command": "git difftool", "may_change_files": false } } ,{ "caption": "Git: Commit", @@ -104,11 +104,11 @@ } ,{ "caption": "Git: Stash Changes", - "command": "git_stash" + "command": "git_raw", "args": { "command": "git stash" } } ,{ "caption": "Git: Stash Pop", - "command": "git_stash_pop" + "command": "git_raw", "args": { "command": "git stash pop" } } ,{ "caption": "Git: Stash Apply", @@ -120,7 +120,7 @@ } ,{ "caption": "Git: Add Current File", - "command": "git_add" + "command": "git_raw", "args": { "command": "git add", "append_current_file": true } } ,{ "caption": "Git: Add...", @@ -128,19 +128,19 @@ } ,{ "caption": "Git: Checkout Current File", - "command": "git_checkout" + "command": "git_raw", "args": { "command": "git checkout", "append_current_file": true } } ,{ "caption": "Git: Fetch", - "command": "git_fetch" + "command": "git_raw", "args": { "command": "git fetch", "may_change_files": false } } ,{ "caption": "Git: Pull", - "command": "git_pull" + "command": "git_raw", "args": { "command": "git pull" } } ,{ "caption": "Git: Pull Using Rebase", - "command": "git_pull_rebase" + "command": "git_raw", "args": { "command": "git pull --rebase" } } ,{ "caption": "Git: Pull Current Branch", @@ -148,7 +148,7 @@ } ,{ "caption": "Git: Push", - "command": "git_push" + "command": "git_raw", "args": { "command": "git push", "may_change_files": false } } ,{ "caption": "Git: Push Current Branch", @@ -196,11 +196,11 @@ } ,{ "caption": "Git: Reset (unstage) Current File", - "command": "git_reset_head" + "command": "git_raw", "args": { "command": "git reset HEAD", "append_current_file": true, "show_in": "suppress" } } ,{ "caption": "Git: Reset (unstage) All", - "command": "git_reset_head_all" + "command": "git_raw", "args": { "command": "git reset HEAD", "show_in": "suppress" } } ,{ "caption": "Git: Reset (hard) HEAD", diff --git a/Main.sublime-menu b/Main.sublime-menu index ec4b358a..73bb63fc 100644 --- a/Main.sublime-menu +++ b/Main.sublime-menu @@ -15,13 +15,13 @@ ,{ "caption": "Graph", "command": "git_graph" } ,{ "caption": "-" } ,{ "caption": "Diff", "command": "git_diff" } - ,{ "caption": "DiffTool", "command": "git_diff_tool" } + ,{ "caption": "DiffTool", "command": "git_raw", "args": { "command": "git difftool", "append_current_file": true, "may_change_files": false } } ,{ "caption": "-" } - ,{ "caption": "Add", "command": "git_add" } + ,{ "caption": "Add", "command": "git_raw", "args": { "command": "git add", "append_current_file": true } } ,{ "caption": "Add Selected Hunk", "command": "git_add_selected_hunk" } ,{ "caption": "-" } - ,{ "caption": "Reset", "command": "git_reset_head" } - ,{ "caption": "Checkout (Discard Changes)", "command": "git_checkout" } + ,{ "caption": "Reset", "command": "git_raw", "args": { "command": "git reset HEAD", "append_current_file": true, "show_in": "suppress" } } + ,{ "caption": "Checkout (Discard Changes)", "command": "git_raw", "args": { "command": "git checkout", "append_current_file": true } } ,{ "caption": "-" } ,{ "caption": "Quick Commit Current File", "command": "git_quick_commit" } ,{ "caption": "Commit Selected Hunk", "command": "git_commit_selected_hunk" } @@ -40,12 +40,12 @@ ,{ "caption": "-" } ,{ "caption": "Diff", "command": "git_diff_all" } ,{ "caption": "Diff Staged", "command": "git_diff_commit" } - ,{ "caption": "Diff Tool", "command": "git_diff_tool_all" } + ,{ "caption": "Diff Tool", "command": "git_raw", "args": { "command": "git difftool", "may_change_files": false } } ,{ "caption": "Reset Hard", "command": "git_reset_hard_head" } ,{ "caption": "-" } ,{ "caption": "Add...", "command": "git_add_choice" } ,{ "caption": "-" } - ,{ "caption": "Reset", "command": "git_reset_head_all" } + ,{ "caption": "Reset", "command": "git_raw", "args": { "command": "git reset HEAD", "show_in": "suppress" } } ,{ "caption": "-" } ,{ "caption": "Commit", "command": "git_commit" } ,{ "caption": "Amend Last Commit", "command": "git_commit_amend" } @@ -57,8 +57,8 @@ "caption": "Stash", "children": [ - { "caption": "Save", "command": "git_stash" } - ,{ "caption": "Pop", "command": "git_stash_pop" } + { "caption": "Save", "command": "git_raw", "args": { "command": "git stash", "may_change_files": true } } + ,{ "caption": "Pop", "command": "git_raw", "args": { "command": "git stash pop", "may_change_files": true } } ,{ "caption": "Apply", "command": "git_stash_apply" } ,{ "caption": "Drop", "command": "git_stash_drop" } ] diff --git a/add.py b/add.py index db17454a..6bb1d953 100644 --- a/add.py +++ b/add.py @@ -40,11 +40,6 @@ def rerun(self, result): self.run() -class GitAdd(GitTextCommand): - def run(self, edit): - self.run_command(['git', 'add', self.get_file_name()]) - - class GitAddSelectedHunkCommand(GitTextCommand): def run(self, edit): self.run_command(['git', 'diff', '--no-color', '-U1', self.get_file_name()], self.cull_diff) diff --git a/diff.py b/diff.py index 4d0b155a..41c829ea 100644 --- a/diff.py +++ b/diff.py @@ -57,20 +57,6 @@ class GitDiffCommitCommand(GitDiffCommit, GitWindowCommand): pass -class GitDiffTool(object): - def run(self, edit=None): - self.run_command(['git', 'difftool', '--', self.get_file_name()]) - - -class GitDiffToolCommand(GitDiffTool, GitTextCommand): - pass - - -class GitDiffToolAll(GitWindowCommand): - def run(self): - self.run_command(['git', 'difftool']) - - class GitGotoDiff(sublime_plugin.TextCommand): def __init__(self, view): self.view = view diff --git a/git.py b/git.py index b2111711..1e28bc6f 100644 --- a/git.py +++ b/git.py @@ -386,11 +386,11 @@ def on_input(self, command): print(command_splitted) self.run_command(command_splitted) + class GitRawCommand(GitWindowCommand): may_change_files = True def run(self, **args): - self.command = str(args.get('command', '')) show_in = str(args.get('show_in', 'pane_below')) @@ -398,15 +398,23 @@ def run(self, **args): self.panel("No git command provided") return import shlex - command_splitted = shlex.split(self.command) - print(command_splitted) + command_split = shlex.split(self.command) + + if args.get('append_current_file', False) and self._active_file_name(): + command_split.extend(('--', self._active_file_name())) + + print(command_split) + + self.may_change_files = bool(args.get('may_change_files', True)) if show_in == 'pane_below': - self.run_command(command_splitted) + self.run_command(command_split) elif show_in == 'quick_panel': - self.run_command(command_splitted, self.show_in_quick_panel) + self.run_command(command_split, self.show_in_quick_panel) elif show_in == 'new_tab': - self.run_command(command_splitted, self.show_in_new_tab) + self.run_command(command_split, self.show_in_new_tab) + elif show_in == 'suppress': + self.run_command(command_split, self.do_nothing) def show_in_quick_panel(self, result): self.results = list(result.rstrip().split('\n')) diff --git a/history.py b/history.py index 59ee921e..ada8114f 100644 --- a/history.py +++ b/history.py @@ -194,6 +194,7 @@ def ls_panel_done(self, picked): def show_done(self, result): self.scratch(result, title="%s:%s" % (self.fileRef, self.filename)) + class GitDocumentCommand(GitBlameCommand): def get_lines(self): selection = self.view.sel()[0] # todo: multi-select support? diff --git a/repo.py b/repo.py index f4f6d52f..78b5d4bd 100644 --- a/repo.py +++ b/repo.py @@ -1,7 +1,7 @@ import os import sublime -from .git import GitTextCommand, GitWindowCommand, git_root_exist +from .git import GitWindowCommand, git_root_exist class GitInit(object): @@ -123,16 +123,6 @@ def panel_done(self, picked): self.run_command(['git', 'show', picked_tag]) -class GitPushTagsCommand(GitWindowCommand): - def run(self): - self.run_command(['git', 'push', '--tags']) - - -class GitPullRebaseCommand(GitWindowCommand): - def run(self): - self.run_command(['git', 'pull', '--rebase'], callback=self.panel) - - class GitCheckoutTagCommand(GitWindowCommand): def run(self): self.run_command(['git', 'tag'], self.fetch_tag) @@ -152,28 +142,11 @@ def panel_done(self, picked): self.run_command(['git', 'checkout', "tags/%s" % picked_tag]) -class GitCheckoutCommand(GitTextCommand): - may_change_files = True - - def run(self, edit): - self.run_command(['git', 'checkout', self.get_file_name()]) - - -class GitFetchCommand(GitWindowCommand): - def run(self): - self.run_command(['git', 'fetch'], callback=self.panel) - - -class GitPullCommand(GitWindowCommand): - def run(self): - self.run_command(['git', 'pull'], callback=self.panel) - - class GitPullCurrentBranchCommand(GitWindowCommand): command_to_run_after_describe = 'pull' def run(self): - self.run_command(['git', 'describe', '--contains', '--all', 'HEAD'], callback=self.describe_done) + self.run_command(['git', 'describe', '--contains', '--all', 'HEAD'], callback=self.describe_done) def describe_done(self, result): self.current_branch = result.strip() @@ -194,10 +167,5 @@ def panel_done(self, picked=0): self.run_command(['git', self.command_to_run_after_describe, self.picked_remote, self.current_branch]) -class GitPushCommand(GitWindowCommand): - def run(self): - self.run_command(['git', 'push'], callback=self.panel) - - class GitPushCurrentBranchCommand(GitPullCurrentBranchCommand): command_to_run_after_describe = 'push' diff --git a/stash.py b/stash.py index 842e027b..0fabf2ec 100644 --- a/stash.py +++ b/stash.py @@ -1,18 +1,6 @@ from .git import GitWindowCommand -class GitStashCommand(GitWindowCommand): - may_change_files = True - - def run(self): - self.run_command(['git', 'stash']) - - -class GitStashPopCommand(GitWindowCommand): - def run(self): - self.run_command(['git', 'stash', 'pop']) - - class GitStashApplyCommand(GitWindowCommand): may_change_files = True command_to_run_after_list = 'apply' From 42e41283d1f2b28ec40f8d9c570789b6ba211d24 Mon Sep 17 00:00:00 2001 From: David Lynch Date: Sun, 18 May 2014 19:42:09 -0500 Subject: [PATCH 44/80] No-whitespace diffs Sort of references pull request 278. But implemented differently. --- Default.sublime-commands | 19 +++++++++++++++++-- Main.sublime-menu | 3 +++ diff.py | 17 +++++++++++------ 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/Default.sublime-commands b/Default.sublime-commands index c1743ae8..88d79db1 100644 --- a/Default.sublime-commands +++ b/Default.sublime-commands @@ -51,13 +51,28 @@ "command": "git_diff" } ,{ - "caption": "Git: Diff All", + "caption": "Git: Diff All Files", "command": "git_diff_all" } ,{ - "caption": "Git: Diff Staged", + "caption": "Git: Diff Staged Files", "command": "git_diff_commit" } + ,{ + "caption": "Git: Diff Current File (Ignore Whitespace)", + "command": "git_diff", + "args": { "ignore_whitespace": true } + } + ,{ + "caption": "Git: Diff All Files (Ignore Whitespace)", + "command": "git_diff_all", + "args": { "ignore_whitespace": true } + } + ,{ + "caption": "Git: Diff Staged Files (Ignore Whitespace)", + "command": "git_diff_commit", + "args": { "ignore_whitespace": true } + } ,{ "caption": "Git: Diff Tool Current File", "command": "git_raw", "args": { "command": "git difftool", "append_current_file": true, "may_change_files": false } diff --git a/Main.sublime-menu b/Main.sublime-menu index 73bb63fc..898c7d71 100644 --- a/Main.sublime-menu +++ b/Main.sublime-menu @@ -15,6 +15,7 @@ ,{ "caption": "Graph", "command": "git_graph" } ,{ "caption": "-" } ,{ "caption": "Diff", "command": "git_diff" } + ,{ "caption": "Diff (no whitespace)", "command": "git_diff", "args": { "ignore_whitespace": true } } ,{ "caption": "DiffTool", "command": "git_raw", "args": { "command": "git difftool", "append_current_file": true, "may_change_files": false } } ,{ "caption": "-" } ,{ "caption": "Add", "command": "git_raw", "args": { "command": "git add", "append_current_file": true } } @@ -39,7 +40,9 @@ ,{ "caption": "Graph", "command": "git_graph_all" } ,{ "caption": "-" } ,{ "caption": "Diff", "command": "git_diff_all" } + ,{ "caption": "Diff (no whitespace)", "command": "git_diff_all", "args": { "ignore_whitespace": true } } ,{ "caption": "Diff Staged", "command": "git_diff_commit" } + ,{ "caption": "Diff Staged (no whitespace)", "command": "git_diff_commit", "args": { "ignore_whitespace": true } } ,{ "caption": "Diff Tool", "command": "git_raw", "args": { "command": "git difftool", "may_change_files": false } } ,{ "caption": "Reset Hard", "command": "git_reset_hard_head" } ,{ "caption": "-" } diff --git a/diff.py b/diff.py index 41c829ea..08740fc0 100644 --- a/diff.py +++ b/diff.py @@ -5,9 +5,12 @@ class GitDiff (object): - def run(self, edit=None): - self.run_command(['git', 'diff', '--no-color', '--', self.get_file_name()], - self.diff_done) + def run(self, edit=None, ignore_whitespace=False): + command = ['git', 'diff', '--no-color'] + if ignore_whitespace: + command.extend(('--ignore-all-space', '--ignore-blank-lines')) + command.extend(('--', self.get_file_name())) + self.run_command(command, self.diff_done) def diff_done(self, result): if not result.strip(): @@ -32,9 +35,11 @@ def diff_done(self, result): class GitDiffCommit (object): - def run(self, edit=None): - self.run_command(['git', 'diff', '--cached', '--no-color'], - self.diff_done) + def run(self, edit=None, ignore_whitespace=False): + command = ['git', 'diff', '--cached', '--no-color'] + if ignore_whitespace: + command.extend(('--ignore-all-space', '--ignore-blank-lines')) + self.run_command(command, self.diff_done) def diff_done(self, result): if not result.strip(): From 96914dfe15b9b3e49eb901297321255769822f65 Mon Sep 17 00:00:00 2001 From: Rahul Ramadas Date: Sun, 8 Jun 2014 12:48:06 -0700 Subject: [PATCH 45/80] Workaround a bug in Sublime Text 3 --- status.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/status.py b/status.py index bb0139af..0620948d 100644 --- a/status.py +++ b/status.py @@ -45,8 +45,9 @@ def panel_followup(self, picked_status, picked_file, picked_index): s = sublime.load_settings("Git.sublime-settings") root = git_root(self.get_working_dir()) if picked_status == '??' or s.get('status_opens_file') or self.force_open: - if(os.path.isfile(os.path.join(root, picked_file))): - self.window.open_file(os.path.join(root, picked_file)) + file_name = os.path.join(root, picked_file) + if(os.path.isfile(file_name)): + sublime.set_timeout(lambda: self.window.open_file(file_name), 0) else: self.run_command(['git', 'diff', '--no-color', '--', picked_file.strip('"')], self.diff_done, working_dir=root) From 326ca8b30dc0259baa662dce708d0730eb4e7a05 Mon Sep 17 00:00:00 2001 From: Rahul Ramadas Date: Sun, 8 Jun 2014 18:09:53 -0700 Subject: [PATCH 46/80] Comment explaining workaround --- status.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/status.py b/status.py index 0620948d..64360b4f 100644 --- a/status.py +++ b/status.py @@ -47,6 +47,9 @@ def panel_followup(self, picked_status, picked_file, picked_index): if picked_status == '??' or s.get('status_opens_file') or self.force_open: file_name = os.path.join(root, picked_file) if(os.path.isfile(file_name)): + # Sublime Text 3 has a bug wherein calling open_file from within a panel + # callback causes the new view to not have focus. Make a deferred call via + # set_timeout to workaround this issue. sublime.set_timeout(lambda: self.window.open_file(file_name), 0) else: self.run_command(['git', 'diff', '--no-color', '--', picked_file.strip('"')], From 5a2c8c7bfef2c3ac0d2f90cf18d05a1fb459c428 Mon Sep 17 00:00:00 2001 From: David Lynch Date: Thu, 26 Jun 2014 14:56:16 -0500 Subject: [PATCH 47/80] Let enter jump to blame commits --- Default.sublime-keymap | 4 +++- history.py | 11 ++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Default.sublime-keymap b/Default.sublime-keymap index 92ee9df7..a75c8e54 100644 --- a/Default.sublime-keymap +++ b/Default.sublime-keymap @@ -2,5 +2,7 @@ {"keys": ["enter"], "command": "git_goto_diff", "context": [{"key": "selector", "operand": "markup.inserted.diff"}]}, {"keys": ["enter"], "command": "git_goto_diff", - "context": [{"key": "selector", "operand": "markup.deleted.diff"}]} + "context": [{"key": "selector", "operand": "markup.deleted.diff"}]}, + {"keys": ["enter"], "command": "git_goto_blame", + "context": [{"key": "selector", "operand": "text.git-blame"}]} ] diff --git a/history.py b/history.py index ada8114f..435de1a4 100644 --- a/history.py +++ b/history.py @@ -1,6 +1,6 @@ +import sublime_plugin import functools import re -from operator import methodcaller import sublime from .git import GitTextCommand, GitWindowCommand, plugin_file @@ -224,3 +224,12 @@ def show_done(self, result): commits = [commit for d, commit in commits] self.scratch('\n\n'.join(commits), title="Git Commit Documentation") + + +class GitGotoBlame(sublime_plugin.TextCommand): + def run(self, edit): + line = self.view.substr(self.view.line(self.view.sel()[0].a)) + commit = line.split(" ")[0] + if not commit or commit == "00000000": + return + self.view.window().run_command("git_raw", {"command": "git show %s" % commit, "show_in": "new_tab", "may_change_files": False}) From e35044deb8e72cf9db8b472f446b121ac25c49f4 Mon Sep 17 00:00:00 2001 From: David Lynch Date: Thu, 26 Jun 2014 15:32:43 -0500 Subject: [PATCH 48/80] Show the hash in the first line of the palette, so filtering works --- history.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/history.py b/history.py index 435de1a4..a5956a97 100644 --- a/history.py +++ b/history.py @@ -55,7 +55,7 @@ def run_log(self, follow, *args): # 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)', + command = ['git', 'log', '--pretty=%s (%h)\a%an <%aE>\a%ad (%ar)', '--date=local', '--max-count=9000', '--follow' if follow else None] command.extend(args) self.run_command( @@ -97,7 +97,7 @@ 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)', + ['git', 'log', '--pretty=%s (%h)\a%an <%aE>\a%ad (%ar)', '--date=local', '--max-count=9000', '--', self.get_file_name()], self.show_done) From 7e400e5e87d3337755ab2f289d326f761b52bc72 Mon Sep 17 00:00:00 2001 From: David Lynch Date: Tue, 1 Jul 2014 02:14:37 -0500 Subject: [PATCH 49/80] Fix hash-finding in log panel --- history.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/history.py b/history.py index a5956a97..11205e83 100644 --- a/history.py +++ b/history.py @@ -70,8 +70,9 @@ 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]) + # the commit hash is the last thing on the first line, in brackets + ref = item[0].split(' ')[-1].strip('()') + self.log_result(ref) def log_result(self, ref): # I'm not certain I should have the file name here; it restricts the @@ -110,8 +111,8 @@ 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] + # the commit hash is the last thing on the first line, in brackets + ref = item[0].split(' ')[-1].strip('()') self.run_command( ['git', 'show', '%s:%s' % (ref, self.get_relative_file_name())], self.details_done, @@ -166,7 +167,6 @@ def branch_panel_done(self, picked): 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], From cddd7fb52fde41073910c4ca3aa5ec3909f2873e Mon Sep 17 00:00:00 2001 From: fauxpark Date: Tue, 8 Jul 2014 20:59:30 +1000 Subject: [PATCH 50/80] Graph syntax highlighting fix --- syntax/Git Graph.tmLanguage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syntax/Git Graph.tmLanguage b/syntax/Git Graph.tmLanguage index 4c19eec8..f9fcb67f 100644 --- a/syntax/Git Graph.tmLanguage +++ b/syntax/Git Graph.tmLanguage @@ -45,7 +45,7 @@ match - ^([| *\\]+)([0-9a-f]{4,40}) -( \(.*?\))? (.*) (\(.*) (<.*>) .* + ^([| *\\]+)([0-9a-f]{4,40}) -( \(.*?\))? (.*) (\(.*) (<.*?>) .* name log-entry.git-graph From 34d32b960a780923e2685e4bd46c536d66b77a54 Mon Sep 17 00:00:00 2001 From: David Lynch Date: Sun, 13 Jul 2014 17:18:27 -0500 Subject: [PATCH 51/80] Don't gutter the diff icons any more. Fixes #356. --- diff.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/diff.py b/diff.py index 08740fc0..f1e5edfb 100644 --- a/diff.py +++ b/diff.py @@ -23,12 +23,6 @@ def diff_done(self, result): else: view = self.scratch(result, title="Git Diff", syntax=syntax) - lines_inserted = view.find_all(r'^\+[^+]{2} ') - lines_deleted = view.find_all(r'^-[^-]{2} ') - - view.add_regions("inserted", lines_inserted, "markup.inserted.diff", "dot", sublime.HIDDEN) - view.add_regions("deleted", lines_deleted, "markup.deleted.diff", "dot", sublime.HIDDEN) - # Store the git root directory in the view so we can resolve relative paths # when the user wants to navigate to the source file. view.settings().set("git_root_dir", git_root(self.get_working_dir())) From c9f8cccd4d739fa301dc57a6cc7be210f7692d11 Mon Sep 17 00:00:00 2001 From: David Lynch Date: Tue, 30 Sep 2014 11:56:55 -0500 Subject: [PATCH 52/80] New command to import your gitignore into project settings --- Default.sublime-commands | 4 ++++ git.py | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/Default.sublime-commands b/Default.sublime-commands index 88d79db1..b822ade2 100644 --- a/Default.sublime-commands +++ b/Default.sublime-commands @@ -241,4 +241,8 @@ "caption": "Git: Commit history", "command": "git_commit_history" } + ,{ + "caption": "Git: Update Project Ignored Files", + "command": "git_update_ignore" + } ] diff --git a/git.py b/git.py index 54bfad90..3c82e584 100644 --- a/git.py +++ b/git.py @@ -450,3 +450,30 @@ class GitGitkCommand(GitTextCommand): def run(self, edit): command = ['gitk'] self.run_command(command) + + +class GitUpdateIgnoreCommand(sublime_plugin.TextCommand): + def run(self, edit): + data = self.view.window().project_data() + project_file_name = self.view.window().project_file_name() + for folder in data['folders']: + path = folder['path'] + if project_file_name: + path = os.path.join(os.path.dirname(project_file_name), path) + gitignore = os.path.join(path, ".gitignore") + print("gitignore path", gitignore) + if (os.path.exists(gitignore)): + with open(gitignore) as gitignore_file: + if not "folder_exclude_patterns" in folder: + folder["folder_exclude_patterns"] = [] + if not "file_exclude_patterns" in folder: + folder["file_exclude_patterns"] = [] + for pattern in gitignore_file: + pattern = pattern.strip() + if os.path.isdir(os.path.join(path, pattern)): + if not pattern in folder["folder_exclude_patterns"]: + folder["folder_exclude_patterns"].append(pattern) + else: + if not pattern in folder["file_exclude_patterns"]: + folder["file_exclude_patterns"].append(pattern) + self.view.window().set_project_data(data) From 46872f1d47028ac54e0fdbdf831cd57f1632c23b Mon Sep 17 00:00:00 2001 From: fauxpark Date: Sun, 2 Nov 2014 02:16:53 +1100 Subject: [PATCH 53/80] Match parenthesis at end of timestamp in graph syntax --- syntax/Git Graph.tmLanguage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syntax/Git Graph.tmLanguage b/syntax/Git Graph.tmLanguage index f9fcb67f..b2ea37ca 100644 --- a/syntax/Git Graph.tmLanguage +++ b/syntax/Git Graph.tmLanguage @@ -45,7 +45,7 @@ match - ^([| *\\]+)([0-9a-f]{4,40}) -( \(.*?\))? (.*) (\(.*) (<.*?>) .* + ^([| *\\]+)([0-9a-f]{4,40}) -( \(.*?\))? (.*) (\(.*\)) (<.*?>) .* name log-entry.git-graph From b768edf4860eab79d5f689b0169bde094caf7c9e Mon Sep 17 00:00:00 2001 From: zzjin Date: Sun, 30 Nov 2014 18:31:49 +0800 Subject: [PATCH 54/80] Update git.py Work around for OSX 10.10 PATH issues like master branch to support st3 --- git.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/git.py b/git.py index 3c82e584..3a87cf73 100644 --- a/git.py +++ b/git.py @@ -175,7 +175,8 @@ def run(self): proc = subprocess.Popen(self.command, stdout=self.stdout, stderr=subprocess.STDOUT, stdin=subprocess.PIPE, startupinfo=startupinfo, - shell=shell, universal_newlines=False) + shell=shell, universal_newlines=False, + env=os.environ) output = proc.communicate(self.stdin)[0] if not output: output = '' From 5868e43089e989da8a26ca682b9d2cf6a0a029c3 Mon Sep 17 00:00:00 2001 From: Arjen Brouwer Date: Thu, 4 Dec 2014 11:16:33 +0100 Subject: [PATCH 55/80] fixed hanging git process with msysgit on windows The msysgit ssh.exe process was unable to find SSH keys because of a missing HOME environment variable on windows. --- git.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/git.py b/git.py index 3a87cf73..5f68d425 100644 --- a/git.py +++ b/git.py @@ -160,7 +160,6 @@ def run(self): if self.working_dir != "": os.chdir(self.working_dir) - # Windows needs startupinfo in order to start process in background startupinfo = None if os.name == 'nt': @@ -171,12 +170,16 @@ def run(self): if sublime.platform() == 'windows': shell = True + env = os.environ.copy() + if sublime.platform() == 'windows' and 'HOME' not in env: + env['HOME'] = env['USERPROFILE'] + # universal_newlines seems to break `log` in python3 proc = subprocess.Popen(self.command, stdout=self.stdout, stderr=subprocess.STDOUT, stdin=subprocess.PIPE, startupinfo=startupinfo, shell=shell, universal_newlines=False, - env=os.environ) + env=env) output = proc.communicate(self.stdin)[0] if not output: output = '' @@ -191,7 +194,6 @@ def run(self): else: raise e - class GitScratchOutputCommand(sublime_plugin.TextCommand): def run(self, edit, output = '', output_file = None, clear = False): if clear: From a2a7ba1d4bbd58e4d3b7b365b48b4e9ab121770e Mon Sep 17 00:00:00 2001 From: PeterPablo Date: Fri, 5 Dec 2014 13:29:01 +0100 Subject: [PATCH 56/80] fix ASCII escape codes in `git log` commands ... if user has `color=always` present in his `~/.gitconfig` --- history.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/history.py b/history.py index 11205e83..dd5019fb 100644 --- a/history.py +++ b/history.py @@ -55,7 +55,7 @@ def run_log(self, follow, *args): # 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 (%h)\a%an <%aE>\a%ad (%ar)', + command = ['git', 'log', '--no-color', '--pretty=%s (%h)\a%an <%aE>\a%ad (%ar)', '--date=local', '--max-count=9000', '--follow' if follow else None] command.extend(args) self.run_command( @@ -79,7 +79,7 @@ def log_result(self, ref): # 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()], + ['git', 'log', '--no-color', '-p', '-1', ref, '--', self.get_file_name()], self.details_done) def details_done(self, result): @@ -98,7 +98,7 @@ class GitShow(object): def run(self, edit=None): # GitLog Copy-Past self.run_command( - ['git', 'log', '--pretty=%s (%h)\a%an <%aE>\a%ad (%ar)', + ['git', 'log', '--no-color', '--pretty=%s (%h)\a%an <%aE>\a%ad (%ar)', '--date=local', '--max-count=9000', '--', self.get_file_name()], self.show_done) From 65ffb325ca2c983abeab52552a71805d965bf420 Mon Sep 17 00:00:00 2001 From: bordaigorl Date: Fri, 23 Jan 2015 15:00:48 +0000 Subject: [PATCH 57/80] Fix: `generic_done` may need to ignore extra arguments --- git.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git.py b/git.py index 5f68d425..70df1d2b 100644 --- a/git.py +++ b/git.py @@ -238,7 +238,7 @@ def run_command(self, command, callback=None, show_status=True, message = kwargs.get('status_message', False) or ' '.join(command) sublime.status_message(message) - def generic_done(self, result): + def generic_done(self, result, **kw): if self.may_change_files and self.active_view() and self.active_view().file_name(): if self.active_view().is_dirty(): result = "WARNING: Current view is dirty.\n\n" From f0c762309686b7d1aabc42a8bed38be43f502f13 Mon Sep 17 00:00:00 2001 From: bordaigorl Date: Fri, 23 Jan 2015 19:41:52 +0000 Subject: [PATCH 58/80] Amend Commit allows to change commit message even when nothing new is staged --- commit.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/commit.py b/commit.py index 19bea66c..6f53d935 100644 --- a/commit.py +++ b/commit.py @@ -51,6 +51,7 @@ def add_done(self, message, result): class GitCommitCommand(GitWindowCommand): active_message = False extra_options = "" + quit_when_nothing_staged = True def run(self): self.lines = [] @@ -68,7 +69,7 @@ def porcelain_status_done(self, result): if line and not line[0].isspace(): has_staged_files = True break - if not has_staged_files: + if not has_staged_files and self.quit_when_nothing_staged: self.panel("Nothing to commit") return # Okay, get the template! @@ -132,6 +133,7 @@ def commit_done(self, result, **kwargs): class GitCommitAmendCommand(GitCommitCommand): extra_options = "--amend" + quit_when_nothing_staged = False def diff_done(self, result): self.after_show = result From f9b64b891befc70de2b1b42a53c7b22af5b98d36 Mon Sep 17 00:00:00 2001 From: David Lynch Date: Thu, 29 Jan 2015 10:04:39 -0600 Subject: [PATCH 59/80] Expose whether commands are running. Use this to stop the statusbar clobbering stuff. --- git.py | 29 +++++++++++++++++++---------- statusbar.py | 7 +++++-- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/git.py b/git.py index 70df1d2b..1eccd6d3 100644 --- a/git.py +++ b/git.py @@ -134,6 +134,9 @@ def find_git(): return git_path GIT = find_git() +commands_working = 0 +def are_commands_working(): + return commands_working != 0 class CommandThread(threading.Thread): def __init__(self, command, on_done, working_dir="", fallback_encoding="", **kwargs): @@ -153,11 +156,15 @@ def __init__(self, command, on_done, working_dir="", fallback_encoding="", **kwa self.kwargs = kwargs def run(self): - try: - # Ignore directories that no longer exist - if not os.path.isdir(self.working_dir): - return + global commands_working + # Ignore directories that no longer exist + if not os.path.isdir(self.working_dir): + return + commands_working = commands_working + 1 + output = '' + callback = self.on_done + try: if self.working_dir != "": os.chdir(self.working_dir) # Windows needs startupinfo in order to start process in background @@ -183,16 +190,18 @@ def run(self): output = proc.communicate(self.stdin)[0] if not output: output = '' - - main_thread(self.on_done, - _make_text_safeish(output, self.fallback_encoding), **self.kwargs) + output = _make_text_safeish(output, self.fallback_encoding) except subprocess.CalledProcessError as e: - main_thread(self.on_done, e.returncode) + output = e.returncode except OSError as e: + callback = sublime.error_message if e.errno == 2: - main_thread(sublime.error_message, "Git binary could not be found in PATH\n\nConsider using the git_command setting for the Git plugin\n\nPATH is: %s" % os.environ['PATH']) + output = "Git binary could not be found in PATH\n\nConsider using the git_command setting for the Git plugin\n\nPATH is: %s" % os.environ['PATH'] else: - raise e + output = e.strerror + finally: + commands_working = commands_working - 1 + main_thread(callback, output, **self.kwargs) class GitScratchOutputCommand(sublime_plugin.TextCommand): def run(self, edit, output = '', output_file = None, clear = False): diff --git a/statusbar.py b/statusbar.py index 0e90693d..ec058e6b 100644 --- a/statusbar.py +++ b/statusbar.py @@ -2,7 +2,7 @@ import sublime import sublime_plugin -from .git import GitTextCommand +from .git import GitTextCommand, do_when, are_commands_working class GitBranchStatusListener(sublime_plugin.EventListener): @@ -21,7 +21,10 @@ def run(self, view): else: self.view.set_status("git-branch", "") if (s.get("statusbar_status")): - self.run_command(['git', 'status', '--porcelain'], self.status_done, show_status=False, no_save=True) + do_when( + lambda: not are_commands_working(), + self.run_command, + ['git', 'status', '--porcelain'], self.status_done, show_status=False, no_save=True) else: self.view.set_status("git-status", "") From 9d2884636d8665958d9e25cea9aff5a74c5c8bb5 Mon Sep 17 00:00:00 2001 From: David Lynch Date: Thu, 29 Jan 2015 10:14:42 -0600 Subject: [PATCH 60/80] Add lock-waiting to the standard run_command --- git.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/git.py b/git.py index 1eccd6d3..41f371cc 100644 --- a/git.py +++ b/git.py @@ -216,7 +216,7 @@ class GitCommand(object): may_change_files = False def run_command(self, command, callback=None, show_status=True, - filter_empty_args=True, no_save=False, **kwargs): + filter_empty_args=True, no_save=False, wait_for_lock=True, **kwargs): if filter_empty_args: command = [arg for arg in command if arg] if 'working_dir' not in kwargs: @@ -224,6 +224,12 @@ def run_command(self, command, callback=None, show_status=True, if 'fallback_encoding' not in kwargs and self.active_view() and self.active_view().settings().get('fallback_encoding'): kwargs['fallback_encoding'] = self.active_view().settings().get('fallback_encoding').rpartition('(')[2].rpartition(')')[0] + root = git_root(self.get_working_dir()) + if wait_for_lock and root and os.path.exists(os.path.join(root, '.git', 'index.lock')): + print("waiting for index.lock", command) + do_when(lambda: not os.path.exists(os.path.join(root, '.git', 'index.lock')), + self.run_command, command, callback=callback, show_status=show_status, filter_empty_args=filter_empty_args, no_save=no_save, wait_for_lock=wait_for_lock, **kwargs) + s = sublime.load_settings("Git.sublime-settings") if s.get('save_first') and self.active_view() and self.active_view().is_dirty() and not no_save: self.active_view().run_command('save') From fe78800bb7c77789e90c850e31f9be6dfa9e4081 Mon Sep 17 00:00:00 2001 From: Jonathan McCall Date: Fri, 6 Feb 2015 11:34:39 -0500 Subject: [PATCH 61/80] Change callback parameter to command to avoid collision When callback is being passed to self.run_command it conflicts with the callback parameter for do_when. Instead let's change the callback parameter on do_when to be command. --- git.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/git.py b/git.py index 41f371cc..b0fbb660 100644 --- a/git.py +++ b/git.py @@ -71,10 +71,10 @@ def plugin_file(name): return PLUGIN_DIRECTORY + '/' + name -def do_when(conditional, callback, *args, **kwargs): +def do_when(conditional, command, *args, **kwargs): if conditional(): - return callback(*args, **kwargs) - sublime.set_timeout(functools.partial(do_when, conditional, callback, *args, **kwargs), 50) + return command(*args, **kwargs) + sublime.set_timeout(functools.partial(do_when, conditional, command, *args, **kwargs), 50) def goto_xy(view, line, col): @@ -228,7 +228,9 @@ def run_command(self, command, callback=None, show_status=True, if wait_for_lock and root and os.path.exists(os.path.join(root, '.git', 'index.lock')): print("waiting for index.lock", command) do_when(lambda: not os.path.exists(os.path.join(root, '.git', 'index.lock')), - self.run_command, command, callback=callback, show_status=show_status, filter_empty_args=filter_empty_args, no_save=no_save, wait_for_lock=wait_for_lock, **kwargs) + self.run_command, command, callback=callback, + show_status=show_status, filter_empty_args=filter_empty_args, + no_save=no_save, wait_for_lock=wait_for_lock, **kwargs) s = sublime.load_settings("Git.sublime-settings") if s.get('save_first') and self.active_view() and self.active_view().is_dirty() and not no_save: From a52891605e5043e9e7b5122123f48f0aeb1b1d53 Mon Sep 17 00:00:00 2001 From: Jonathan McCall Date: Fri, 6 Feb 2015 13:37:36 -0500 Subject: [PATCH 62/80] Add return so diff command does not show output twice --- git.py | 1 + 1 file changed, 1 insertion(+) diff --git a/git.py b/git.py index b0fbb660..1d7cc386 100644 --- a/git.py +++ b/git.py @@ -231,6 +231,7 @@ def run_command(self, command, callback=None, show_status=True, self.run_command, command, callback=callback, show_status=show_status, filter_empty_args=filter_empty_args, no_save=no_save, wait_for_lock=wait_for_lock, **kwargs) + return s = sublime.load_settings("Git.sublime-settings") if s.get('save_first') and self.active_view() and self.active_view().is_dirty() and not no_save: From 7dc97043972fce3d0a72922d2404a64c9626d1ca Mon Sep 17 00:00:00 2001 From: bordaigorl Date: Tue, 11 Aug 2015 19:21:44 +0200 Subject: [PATCH 63/80] BugFix: when selecting a folder in the "Add..." panel, `git rm` was attempted instead of `git add` --- add.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/add.py b/add.py index 6bb1d953..c39a7406 100644 --- a/add.py +++ b/add.py @@ -27,7 +27,8 @@ def panel_followup(self, picked_status, picked_file, picked_index): else: command = ['git'] picked_file = picked_file.strip('"') - if os.path.isfile(working_dir + "/" + picked_file): + realpath = working_dir + "/" + picked_file + if os.path.isfile(realpath) or os.path.isdir(realpath): command += ['add'] else: command += ['rm'] From 8d68b524bf76b790a075428401a573017beeefbf Mon Sep 17 00:00:00 2001 From: Jonathan McCall Date: Mon, 17 Aug 2015 17:51:44 -0400 Subject: [PATCH 64/80] Set word wrap to false when creating scratch views When viewing the output of git diff and git blame it becomes hard to read when many long lines begin to wrap. --- git.py | 1 + 1 file changed, 1 insertion(+) diff --git a/git.py b/git.py index 1d7cc386..0b3e690b 100644 --- a/git.py +++ b/git.py @@ -292,6 +292,7 @@ def scratch(self, output, title=False, position=None, **kwargs): scratch_file.set_scratch(True) self._output_to_view(scratch_file, output, **kwargs) scratch_file.set_read_only(True) + scratch_file.settings().set('word_wrap', False) if position: sublime.set_timeout(lambda: scratch_file.set_viewport_position(position), 0) return scratch_file From d6fdeb8f388ce111fb69b1584d3c8a58e2f07936 Mon Sep 17 00:00:00 2001 From: Jonathan McCall Date: Wed, 19 Aug 2015 13:08:19 -0400 Subject: [PATCH 65/80] Use threading lock object instead of commands_working counter --- git.py | 21 +++++---------------- statusbar.py | 7 ++----- 2 files changed, 7 insertions(+), 21 deletions(-) diff --git a/git.py b/git.py index 1d7cc386..851e662f 100644 --- a/git.py +++ b/git.py @@ -134,11 +134,10 @@ def find_git(): return git_path GIT = find_git() -commands_working = 0 -def are_commands_working(): - return commands_working != 0 class CommandThread(threading.Thread): + command_lock = threading.Lock() + def __init__(self, command, on_done, working_dir="", fallback_encoding="", **kwargs): threading.Thread.__init__(self) self.command = command @@ -156,12 +155,11 @@ def __init__(self, command, on_done, working_dir="", fallback_encoding="", **kwa self.kwargs = kwargs def run(self): - global commands_working # Ignore directories that no longer exist if not os.path.isdir(self.working_dir): return - commands_working = commands_working + 1 + self.command_lock.acquire() output = '' callback = self.on_done try: @@ -200,7 +198,7 @@ def run(self): else: output = e.strerror finally: - commands_working = commands_working - 1 + self.command_lock.release() main_thread(callback, output, **self.kwargs) class GitScratchOutputCommand(sublime_plugin.TextCommand): @@ -216,7 +214,7 @@ class GitCommand(object): may_change_files = False def run_command(self, command, callback=None, show_status=True, - filter_empty_args=True, no_save=False, wait_for_lock=True, **kwargs): + filter_empty_args=True, no_save=False, **kwargs): if filter_empty_args: command = [arg for arg in command if arg] if 'working_dir' not in kwargs: @@ -224,15 +222,6 @@ def run_command(self, command, callback=None, show_status=True, if 'fallback_encoding' not in kwargs and self.active_view() and self.active_view().settings().get('fallback_encoding'): kwargs['fallback_encoding'] = self.active_view().settings().get('fallback_encoding').rpartition('(')[2].rpartition(')')[0] - root = git_root(self.get_working_dir()) - if wait_for_lock and root and os.path.exists(os.path.join(root, '.git', 'index.lock')): - print("waiting for index.lock", command) - do_when(lambda: not os.path.exists(os.path.join(root, '.git', 'index.lock')), - self.run_command, command, callback=callback, - show_status=show_status, filter_empty_args=filter_empty_args, - no_save=no_save, wait_for_lock=wait_for_lock, **kwargs) - return - s = sublime.load_settings("Git.sublime-settings") if s.get('save_first') and self.active_view() and self.active_view().is_dirty() and not no_save: self.active_view().run_command('save') diff --git a/statusbar.py b/statusbar.py index ec058e6b..0e90693d 100644 --- a/statusbar.py +++ b/statusbar.py @@ -2,7 +2,7 @@ import sublime import sublime_plugin -from .git import GitTextCommand, do_when, are_commands_working +from .git import GitTextCommand class GitBranchStatusListener(sublime_plugin.EventListener): @@ -21,10 +21,7 @@ def run(self, view): else: self.view.set_status("git-branch", "") if (s.get("statusbar_status")): - do_when( - lambda: not are_commands_working(), - self.run_command, - ['git', 'status', '--porcelain'], self.status_done, show_status=False, no_save=True) + self.run_command(['git', 'status', '--porcelain'], self.status_done, show_status=False, no_save=True) else: self.view.set_status("git-status", "") From 816bc79122a47df8e5c81577a9e0fafc98bbde5e Mon Sep 17 00:00:00 2001 From: Christopher Dudley Date: Fri, 28 Aug 2015 16:50:54 -0400 Subject: [PATCH 66/80] Ignore comments and empty lines in .gitignore --- git.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/git.py b/git.py index 851e662f..cb6d54ea 100644 --- a/git.py +++ b/git.py @@ -480,7 +480,9 @@ def run(self, edit): folder["file_exclude_patterns"] = [] for pattern in gitignore_file: pattern = pattern.strip() - if os.path.isdir(os.path.join(path, pattern)): + if len(pattern) == 0 or pattern[0] == '#': + continue + elif os.path.isdir(os.path.join(path, pattern)): if not pattern in folder["folder_exclude_patterns"]: folder["folder_exclude_patterns"].append(pattern) else: From adf42c1aeac8a5f0d51ba809b638c6429f2b35fb Mon Sep 17 00:00:00 2001 From: Rafal Chlodnicki Date: Mon, 3 Dec 2012 02:32:14 +0100 Subject: [PATCH 67/80] Improve showing full commits from the blame view. #436 Changed the command to work reliably for any file, even ones outside the project. Previous code also failed for files within the project, when project had more than one directory added. Also added new syntax file for commit view with better highlighting. And use this syntax for git_document command and commits opened from git log commands. --- Default.sublime-keymap | 2 +- history.py | 27 ++++-- syntax/Git Commit View.tmLanguage | 145 ++++++++++++++++++++++++++++++ 3 files changed, 166 insertions(+), 8 deletions(-) create mode 100644 syntax/Git Commit View.tmLanguage diff --git a/Default.sublime-keymap b/Default.sublime-keymap index a75c8e54..bed23d27 100644 --- a/Default.sublime-keymap +++ b/Default.sublime-keymap @@ -3,6 +3,6 @@ "context": [{"key": "selector", "operand": "markup.inserted.diff"}]}, {"keys": ["enter"], "command": "git_goto_diff", "context": [{"key": "selector", "operand": "markup.deleted.diff"}]}, - {"keys": ["enter"], "command": "git_goto_blame", + {"keys": ["enter"], "command": "git_goto_commit", "context": [{"key": "selector", "operand": "text.git-blame"}]} ] diff --git a/history.py b/history.py index dd5019fb..46d6aef0 100644 --- a/history.py +++ b/history.py @@ -39,8 +39,10 @@ def get_lines(self): return begin_line + 1, end_line + 1 def blame_done(self, result, position=None): - self.scratch(result, title="Git Blame", position=position, - syntax=plugin_file("syntax/Git Blame.tmLanguage")) + view = self.scratch(result, title="Git Blame", position=position, + syntax=plugin_file("syntax/Git Blame.tmLanguage")) + # store working dir to be potentially used by the GitGotoCommit command + view.settings().set("git_working_dir", self.get_working_dir()) class GitLog(object): @@ -83,7 +85,8 @@ def log_result(self, ref): self.details_done) def details_done(self, result): - self.scratch(result, title="Git Commit Details", syntax=plugin_file("syntax/Git Commit Message.tmLanguage")) + self.scratch(result, title="Git Commit Details", + syntax=plugin_file("syntax/Git Commit View.tmLanguage")) class GitLogCommand(GitLog, GitTextCommand): @@ -223,13 +226,23 @@ def show_done(self, result): commits.sort(reverse=True) commits = [commit for d, commit in commits] - self.scratch('\n\n'.join(commits), title="Git Commit Documentation") + self.scratch('\n\n'.join(commits), title="Git Commit Documentation", + syntax=plugin_file("syntax/Git Commit View.tmLanguage")) -class GitGotoBlame(sublime_plugin.TextCommand): +class GitGotoCommit(GitTextCommand): def run(self, edit): - line = self.view.substr(self.view.line(self.view.sel()[0].a)) + view = self.view + line = view.substr(view.line(view.sel()[0].a)) commit = line.split(" ")[0] if not commit or commit == "00000000": return - self.view.window().run_command("git_raw", {"command": "git show %s" % commit, "show_in": "new_tab", "may_change_files": False}) + working_dir = view.settings().get("git_working_dir") + self.run_command(['git', 'show', commit], self.show_done, working_dir=working_dir) + + def show_done(self, result): + self.scratch(result, title="Git Commit View", + syntax=plugin_file("syntax/Git Commit View.tmLanguage")) + + def is_enabled(self): + return True diff --git a/syntax/Git Commit View.tmLanguage b/syntax/Git Commit View.tmLanguage new file mode 100644 index 00000000..5bb94c96 --- /dev/null +++ b/syntax/Git Commit View.tmLanguage @@ -0,0 +1,145 @@ + + + + + fileTypes + + git-commit-view + + name + Git Commit View + patterns + + + name + string.sha.git-blame + match + ^commit [a-f0-9]+$ + + + name + support.function.author.git-blame + match + ^Author: .+$ + + + name + constant.numeric.date.git-blame + match + ^Date: .+$ + + + match + (^diff --.+$) + name + string.path.git-diff + + + match + (^(((-{3}) .+)|((\*{3}) .+))$\n?|^(={4}) .+(?= - )) + name + meta.diff.header.from-file + + + match + (^(\+{3}) .+$\n?| (-) .* (={4})$\n?) + name + meta.diff.header.to-file + + + captures + + 1 + + name + punctuation.definition.range.diff + + 2 + + name + meta.toc-list.line-number.diff + + 3 + + name + punctuation.definition.range.diff + + + match + ^(@@)\s*(.+?)\s*(@@)($\n?)? + name + meta.diff.range.unified + + + captures + + 3 + + name + punctuation.definition.inserted.diff + + 6 + + name + punctuation.definition.inserted.diff + + + match + ^(((>)( .*)?)|((\+).*))$\n? + name + markup.inserted.diff + + + captures + + 1 + + name + punctuation.definition.inserted.diff + + + match + ^(!).*$\n? + name + markup.changed.diff + + + captures + + 3 + + name + punctuation.definition.inserted.diff + + 6 + + name + punctuation.definition.inserted.diff + + + match + ^(((<)( .*)?)|((-).*))$\n? + name + markup.deleted.diff + + + captures + + 1 + + name + meta.toc-list.file-name.diff + + + match + ^index (.+)$\n? + name + meta.diff.index + + + scopeName + text.git-commit-view + uuid + 5d37add9-1219-4174-b232-4bd423b84c0a + + From b11972bd1fa8165ab73ed78dae027557a52ea61c Mon Sep 17 00:00:00 2001 From: fauxpark Date: Fri, 9 Oct 2015 15:53:43 +1100 Subject: [PATCH 68/80] Add setting to configure rulers shown when committing --- Git Commit Message.sublime-settings | 3 --- Git.sublime-settings | 3 +++ commit.py | 5 +++++ 3 files changed, 8 insertions(+), 3 deletions(-) delete mode 100644 Git Commit Message.sublime-settings diff --git a/Git Commit Message.sublime-settings b/Git Commit Message.sublime-settings deleted file mode 100644 index ba5b6908..00000000 --- a/Git Commit Message.sublime-settings +++ /dev/null @@ -1,3 +0,0 @@ -{ - "rulers": [70] -} diff --git a/Git.sublime-settings b/Git.sublime-settings index acd87e81..be4bca03 100644 --- a/Git.sublime-settings +++ b/Git.sublime-settings @@ -37,4 +37,7 @@ // e.g. "Packages/Git/syntax/Git Commit Message.tmLanguage" ,"diff_syntax": "Packages/Diff/Diff.tmLanguage" + + // Rulers for commit view + ,"commit_rulers": [70] } diff --git a/commit.py b/commit.py index 19bea66c..babb20bd 100644 --- a/commit.py +++ b/commit.py @@ -81,6 +81,7 @@ def porcelain_status_done(self, result): def diff_done(self, result): settings = sublime.load_settings("Git.sublime-settings") historySize = settings.get('history_size') + rulers = settings.get('commit_rulers') def format(line): return '# ' + line.replace("\n", " ") @@ -100,6 +101,10 @@ def format(line): msg = self.window.new_file() msg.set_scratch(True) msg.set_name("COMMIT_EDITMSG") + + if rulers: + msg.settings().set('rulers', rulers) + self._output_to_view(msg, template, syntax=plugin_file("syntax/Git Commit Message.tmLanguage")) msg.sel().clear() msg.sel().add(sublime.Region(0, 0)) From 48254c70a2e21e216f6f9f4c9466ef3fdfd4e0e4 Mon Sep 17 00:00:00 2001 From: David Lynch Date: Thu, 26 Nov 2015 12:39:37 -0600 Subject: [PATCH 69/80] PEP8 pass --- add.py | 4 ++-- commit.py | 2 +- diff.py | 9 +++++---- git.py | 14 +++++++++----- repo.py | 2 +- 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/add.py b/add.py index 6bb1d953..b7a502be 100644 --- a/add.py +++ b/add.py @@ -14,7 +14,7 @@ def show_status_list(self): self.results = [ [" + All Files", "apart from untracked files"], [" + All Files", "including untracked files"], - ] + [[a,''] for a in self.results] + ] + [[a, ''] for a in self.results] return super(GitAddChoiceCommand, self).show_status_list() def panel_followup(self, picked_status, picked_file, picked_index): @@ -52,7 +52,7 @@ def cull_diff(self, result): "end": self.view.rowcol(sel.end())[0] + 1, }) - hunks = [{"diff":""}] + hunks = [{"diff": ""}] i = 0 matcher = re.compile('^@@ -([0-9]*)(?:,([0-9]*))? \+([0-9]*)(?:,([0-9]*))? @@') for line in result.splitlines(): diff --git a/commit.py b/commit.py index 19bea66c..cad25131 100644 --- a/commit.py +++ b/commit.py @@ -58,7 +58,7 @@ def run(self): self.run_command( ['git', 'status', '--untracked-files=no', '--porcelain'], self.porcelain_status_done - ) + ) def porcelain_status_done(self, result): # todo: split out these status-parsing things... asdf diff --git a/diff.py b/diff.py index f1e5edfb..40a2c1a0 100644 --- a/diff.py +++ b/diff.py @@ -1,4 +1,5 @@ -import sublime, sublime_plugin +import sublime +import sublime_plugin import os import re from .git import git_root, GitTextCommand, GitWindowCommand, do_when, goto_xy @@ -84,12 +85,12 @@ def run(self, edit): if not hunk_line: hunk_line = lineContent elif lineContent.startswith("+++ b/"): - self.file_name = v.substr(sublime.Region(line.a+6, line.b)).strip() + self.file_name = v.substr(sublime.Region(line.a + 6, line.b)).strip() break elif not hunk_line and not lineContent.startswith("-"): - line_offset = line_offset+1 + line_offset = line_offset + 1 - pt = v.line(pt-1).a + pt = v.line(pt - 1).a hunk = re.match(r"^@@ -(\d+),(\d+) \+(\d+),(\d+) @@.*", hunk_line) if not hunk: diff --git a/git.py b/git.py index 851e662f..72a0e503 100644 --- a/git.py +++ b/git.py @@ -13,6 +13,7 @@ git_root_cache = {} + def find_plugin_directory(f): dirname = os.path.split(os.path.dirname(f))[-1] return "Packages/" + dirname.replace(".sublime-package", "") @@ -104,6 +105,8 @@ def _test_paths_for_executable(paths, test_file): file_path = os.path.join(directory, test_file) if os.path.exists(file_path) and os.access(file_path, os.X_OK): return file_path + + def find_git(): # It turns out to be difficult to reliably run git, with varying paths # and subprocess environments across different platforms. So. Let's hack @@ -201,8 +204,9 @@ def run(self): self.command_lock.release() main_thread(callback, output, **self.kwargs) + class GitScratchOutputCommand(sublime_plugin.TextCommand): - def run(self, edit, output = '', output_file = None, clear = False): + def run(self, edit, output='', output_file=None, clear=False): if clear: region = sublime.Region(0, self.view.size()) self.view.erase(edit, region) @@ -474,16 +478,16 @@ def run(self, edit): print("gitignore path", gitignore) if (os.path.exists(gitignore)): with open(gitignore) as gitignore_file: - if not "folder_exclude_patterns" in folder: + if "folder_exclude_patterns" not in folder: folder["folder_exclude_patterns"] = [] - if not "file_exclude_patterns" in folder: + if "file_exclude_patterns" not in folder: folder["file_exclude_patterns"] = [] for pattern in gitignore_file: pattern = pattern.strip() if os.path.isdir(os.path.join(path, pattern)): - if not pattern in folder["folder_exclude_patterns"]: + if pattern not in folder["folder_exclude_patterns"]: folder["folder_exclude_patterns"].append(pattern) else: - if not pattern in folder["file_exclude_patterns"]: + if pattern not in folder["file_exclude_patterns"]: folder["file_exclude_patterns"].append(pattern) self.view.window().set_project_data(data) diff --git a/repo.py b/repo.py index 78b5d4bd..4998a47b 100644 --- a/repo.py +++ b/repo.py @@ -103,7 +103,7 @@ def panel_done(self, picked): return picked_tag = self.results[picked] picked_tag = picked_tag.strip() - if sublime.ok_cancel_dialog("Delete \"%s\" Tag?" % picked_tag, "Delete"): + if sublime.ok_cancel_dialog("Delete \"%s\" Tag?" % picked_tag, "Delete"): self.run_command(['git', 'tag', '-d', picked_tag]) From 121d72939516827b3239d754b56ee6e422d46d21 Mon Sep 17 00:00:00 2001 From: David Lynch Date: Thu, 26 Nov 2015 12:43:22 -0600 Subject: [PATCH 70/80] Add "Add All" command Fixes #448. --- Default.sublime-commands | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Default.sublime-commands b/Default.sublime-commands index b822ade2..b00d713a 100644 --- a/Default.sublime-commands +++ b/Default.sublime-commands @@ -141,6 +141,10 @@ "caption": "Git: Add...", "command": "git_add_choice" } + ,{ + "caption": "Git: Add All", + "command": "git_raw", "args": { "command": "git add -A" } + } ,{ "caption": "Git: Checkout Current File", "command": "git_raw", "args": { "command": "git checkout", "append_current_file": true } From 50bd85f6cb160351cff472f58772102793effd21 Mon Sep 17 00:00:00 2001 From: Jonathan McCall Date: Tue, 8 Dec 2015 12:04:27 -0500 Subject: [PATCH 71/80] Use os.path.exists instead of isfile + isdir --- add.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/add.py b/add.py index c39a7406..71c8e9ce 100644 --- a/add.py +++ b/add.py @@ -27,8 +27,7 @@ def panel_followup(self, picked_status, picked_file, picked_index): else: command = ['git'] picked_file = picked_file.strip('"') - realpath = working_dir + "/" + picked_file - if os.path.isfile(realpath) or os.path.isdir(realpath): + if os.path.exists(working_dir + "/" + picked_file): command += ['add'] else: command += ['rm'] From bcf1170d8a935655cb55d166b0aca663ff0773a6 Mon Sep 17 00:00:00 2001 From: David Lynch Date: Tue, 5 Jan 2016 11:58:03 -0800 Subject: [PATCH 72/80] List stash command --- Default.sublime-commands | 4 ++++ stash.py | 27 +++++++++++++++++++++------ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/Default.sublime-commands b/Default.sublime-commands index b00d713a..8edfc40d 100644 --- a/Default.sublime-commands +++ b/Default.sublime-commands @@ -133,6 +133,10 @@ "caption": "Git: Stash Drop", "command": "git_stash_drop" } + ,{ + "caption": "Git: Stash List", + "command": "git_stash_list" + } ,{ "caption": "Git: Add Current File", "command": "git_raw", "args": { "command": "git add", "append_current_file": true } diff --git a/stash.py b/stash.py index 0fabf2ec..dac4c783 100644 --- a/stash.py +++ b/stash.py @@ -1,9 +1,9 @@ from .git import GitWindowCommand -class GitStashApplyCommand(GitWindowCommand): +class GitStashCommand(GitWindowCommand): may_change_files = True - command_to_run_after_list = 'apply' + command_to_run_after_list = False def run(self): self.run_command(['git', 'stash', 'list'], self.stash_list_done) @@ -27,9 +27,24 @@ def stash_list_panel_done(self, picked=0): return # get the stash ref (e.g. stash@{3}) - self.stash = self.results[picked].split(':')[0] - self.run_command(['git', 'stash', self.command_to_run_after_list, self.stash]) + stash = self.results[picked].split(':')[0] + self.run_command(['git', 'stash'] + self.command_to_run_after_list + [stash], self.handle_command or self.generic_done, stash=stash) + def handle_command(self, result, stash, **kw): + return self.generic_done(result, **kw) -class GitStashDropCommand(GitStashApplyCommand): - command_to_run_after_list = 'drop' + +class GitStashListCommand(GitStashCommand): + may_change_files = False + command_to_run_after_list = ['show', '-p'] + + def handle_command(self, result, stash, **kw): + self.scratch(result, title=stash, syntax="Packages/Diff/Diff.tmLanguage") + + +class GitStashApplyCommand(GitStashCommand): + command_to_run_after_list = ['apply'] + + +class GitStashDropCommand(GitStashCommand): + command_to_run_after_list = ['drop'] From 16faa42c80eeea3caca3a96775ab8d7565f74934 Mon Sep 17 00:00:00 2001 From: David Lynch Date: Tue, 5 Jan 2016 12:11:59 -0800 Subject: [PATCH 73/80] Git flow: be a standard git subcommand --- flow.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/flow.py b/flow.py index b3c5284e..7ea6265e 100644 --- a/flow.py +++ b/flow.py @@ -15,12 +15,12 @@ def run(self): self.get_window().show_input_panel('Enter Feature Name:', '', self.on_done, None, None) def on_done(self, feature_name): - self.run_command(['git-flow', 'feature', 'start', feature_name]) + self.run_command(['git', 'flow', 'feature', 'start', feature_name]) class GitFlowFeatureFinishCommand(GitFlowCommand): def run(self): - self.run_command(['git-flow', 'feature'], self.feature_done) + self.run_command(['git', 'flow', 'feature'], self.feature_done) def feature_done(self, result): self.results = result.rstrip().split('\n') @@ -34,7 +34,7 @@ def panel_done(self, picked): if picked_feature.startswith("*"): picked_feature = picked_feature.strip("*") picked_feature = picked_feature.strip() - self.run_command(['git-flow', 'feature', 'finish', picked_feature]) + self.run_command(['git', 'flow', 'feature', 'finish', picked_feature]) class GitFlowReleaseStartCommand(GitFlowCommand): @@ -42,12 +42,12 @@ def run(self): self.get_window().show_input_panel('Enter Version Number:', '', self.on_done, None, None) def on_done(self, release_name): - self.run_command(['git-flow', 'release', 'start', release_name]) + self.run_command(['git', 'flow', 'release', 'start', release_name]) class GitFlowReleaseFinishCommand(GitFlowCommand): def run(self): - self.run_command(['git-flow', 'release'], self.release_done) + self.run_command(['git', 'flow', 'release'], self.release_done) def release_done(self, result): self.results = result.rstrip().split('\n') @@ -61,7 +61,7 @@ def panel_done(self, picked): if picked_release.startswith("*"): picked_release = picked_release.strip("*") picked_release = picked_release.strip() - self.run_command(['git-flow', 'release', 'finish', picked_release]) + self.run_command(['git', 'flow', 'release', 'finish', picked_release]) class GitFlowHotfixStartCommand(GitFlowCommand): @@ -69,12 +69,12 @@ def run(self): self.get_window().show_input_panel('Enter hotfix name:', '', self.on_done, None, None) def on_done(self, hotfix_name): - self.run_command(['git-flow', 'hotfix', 'start', hotfix_name]) + self.run_command(['git', 'flow', 'hotfix', 'start', hotfix_name]) class GitFlowHotfixFinishCommand(GitFlowCommand): def run(self): - self.run_command(['git-flow', 'hotfix'], self.hotfix_done) + self.run_command(['git', 'flow', 'hotfix'], self.hotfix_done) def hotfix_done(self, result): self.results = result.rstrip().split('\n') @@ -88,4 +88,4 @@ def panel_done(self, picked): if picked_hotfix.startswith("*"): picked_hotfix = picked_hotfix.strip("*") picked_hotfix = picked_hotfix.strip() - self.run_command(['git-flow', 'hotfix', 'finish', picked_hotfix]) + self.run_command(['git', 'flow', 'hotfix', 'finish', picked_hotfix]) From ece105ea6c43fc437fd71998eb0048212b4cc7be Mon Sep 17 00:00:00 2001 From: David Lynch Date: Tue, 5 Jan 2016 12:15:32 -0800 Subject: [PATCH 74/80] git_flow_command should still work --- git.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/git.py b/git.py index 72a0e503..6fddb297 100644 --- a/git.py +++ b/git.py @@ -237,8 +237,9 @@ def run_command(self, command, callback=None, show_status=True, command[0] = GIT if command[0] == 'gitk' and s.get('gitk_command'): command[0] = s.get('gitk_command') - if command[0] == 'git-flow' and s.get('git_flow_command'): + if command[0] == 'git' and command[1] == 'flow' and s.get('git_flow_command'): command[0] = s.get('git_flow_command') + del(command[1]) if not callback: callback = self.generic_done From e83676386574847506fef7eb3df7a0da6605dac4 Mon Sep 17 00:00:00 2001 From: David Lynch Date: Tue, 5 Jan 2016 15:16:49 -0800 Subject: [PATCH 75/80] =?UTF-8?q?Use=20=E2=9C=93=20instead=20of=20sqrt=20s?= =?UTF-8?q?ymbol?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Git.sublime-settings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Git.sublime-settings b/Git.sublime-settings index be4bca03..361ec60f 100644 --- a/Git.sublime-settings +++ b/Git.sublime-settings @@ -33,7 +33,7 @@ ,"statusbar_branch": true // Symbols for quick git status in status bar ,"statusbar_status": true - ,"statusbar_status_symbols" : {"modified": "≠", "added": "+", "deleted": "×", "untracked": "?", "conflicts": "‼", "renamed":"R", "copied":"C", "clean": "√", "separator": " "} + ,"statusbar_status_symbols" : {"modified": "≠", "added": "+", "deleted": "×", "untracked": "?", "conflicts": "‼", "renamed":"R", "copied":"C", "clean": "✓", "separator": " "} // e.g. "Packages/Git/syntax/Git Commit Message.tmLanguage" ,"diff_syntax": "Packages/Diff/Diff.tmLanguage" From dfdccee0d1550035abb8cb3e0abe1f5a0108114d Mon Sep 17 00:00:00 2001 From: Jonathan McCall Date: Wed, 6 Jan 2016 16:44:37 -0500 Subject: [PATCH 76/80] Always update the branch status in status bar Originally when adding files the 'index:' and 'working:' values in the status bar would be updated reflecting the current branch status. However when using reset the changes to the branch would not be immediately reflected in the status bar. The branch status would then be updated in the status bar after saving the file. This change makes it so that after any git command is run the branch status is updated in the status bar. --- git.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/git.py b/git.py index 59ab813d..9cade507 100644 --- a/git.py +++ b/git.py @@ -436,6 +436,9 @@ def run(self, **args): elif show_in == 'suppress': self.run_command(command_split, self.do_nothing) + view = self.active_view() + view.run_command('git_branch_status') + def show_in_quick_panel(self, result): self.results = list(result.rstrip().split('\n')) if len(self.results): From 9d2667076ec0152ae96117aca718b7c716fdbad5 Mon Sep 17 00:00:00 2001 From: David Lynch Date: Wed, 6 Jan 2016 14:39:48 -0800 Subject: [PATCH 77/80] Use async commands for statusbar events --- statusbar.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/statusbar.py b/statusbar.py index 0e90693d..1eec3aa5 100644 --- a/statusbar.py +++ b/statusbar.py @@ -6,10 +6,10 @@ class GitBranchStatusListener(sublime_plugin.EventListener): - def on_activated(self, view): + def on_activated_async(self, view): view.run_command("git_branch_status") - def on_post_save(self, view): + def on_post_save_async(self, view): view.run_command("git_branch_status") From f6cde852e893524c3a90e1b8057070f38d576b78 Mon Sep 17 00:00:00 2001 From: David Lynch Date: Fri, 8 Jan 2016 10:41:43 -0800 Subject: [PATCH 78/80] Move everything into a submodule --- README.markdown | 8 +- commands.py | 17 ++++ git.py => git/__init__.py | 136 +++---------------------------- add.py => git/add.py | 2 +- annotate.py => git/annotate.py | 2 +- commit.py => git/commit.py | 2 +- git/core.py | 125 ++++++++++++++++++++++++++++ diff.py => git/diff.py | 2 +- flow.py => git/flow.py | 2 +- history.py => git/history.py | 2 +- repo.py => git/repo.py | 2 +- stash.py => git/stash.py | 2 +- status.py => git/status.py | 2 +- statusbar.py => git/statusbar.py | 2 +- 14 files changed, 166 insertions(+), 140 deletions(-) create mode 100644 commands.py rename git.py => git/__init__.py (75%) rename add.py => git/add.py (98%) rename annotate.py => git/annotate.py (99%) rename commit.py => git/commit.py (98%) create mode 100644 git/core.py rename diff.py => git/diff.py (98%) rename flow.py => git/flow.py (98%) rename history.py => git/history.py (99%) rename repo.py => git/repo.py (99%) rename stash.py => git/stash.py (97%) rename status.py => git/status.py (98%) rename statusbar.py => git/statusbar.py (98%) diff --git a/README.markdown b/README.markdown index 73eba1fa..62e6d80e 100644 --- a/README.markdown +++ b/README.markdown @@ -1,8 +1,8 @@ -# Sublime Text 3 plugin: git +# Sublime Text plugin: git Git integration: it's pretty handy. Who knew, right? -For more information about what's supported, and how to install this, [check the wiki](https://github.com/kemayo/sublime-text-2-git/wiki). +For more information about what's supported, and how to install this, [check the wiki](https://github.com/kemayo/sublime-text-git/wiki). ## Install @@ -10,7 +10,7 @@ For more information about what's supported, and how to install this, [check the The easiest way to install this is with [Package Control](http://wbond.net/sublime\_packages/package\_control). - * If you just went and installed Package Control, you probably need to restart Sublime Text 2 before doing this next bit. + * If you just went and installed Package Control, you probably need to restart Sublime Text before doing this next bit. * Bring up the Command Palette (Command+Shift+p on OS X, Control+Shift+p on Linux/Windows). * Select "Package Control: Install Package" (it'll take a few seconds) * Select Git when the list appears. @@ -19,4 +19,4 @@ Package Control will automatically keep Git up to date with the latest version. ### The rest -If you don't want to use Package Control, [check the wiki](https://github.com/kemayo/sublime-text-2-git/wiki) for other installation methods on various platforms. +If you don't want to use Package Control, [check the wiki](https://github.com/kemayo/sublime-text-git/wiki) for other installation methods on various platforms. diff --git a/commands.py b/commands.py new file mode 100644 index 00000000..02090ad0 --- /dev/null +++ b/commands.py @@ -0,0 +1,17 @@ +"""This module collates the Git commands from the submodule + +...it's a python 2 / 3 compatibility workaround, mostly. +""" + +from git.core import * + +from git.add import * +from git.annotate import * +from git.commit import * +from git.diff import * +from git.flow import * +from git.history import * +from git.repo import * +from git.stash import * +from git.status import * +from git.statusbar import * diff --git a/git.py b/git/__init__.py similarity index 75% rename from git.py rename to git/__init__.py index d1786b68..3f28954a 100644 --- a/git.py +++ b/git/__init__.py @@ -1,3 +1,6 @@ +from __future__ import print_function +from __future__ import absolute_import + import os import sublime import sublime_plugin @@ -7,17 +10,20 @@ import os.path import time -# In a complete inversion from ST2, in ST3 when a plugin is loaded we -# actually can trust __file__. -# Goal is to get: "Packages/Git", allowing for people who rename things git_root_cache = {} +# Goal is to get: "Packages/Git", allowing for people who rename things def find_plugin_directory(f): dirname = os.path.split(os.path.dirname(f))[-1] return "Packages/" + dirname.replace(".sublime-package", "") -PLUGIN_DIRECTORY = find_plugin_directory(__file__) +if __file__.startswith('./'): + PLUGIN_DIRECTORY = os.getcwd().replace(os.path.normpath(os.path.join(os.getcwd(), '..', '..')) + os.path.sep, '').replace(os.path.sep, '/') +else: + # In a complete inversion from ST2, in ST3 when a plugin is loaded we + # actually can trust __file__. + PLUGIN_DIRECTORY = find_plugin_directory(__file__) def main_thread(callback, *args, **kwargs): @@ -205,14 +211,6 @@ def run(self): main_thread(callback, output, **self.kwargs) -class GitScratchOutputCommand(sublime_plugin.TextCommand): - def run(self, edit, output='', output_file=None, clear=False): - if clear: - region = sublime.Region(0, self.view.size()) - self.view.erase(edit, region) - self.view.insert(edit, 0, output) - - # A base for all commands class GitCommand(object): may_change_files = False @@ -384,117 +382,3 @@ def get_window(self): # the case of the quick panel. # So, this is not necessarily ideal, but it does work. return self.view.window() or sublime.active_window() - - -# A few miscellaneous commands - - -class GitCustomCommand(GitWindowCommand): - may_change_files = True - - def run(self): - self.get_window().show_input_panel("Git command", "", - self.on_input, None, None) - - def on_input(self, command): - command = str(command) # avoiding unicode - if command.strip() == "": - self.panel("No git command provided") - return - import shlex - command_splitted = ['git'] + shlex.split(command) - print(command_splitted) - self.run_command(command_splitted) - - -class GitRawCommand(GitWindowCommand): - may_change_files = True - - def run(self, **args): - self.command = str(args.get('command', '')) - show_in = str(args.get('show_in', 'pane_below')) - - if self.command.strip() == "": - self.panel("No git command provided") - return - import shlex - command_split = shlex.split(self.command) - - if args.get('append_current_file', False) and self._active_file_name(): - command_split.extend(('--', self._active_file_name())) - - print(command_split) - - self.may_change_files = bool(args.get('may_change_files', True)) - - if show_in == 'pane_below': - self.run_command(command_split) - elif show_in == 'quick_panel': - self.run_command(command_split, self.show_in_quick_panel) - elif show_in == 'new_tab': - self.run_command(command_split, self.show_in_new_tab) - elif show_in == 'suppress': - self.run_command(command_split, self.do_nothing) - - view = self.active_view() - view.run_command('git_branch_status') - - def show_in_quick_panel(self, result): - self.results = list(result.rstrip().split('\n')) - if len(self.results): - self.quick_panel(self.results, - self.do_nothing, sublime.MONOSPACE_FONT) - else: - sublime.status_message("Nothing to show") - - def do_nothing(self, picked): - return - - def show_in_new_tab(self, result): - msg = self.window.new_file() - msg.set_scratch(True) - msg.set_name(self.command) - self._output_to_view(msg, result) - msg.sel().clear() - msg.sel().add(sublime.Region(0, 0)) - - -class GitGuiCommand(GitTextCommand): - def run(self, edit): - command = ['git', 'gui'] - self.run_command(command) - - -class GitGitkCommand(GitTextCommand): - def run(self, edit): - command = ['gitk'] - self.run_command(command) - - -class GitUpdateIgnoreCommand(sublime_plugin.TextCommand): - def run(self, edit): - data = self.view.window().project_data() - project_file_name = self.view.window().project_file_name() - for folder in data['folders']: - path = folder['path'] - if project_file_name: - path = os.path.join(os.path.dirname(project_file_name), path) - gitignore = os.path.join(path, ".gitignore") - print("gitignore path", gitignore) - if (os.path.exists(gitignore)): - with open(gitignore) as gitignore_file: - if "folder_exclude_patterns" not in folder: - folder["folder_exclude_patterns"] = [] - if "file_exclude_patterns" not in folder: - folder["file_exclude_patterns"] = [] - for pattern in gitignore_file: - pattern = pattern.strip() - if len(pattern) == 0 or pattern[0] == '#': - continue - elif os.path.isdir(os.path.join(path, pattern)): - if pattern not in folder["folder_exclude_patterns"]: - folder["folder_exclude_patterns"].append(pattern) - else: - if pattern not in folder["file_exclude_patterns"]: - folder["file_exclude_patterns"].append(pattern) - self.view.window().set_project_data(data) diff --git a/add.py b/git/add.py similarity index 98% rename from add.py rename to git/add.py index 59bbb381..5619082c 100644 --- a/add.py +++ b/git/add.py @@ -2,7 +2,7 @@ import re import sublime -from .git import GitTextCommand, GitWindowCommand, git_root +from . import GitTextCommand, GitWindowCommand, git_root from .status import GitStatusCommand diff --git a/annotate.py b/git/annotate.py similarity index 99% rename from annotate.py rename to git/annotate.py index 45357067..2cf147ac 100644 --- a/annotate.py +++ b/git/annotate.py @@ -4,7 +4,7 @@ import sublime import sublime_plugin -from .git import git_root, GitTextCommand +from . import git_root, GitTextCommand class GitClearAnnotationCommand(GitTextCommand): diff --git a/commit.py b/git/commit.py similarity index 98% rename from commit.py rename to git/commit.py index 1fbc5dbb..f8dbc504 100644 --- a/commit.py +++ b/git/commit.py @@ -4,7 +4,7 @@ import sublime import sublime_plugin -from .git import GitTextCommand, GitWindowCommand, plugin_file, view_contents, _make_text_safeish +from . import GitTextCommand, GitWindowCommand, plugin_file, view_contents, _make_text_safeish from .add import GitAddSelectedHunkCommand history = [] diff --git a/git/core.py b/git/core.py new file mode 100644 index 00000000..34dcde9a --- /dev/null +++ b/git/core.py @@ -0,0 +1,125 @@ +import os +import sublime +import sublime_plugin + +from . import GitWindowCommand, GitTextCommand + + +class GitCustomCommand(GitWindowCommand): + may_change_files = True + + def run(self): + self.get_window().show_input_panel("Git command", "", + self.on_input, None, None) + + def on_input(self, command): + command = str(command) # avoiding unicode + if command.strip() == "": + self.panel("No git command provided") + return + import shlex + command_splitted = ['git'] + shlex.split(command) + print(command_splitted) + self.run_command(command_splitted) + + +class GitRawCommand(GitWindowCommand): + may_change_files = True + + def run(self, **args): + self.command = str(args.get('command', '')) + show_in = str(args.get('show_in', 'pane_below')) + + if self.command.strip() == "": + self.panel("No git command provided") + return + import shlex + command_split = shlex.split(self.command) + + if args.get('append_current_file', False) and self._active_file_name(): + command_split.extend(('--', self._active_file_name())) + + print(command_split) + + self.may_change_files = bool(args.get('may_change_files', True)) + + if show_in == 'pane_below': + self.run_command(command_split) + elif show_in == 'quick_panel': + self.run_command(command_split, self.show_in_quick_panel) + elif show_in == 'new_tab': + self.run_command(command_split, self.show_in_new_tab) + elif show_in == 'suppress': + self.run_command(command_split, self.do_nothing) + + view = self.active_view() + view.run_command('git_branch_status') + + def show_in_quick_panel(self, result): + self.results = list(result.rstrip().split('\n')) + if len(self.results): + self.quick_panel(self.results, + self.do_nothing, sublime.MONOSPACE_FONT) + else: + sublime.status_message("Nothing to show") + + def do_nothing(self, picked): + return + + def show_in_new_tab(self, result): + msg = self.window.new_file() + msg.set_scratch(True) + msg.set_name(self.command) + self._output_to_view(msg, result) + msg.sel().clear() + msg.sel().add(sublime.Region(0, 0)) + + +class GitGuiCommand(GitTextCommand): + def run(self, edit): + command = ['git', 'gui'] + self.run_command(command) + + +class GitGitkCommand(GitTextCommand): + def run(self, edit): + command = ['gitk'] + self.run_command(command) + + +class GitUpdateIgnoreCommand(sublime_plugin.TextCommand): + def run(self, edit): + data = self.view.window().project_data() + project_file_name = self.view.window().project_file_name() + for folder in data['folders']: + path = folder['path'] + if project_file_name: + path = os.path.join(os.path.dirname(project_file_name), path) + gitignore = os.path.join(path, ".gitignore") + print("gitignore path", gitignore) + if (os.path.exists(gitignore)): + with open(gitignore) as gitignore_file: + if "folder_exclude_patterns" not in folder: + folder["folder_exclude_patterns"] = [] + if "file_exclude_patterns" not in folder: + folder["file_exclude_patterns"] = [] + for pattern in gitignore_file: + pattern = pattern.strip() + if len(pattern) == 0 or pattern[0] == '#': + continue + elif os.path.isdir(os.path.join(path, pattern)): + if pattern not in folder["folder_exclude_patterns"]: + folder["folder_exclude_patterns"].append(pattern) + else: + if pattern not in folder["file_exclude_patterns"]: + folder["file_exclude_patterns"].append(pattern) + self.view.window().set_project_data(data) + + +# called by GitWindowCommand +class GitScratchOutputCommand(sublime_plugin.TextCommand): + def run(self, edit, output='', output_file=None, clear=False): + if clear: + region = sublime.Region(0, self.view.size()) + self.view.erase(edit, region) + self.view.insert(edit, 0, output) diff --git a/diff.py b/git/diff.py similarity index 98% rename from diff.py rename to git/diff.py index 40a2c1a0..11a4c3ca 100644 --- a/diff.py +++ b/git/diff.py @@ -2,7 +2,7 @@ import sublime_plugin import os import re -from .git import git_root, GitTextCommand, GitWindowCommand, do_when, goto_xy +from . import git_root, GitTextCommand, GitWindowCommand, do_when, goto_xy class GitDiff (object): diff --git a/flow.py b/git/flow.py similarity index 98% rename from flow.py rename to git/flow.py index 7ea6265e..51317663 100644 --- a/flow.py +++ b/git/flow.py @@ -1,5 +1,5 @@ import sublime -from .git import GitWindowCommand +from . import GitWindowCommand class GitFlowCommand(GitWindowCommand): diff --git a/history.py b/git/history.py similarity index 99% rename from history.py rename to git/history.py index 46d6aef0..c1c5e163 100644 --- a/history.py +++ b/git/history.py @@ -3,7 +3,7 @@ import re import sublime -from .git import GitTextCommand, GitWindowCommand, plugin_file +from . import GitTextCommand, GitWindowCommand, plugin_file class GitBlameCommand(GitTextCommand): diff --git a/repo.py b/git/repo.py similarity index 99% rename from repo.py rename to git/repo.py index 4998a47b..bf1b277e 100644 --- a/repo.py +++ b/git/repo.py @@ -1,7 +1,7 @@ import os import sublime -from .git import GitWindowCommand, git_root_exist +from . import GitWindowCommand, git_root_exist class GitInit(object): diff --git a/stash.py b/git/stash.py similarity index 97% rename from stash.py rename to git/stash.py index dac4c783..a4cf215e 100644 --- a/stash.py +++ b/git/stash.py @@ -1,4 +1,4 @@ -from .git import GitWindowCommand +from . import GitWindowCommand class GitStashCommand(GitWindowCommand): diff --git a/status.py b/git/status.py similarity index 98% rename from status.py rename to git/status.py index 64360b4f..fdec545b 100644 --- a/status.py +++ b/git/status.py @@ -2,7 +2,7 @@ import re import sublime -from .git import GitWindowCommand, git_root +from . import GitWindowCommand, git_root class GitStatusCommand(GitWindowCommand): diff --git a/statusbar.py b/git/statusbar.py similarity index 98% rename from statusbar.py rename to git/statusbar.py index 1eec3aa5..520666c4 100644 --- a/statusbar.py +++ b/git/statusbar.py @@ -2,7 +2,7 @@ import sublime import sublime_plugin -from .git import GitTextCommand +from . import GitTextCommand class GitBranchStatusListener(sublime_plugin.EventListener): From c4adee0fe6b5d0a9515f51fd3b0b18965d38eb0f Mon Sep 17 00:00:00 2001 From: David Lynch Date: Fri, 8 Jan 2016 10:44:15 -0800 Subject: [PATCH 79/80] ST3 plugin directory --- git/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git/__init__.py b/git/__init__.py index 3f28954a..a381808c 100644 --- a/git/__init__.py +++ b/git/__init__.py @@ -23,7 +23,7 @@ def find_plugin_directory(f): else: # In a complete inversion from ST2, in ST3 when a plugin is loaded we # actually can trust __file__. - PLUGIN_DIRECTORY = find_plugin_directory(__file__) + PLUGIN_DIRECTORY = find_plugin_directory(os.path.normpath(os.path.join(os.path.dirname(__file__), '..'))) def main_thread(callback, *args, **kwargs): From 8dd236aea3edee2a35ca41b2c5540bcdabdb255a Mon Sep 17 00:00:00 2001 From: David Lynch Date: Fri, 8 Jan 2016 12:07:10 -0800 Subject: [PATCH 80/80] Better future and imports --- commands.py | 40 +++++++++++++++++++++++++++++----------- git/__init__.py | 3 +-- git/add.py | 2 ++ git/annotate.py | 2 ++ git/commit.py | 2 ++ git/core.py | 2 ++ git/diff.py | 2 ++ git/flow.py | 2 ++ git/history.py | 4 +++- git/repo.py | 2 ++ git/stash.py | 2 ++ git/status.py | 2 ++ git/statusbar.py | 2 ++ 13 files changed, 53 insertions(+), 14 deletions(-) diff --git a/commands.py b/commands.py index 02090ad0..d4b5f5de 100644 --- a/commands.py +++ b/commands.py @@ -1,17 +1,35 @@ +from __future__ import absolute_import, unicode_literals, print_function, division + """This module collates the Git commands from the submodule ...it's a python 2 / 3 compatibility workaround, mostly. """ -from git.core import * +try: + # Python 3 + from .git.core import * + + from .git.add import * + from .git.annotate import * + from .git.commit import * + from .git.diff import * + from .git.flow import * + from .git.history import * + from .git.repo import * + from .git.stash import * + from .git.status import * + from .git.statusbar import * +except (ImportError, ValueError): + # Python 2 + from git.core import * -from git.add import * -from git.annotate import * -from git.commit import * -from git.diff import * -from git.flow import * -from git.history import * -from git.repo import * -from git.stash import * -from git.status import * -from git.statusbar import * + from git.add import * + from git.annotate import * + from git.commit import * + from git.diff import * + from git.flow import * + from git.history import * + from git.repo import * + from git.stash import * + from git.status import * + from git.statusbar import * diff --git a/git/__init__.py b/git/__init__.py index a381808c..210deb29 100644 --- a/git/__init__.py +++ b/git/__init__.py @@ -1,5 +1,4 @@ -from __future__ import print_function -from __future__ import absolute_import +from __future__ import absolute_import, unicode_literals, print_function, division import os import sublime diff --git a/git/add.py b/git/add.py index 5619082c..502ec61c 100644 --- a/git/add.py +++ b/git/add.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import, unicode_literals, print_function, division + import os import re diff --git a/git/annotate.py b/git/annotate.py index 2cf147ac..686fc432 100644 --- a/git/annotate.py +++ b/git/annotate.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import, unicode_literals, print_function, division + import tempfile import re import os diff --git a/git/commit.py b/git/commit.py index f8dbc504..7fc57266 100644 --- a/git/commit.py +++ b/git/commit.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import, unicode_literals, print_function, division + import functools import tempfile import os diff --git a/git/core.py b/git/core.py index 34dcde9a..f25354f3 100644 --- a/git/core.py +++ b/git/core.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import, unicode_literals, print_function, division + import os import sublime import sublime_plugin diff --git a/git/diff.py b/git/diff.py index 11a4c3ca..5abdc390 100644 --- a/git/diff.py +++ b/git/diff.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import, unicode_literals, print_function, division + import sublime import sublime_plugin import os diff --git a/git/flow.py b/git/flow.py index 51317663..68a4115a 100644 --- a/git/flow.py +++ b/git/flow.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import, unicode_literals, print_function, division + import sublime from . import GitWindowCommand diff --git a/git/history.py b/git/history.py index c1c5e163..83e9acd5 100644 --- a/git/history.py +++ b/git/history.py @@ -1,8 +1,10 @@ -import sublime_plugin +from __future__ import absolute_import, unicode_literals, print_function, division + import functools import re import sublime +import sublime_plugin from . import GitTextCommand, GitWindowCommand, plugin_file diff --git a/git/repo.py b/git/repo.py index bf1b277e..9b1e5f6a 100644 --- a/git/repo.py +++ b/git/repo.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import, unicode_literals, print_function, division + import os import sublime diff --git a/git/stash.py b/git/stash.py index a4cf215e..c46e8af5 100644 --- a/git/stash.py +++ b/git/stash.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import, unicode_literals, print_function, division + from . import GitWindowCommand diff --git a/git/status.py b/git/status.py index fdec545b..e0a010f7 100644 --- a/git/status.py +++ b/git/status.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import, unicode_literals, print_function, division + import os import re diff --git a/git/statusbar.py b/git/statusbar.py index 520666c4..d5b9f26d 100644 --- a/git/statusbar.py +++ b/git/statusbar.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import, unicode_literals, print_function, division + import re import sublime