Skip to content


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP


Fix windows annotations #158

wants to merge 3 commits into from

4 participants

Niklas Hambüchen Sheldon Els hex Tomasz Wesołowski
Niklas Hambüchen

Windows doesn't allow other programs to read files created with tempfile.NamedTemporaryFile(delete=True). That's why we get something like:

Exception in thread Thread-173:
Traceback (most recent call last):
  File ".\", line 532, in __bootstrap_inner
  File ".\", line 96, in run
  File ".\", line 701, in communicate
  File ".\", line 911, in _communicate
IOError: [Errno 32] Broken pipe

Also, diff is usually not installed on windows. One of these commits tells the user about this.

Side note: It would be cool if the checks for the existence of external programs could be done in some initialisation part of the plugin, like in the detection phase of SublimeLinter.

Sheldon Els

Thanks for the patch, I agree about the detection too it would be nice. If there's any other windows users that can confirm a test of this as working I'll get it merged up.

Sheldon Els

@nh2 I did a little reading through this and it seems ok to me. the intention is to throw a nicer error on not being able to run annotations via diff right? if you say you've done a thorough test I'll get it merged up anyway, as it seems we don't have anyone testing separately. can always revert if there's any major problems.

Niklas Hambüchen

the intention is to throw a nicer error on not being able to run annotations via diff right?


I tested it on a Windows 7 machine and would prefer if others could confirm that it works for them, but if we don't have anyone trying it, we should probably merge it and see if people complain.


I tried this fix on Windows 7, but the annotations don't appear. I got rid of the erros, but still no live annotations.

Tomasz Wesołowski

I confirm that this fix has worked for me on Windows 7 64bit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jul 11, 2012
  1. Niklas Hambüchen
Commits on Jul 12, 2012
  1. Niklas Hambüchen
  2. Niklas Hambüchen

    Tell which "git binary" was not found.

    nh2 authored
    Theoretically, this could be any program started via CommandThread.
This page is out of date. Refresh to see the latest.
Showing with 33 additions and 4 deletions.
  1. +33 −4
@@ -79,6 +79,20 @@ def _make_text_safeish(text, fallback_encoding):
return unitext
+def cmd_exists(cmd_args, shell=True):
+ """
+ Tells if the command given in `cmd_args` returns exit code 0 when executed.
+ If `shell==False`, it takes a subprocess argument list instead of a shell invocation string.
+ Examples:
+ cmd_exists('diff --version')
+ cmd_exists(['diff.exe', '--version'], shell=False)
+ """
+ try:
+ return 0 == subprocess.check_call(cmd_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=shell)
+ except subprocess.CalledProcessError:
+ return False
class CommandThread(threading.Thread):
def __init__(self, command, on_done, working_dir="", fallback_encoding="", **kwargs):
@@ -119,7 +133,7 @@ def run(self):
main_thread(self.on_done, e.returncode)
except OSError, 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'])
+ main_thread(sublime.error_message, "Git binary (%s) could not be found in PATH\n\nConsider using the git_command setting for the Git plugin\n\nPATH is: %s" % (self.command[0], os.environ['PATH']))
raise e
@@ -904,15 +918,30 @@ def run(self, view):
if hasattr(self, "tmp"):
- self.tmp = tempfile.NamedTemporaryFile()
+ # Don't set autodelete the file on windows, because if we set it, diff doesn't
+ # have permission to read the file (
+ # As they are not deleted, we are leaking these temporary files on Windows.
+ self.tmp = tempfile.NamedTemporaryFile(delete=( != 'nt'))
self.active_view().settings().set('live_git_annotations', True)
root = git_root(self.get_working_dir())
- repo_file = os.path.relpath(self.view.file_name(), root)
+ repo_file = '/'.join(os.path.relpath(self.view.file_name(), root).split(os.path.sep))
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")
- self.run_command(['diff', '-u',, '-'], stdin=all_text, no_save=True, show_status=False, callback=self.parse_diff)
+ # Only diff when diff is available.
+ if not self.detect_diff_cached():
+ main_thread(sublime.error_message, "diff could not be found in PATH\n\nPATH is: %s" % os.environ['PATH'])
+ else:
+ self.run_command(['diff', '-u',, '-'], stdin=all_text, no_save=True, show_status=False, callback=self.parse_diff)
+ # Checks if diff exists. Only executed on first invocation, checked otherwise.
+ # The caching is necessary as this is called on every keystroke if live annotations are on.
+ # TODO It would be nicer to check if `git` and `diff` exist when the plugin is loaded.
+ def detect_diff_cached(self):
+ if not hasattr(self, 'diff_command_available'):
+ self.diff_command_available = cmd_exists('diff --version')
+ return self.diff_command_available
# This is where the magic happens. At the moment, only one chunk format is supported. While
# the unified diff format theoritaclly supports more, I don't think git diff creates them.
Something went wrong with that request. Please try again.