Changeset 2fd8c418d9d8…
Parent 9c6ba0878420…
by
Changes to 5 files · Browse files at 2fd8c418d9d8 Showing diff from parent 9c6ba0878420 Diff from another changeset...
@@ -13,7 +13,7 @@
from mercurial import demandimport; demandimport.enable()
from mercurial import ui
-from tortoise.thgutil import get_prog_root
+from tortoise.thgutil import get_prog_root, find_root
# always use hg exe installed with TortoiseHg
thgdir = get_prog_root()
@@ -103,6 +103,11 @@ if 'root' in option:
cmdline.append('--repository')
cmdline.append(option['root'])
+ elif 'cwd' in option:
+ root = find_root(option['cwd'])
+ option['root'] = root
+ cmdline.append('--repository')
+ cmdline.append(option['root'])
cmdline.extend(args)
cmdline.extend(option['files'])
option['cmdline'] = cmdline
|
|
|
@@ -0,0 +1,286 @@ + import os
+import win32api
+import win32con
+from win32com.shell import shell, shellcon
+import _winreg
+from mercurial import hg, cmdutil, util
+from mercurial import repo as _repo
+import thgutil
+import sys
+import win32serviceutil
+import win32service
+import win32event
+import win32pipe
+import win32file
+import pywintypes
+import winerror
+
+PIPENAME = "\\\\.\\pipe\\PyPipeService"
+PIPEBUFSIZE = 4096
+
+# FIXME: quick workaround traceback caused by missing "closed"
+# attribute in win32trace.
+from mercurial import ui
+def write_err(self, *args):
+ for a in args:
+ sys.stderr.write(str(a))
+ui.ui.write_err = write_err
+
+# file/directory status
+UNCHANGED = "unchanged"
+ADDED = "added"
+MODIFIED = "modified"
+UNKNOWN = "unknown"
+NOT_IN_REPO = "n/a"
+
+# file status cache
+CACHE_TIMEOUT = 5000
+overlay_cache = {}
+cache_tick_count = 0
+cache_root = None
+cache_pdir = None
+
+# some misc constants
+S_OK = 0
+S_FALSE = 1
+
+def add_dirs(list):
+ dirs = set()
+ for f in list:
+ dir = os.path.dirname(f)
+ if dir in dirs:
+ continue
+ while dir:
+ dirs.add(dir)
+ dir = os.path.dirname(dir)
+ list.extend(dirs)
+
+def get_hg_state(upath):
+ """
+ Get the state of a given path in source control.
+ """
+ global overlay_cache, cache_tick_count
+ global cache_root, cache_pdir
+
+ #print "called: _get_state(%s)" % path
+ tc = win32api.GetTickCount()
+
+ try:
+ # handle some Asian charsets
+ path = upath.encode('mbcs')
+ except:
+ path = upath
+
+ print "get_hg_state: path =", path
+ if not path:
+ return UNKNOWN
+
+ # check if path is cached
+ pdir = os.path.dirname(path)
+ if cache_pdir == pdir and overlay_cache:
+ if tc - cache_tick_count < CACHE_TIMEOUT:
+ try:
+ status = overlay_cache[path]
+ except:
+ status = UNKNOWN
+ print "%s: %s (cached)" % (path, status)
+ return status
+ else:
+ print "Timed out!!"
+ overlay_cache.clear()
+
+ # path is a drive
+ if path.endswith(":\\"):
+ overlay_cache[path] = UNKNOWN
+ return NOT_IN_REPO
+
+ # open repo
+ if cache_pdir == pdir:
+ root = cache_root
+ else:
+ print "find new root"
+ cache_pdir = pdir
+ cache_root = root = thgutil.find_root(pdir)
+ print "_get_state: root = ", root
+ if root is None:
+ print "_get_state: not in repo"
+ overlay_cache = {None : None}
+ cache_tick_count = win32api.GetTickCount()
+ return NOT_IN_REPO
+
+ try:
+ tc1 = win32api.GetTickCount()
+ repo = hg.repository(ui.ui(), path=root)
+ print "hg.repository() took %d ticks" % (win32api.GetTickCount() - tc1)
+
+ # check if to display overlay icons in this repo
+ global_opts = ui.ui().configlist('tortoisehg', 'overlayicons', [])
+ repo_opts = repo.ui.configlist('tortoisehg', 'overlayicons', [])
+
+ print "%s: global overlayicons = " % path, global_opts
+ print "%s: repo overlayicons = " % path, repo_opts
+ is_netdrive = thgutil.netdrive_status(path) is not None
+ if (is_netdrive and 'localdisks' in global_opts) \
+ or 'False' in repo_opts:
+ print "%s: overlayicons disabled" % path
+ overlay_cache = {None : None}
+ cache_tick_count = win32api.GetTickCount()
+ return NOT_IN_REPO
+ except _repo.RepoError:
+ # We aren't in a working tree
+ print "%s: not in repo" % dir
+ overlay_cache[path] = UNKNOWN
+ return NOT_IN_REPO
+
+ # get file status
+ tc1 = win32api.GetTickCount()
+
+ modified, added, removed, deleted = [], [], [], []
+ unknown, ignored, clean = [], [], []
+ files = []
+ try:
+ matcher = cmdutil.match(repo, [pdir])
+ modified, added, removed, deleted, unknown, ignored, clean = \
+ repo.status(match=matcher, ignored=True,
+ clean=True, unknown=True)
+
+ # add directory status to list
+ for grp in (clean,modified,added,removed,deleted,ignored,unknown):
+ add_dirs(grp)
+ except util.Abort, inst:
+ print "abort: %s" % inst
+ print "treat as unknown : %s" % path
+ return UNKNOWN
+
+ print "status() took %d ticks" % (win32api.GetTickCount() - tc1)
+
+ # cached file info
+ tc = win32api.GetTickCount()
+ overlay_cache = {}
+ for grp, st in (
+ (ignored, UNKNOWN),
+ (unknown, UNKNOWN),
+ (clean, UNCHANGED),
+ (added, ADDED),
+ (removed, MODIFIED),
+ (deleted, MODIFIED),
+ (modified, MODIFIED)):
+ for f in grp:
+ fpath = os.path.join(repo.root, os.path.normpath(f))
+ overlay_cache[fpath] = st
+
+ if path in overlay_cache:
+ status = overlay_cache[path]
+ else:
+ status = overlay_cache[path] = UNKNOWN
+ print "%s: %s" % (path, status)
+ cache_tick_count = win32api.GetTickCount()
+ return status
+
+class PipeServer:
+ def __init__(self):
+ # Create an event which we will use to wait on.
+ # The "service stop" request will set this event.
+ self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
+
+ # We need to use overlapped IO for this, so we dont block when
+ # waiting for a client to connect. This is the only effective way
+ # to handle either a client connection, or a service stop request.
+ self.overlapped = pywintypes.OVERLAPPED()
+
+ # And create an event to be used in the OVERLAPPED object.
+ self.overlapped.hEvent = win32event.CreateEvent(None,0,0,None)
+
+ def SvcDoRun(self):
+ # We create our named pipe.
+ pipeName = PIPENAME
+ openMode = win32pipe.PIPE_ACCESS_DUPLEX | win32file.FILE_FLAG_OVERLAPPED
+ pipeMode = win32pipe.PIPE_TYPE_MESSAGE
+
+ # When running as a service, we must use special security for the pipe
+ sa = pywintypes.SECURITY_ATTRIBUTES()
+ # Say we do have a DACL, and it is empty
+ # (ie, allow full access!)
+ sa.SetSecurityDescriptorDacl ( 1, None, 0 )
+
+ pipeHandle = win32pipe.CreateNamedPipe(pipeName,
+ openMode,
+ pipeMode,
+ win32pipe.PIPE_UNLIMITED_INSTANCES,
+ 0, 0, 6000, # default buffers, and 6 second timeout.
+ sa)
+
+ # Loop accepting and processing connections
+ while 1:
+ try:
+ hr = win32pipe.ConnectNamedPipe(pipeHandle, self.overlapped)
+ except pywintypes.error, inst:
+ print "Error connecting pipe: ", inst
+ pipeHandle.Close()
+ break
+
+ if hr==winerror.ERROR_PIPE_CONNECTED:
+ # Client is fast, and already connected - signal event
+ win32event.SetEvent(self.overlapped.hEvent)
+ # Wait for either a connection, or a service stop request.
+ timeout = win32event.INFINITE
+ waitHandles = self.hWaitStop, self.overlapped.hEvent
+ rc = win32event.WaitForMultipleObjects(waitHandles, 0, timeout)
+ if rc==win32event.WAIT_OBJECT_0:
+ # Stop event
+ break
+ else:
+ # read pipe and process request
+ try:
+ hr, data = win32file.ReadFile(pipeHandle, PIPEBUFSIZE)
+ message = "Processed %d bytes: '%s'" % (len(data), data)
+ print (message)
+
+ if not data:
+ raise SystemExit # signal by dispatch terminate
+
+ try:
+ data = data.decode('mbcs')
+ except:
+ pass
+
+ try:
+ status = get_hg_state(data)
+ except SystemExit:
+ raise SystemExit # interrupted by thread2.terminate()
+ except:
+ import traceback
+ print "WARNING: something went wrong in get_hg_state()"
+ print traceback.format_exc()
+ status = "ERROR"
+
+ win32file.WriteFile(pipeHandle, status)
+
+ # And disconnect from the client.
+ win32pipe.DisconnectNamedPipe(pipeHandle)
+ except win32file.error:
+ # Client disconnected without sending data
+ # or before reading the response.
+ # Thats OK - just get the next connection
+ continue
+
+if __name__ == '__main__':
+ import sys
+ if '--server' in sys.argv:
+ svc = PipeServer()
+ svc.SvcDoRun()
+ elif '--client' in sys.argv:
+ for x in sys.argv[1:]:
+ if x.startswith('-'):
+ continue
+ path = os.path.abspath(x)
+ try:
+ status = win32pipe.CallNamedPipe(PIPENAME, path, PIPEBUFSIZE, 0)
+ except pywintypes.error, inst:
+ print "can't access named pipe '%s'" % PIPENAME
+ sys.exit()
+ print "%s = %s" % (path, status)
+ else:
+ print "usage:\n%s [--server|--client]" % sys.argv[0]
+
+
|
@@ -76,6 +76,28 @@ os.environ['THG_ICON_PATH'] = os.path.join(dir, 'icons')
return dir
+ def get_tortoise_icon(icon):
+ '''Find a tortoise icon, apply to PyGtk window'''
+ # The context menu should set this variable
+ var = os.environ.get('THG_ICON_PATH', None)
+ paths = var and [ var ] or []
+ try:
+ # Else try relative paths from hggtk, the repository layout
+ dir = os.path.dirname(__file__)
+ paths.append(os.path.join(dir, '..', 'icons'))
+ # ... or the source installer layout
+ paths.append(os.path.join(dir, '..', '..', '..',
+ 'share', 'tortoisehg', 'icons'))
+ except NameError: # __file__ is not always available
+ pass
+ for p in paths:
+ path = os.path.join(p, 'tortoise', icon)
+ if os.path.isfile(path):
+ return path
+ else:
+ print 'icon not found', icon
+ return None
+
def netdrive_status(drive):
"""
return True if a network drive is accessible (connected, ...),
|
|
@@ -0,0 +1,49 @@ + # Interuptible threads
+#
+# http://sebulba.wikispaces.com/recipe+thread2
+
+import threading
+import inspect
+import ctypes
+
+
+def _async_raise(tid, exctype):
+ """raises the exception, performs cleanup if needed"""
+ if not inspect.isclass(exctype):
+ raise TypeError("Only types can be raised (not instances)")
+ res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
+ if res == 0:
+ raise ValueError("invalid thread id")
+ elif res != 1:
+ # """if it returns a number greater than one, you're in trouble,
+ # and you should call it again with exc=NULL to revert the effect"""
+ ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
+ raise SystemError("PyThreadState_SetAsyncExc failed")
+
+
+class Thread(threading.Thread):
+ def _get_my_tid(self):
+ """determines this (self's) thread id"""
+ if not self.isAlive():
+ raise threading.ThreadError("the thread is not active")
+
+ # do we have it cached?
+ if hasattr(self, "_thread_id"):
+ return self._thread_id
+
+ # no, look for it in the _active dict
+ for tid, tobj in threading._active.items():
+ if tobj is self:
+ self._thread_id = tid
+ return tid
+
+ raise AssertionError("could not determine the thread's id")
+
+ def raise_exc(self, exctype):
+ """raises the given exception type in the context of this thread"""
+ _async_raise(self._get_my_tid(), exctype)
+
+ def terminate(self):
+ """raises SystemExit in the context of the given thread, which should
+ cause the thread to exit silently (unless caught)"""
+ self.raise_exc(SystemExit)
|
@@ -48,17 +48,11 @@ def DllRegisterServer():
check_tortoise_overlays()
RegisterServer(ContextMenuExtension)
- RegisterServer(ChangedOverlay)
- RegisterServer(AddedOverlay)
- RegisterServer(UnchangedOverlay)
register_tortoise_path()
# for COM registration via py2exe
def DllUnregisterServer():
UnregisterServer(ContextMenuExtension)
- UnregisterServer(ChangedOverlay)
- UnregisterServer(AddedOverlay)
- UnregisterServer(UnchangedOverlay)
register_tortoise_path(unregister=True)
def RegisterServer(cls):
@@ -122,11 +116,6 @@ register.UseCommandLine(ContextMenuExtension,
finalize_register = lambda: RegisterServer(ContextMenuExtension),
finalize_unregister = lambda: UnregisterServer(ContextMenuExtension))
-
- for cls in (ChangedOverlay, AddedOverlay, UnchangedOverlay):
- register.UseCommandLine(cls,
- finalize_register = lambda: RegisterServer(cls),
- finalize_unregister = lambda: UnregisterServer(cls))
if "--unregister" in sys.argv[1:]:
register_tortoise_path(unregister=True)
|
Loading...