Source

thg / thgutil / shlib.py

# shlib.py - TortoiseHg shell utilities
#
# Copyright 2007 TK Soh <teekaysoh@gmail.com>
# Copyright 2008 Steve Borho <steve@borho.org>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2, incorporated herein by reference.

import os
import sys
import time
from mercurial.i18n import _
from mercurial import hg

def get_system_times():
    t = os.times()
    if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
        t = (t[0], t[1], t[2], t[3], time.clock())
    return t

def version():
    try:
        import __version__
        return __version__.version
    except ImportError:
        return _('unknown')

if os.name == 'nt':
    def shell_notify(paths):
        try:
            from win32com.shell import shell, shellcon
            import pywintypes
        except ImportError:
            return
        dirs = set()
        for path in paths:
            abspath = os.path.abspath(path)
            if not os.path.isdir(abspath):
                abspath = os.path.dirname(abspath)
            dirs.add(abspath)
        # send notifications to deepest directories first
        for dir in sorted(dirs, key=len, reverse=True):
            try:
                pidl, ignore = shell.SHILCreateFromPath(dir, 0)
            except pywintypes.com_error:
                return
            if pidl is None:
                continue
            shell.SHChangeNotify(shellcon.SHCNE_UPDATEITEM,
                                 shellcon.SHCNF_IDLIST | shellcon.SHCNF_FLUSH,
                                 pidl, None)

    def update_thgstatus(ui, root, wait=False):
        '''Rewrite the file .hg/thgstatus

        Caches the information provided by repo.status() in the file 
        .hg/thgstatus, which can then be read by the overlay shell extension
        to display overlay icons for directories.

        The file .hg/thgstatus contains one line for each directory that has
        removed, modified or added files (in that order of preference). Each
        line consists of one char for the status of the directory (r, m or a),
        followed by the relative path of the directory in the repo. If the
        file .hg/thgstatus is empty, then the repo's working directory is
        clean.

        Specify wait=True to wait until the system clock ticks to the next
        second before accessing Mercurial's dirstate. This is useful when
        Mercurial's .hg/dirstate contains unset entries (in output of
        "hg debugstate"). unset entries happen if .hg/dirstate was updated
        within the same second as Mercurial updated the respective file in
        the working tree. This happens with a high probability for example
        when cloning a repo. The overlay shell extension will display unset
        dirstate entries as (potentially false) modified. Specifying wait=True
        ensures that there are no unset entries left in .hg/dirstate when this
        function exits.
        '''
        if wait:
            tref = time.time()
            tdelta = float(int(tref)) + 1.0 - tref
            if (tdelta > 0.0):
                time.sleep(tdelta)

        repo = hg.repository(ui, root) # a fresh repo object is needed
        repostate = repo.status() # will update .hg/dirstate as a side effect
        modified, added, removed, deleted = repostate[:4]

        dirstatus = {}
        def dirname(f):
            return '/'.join(f.split('/')[:-1])
        for fn in added:
            dirstatus[dirname(fn)] = 'a'
        for fn in modified:
            dirstatus[dirname(fn)] = 'm'
        for fn in removed + deleted:
            dirstatus[dirname(fn)] = 'r'

        update = False
        f = None
        try:
            try:
                f = repo.opener('thgstatus', 'rb')
                for dn in sorted(dirstatus):
                    s = dirstatus[dn]
                    e = f.readline()
                    if e.startswith('@@noicons'):
                        break
                    if e == '' or e[0] != s or e[1:-1] != dn:
                        update = True
                        break
                if f.readline() != '':
                    # extra line in f, needs update
                    update = True
            except IOError:
                update = True
        finally:
            if f != None:
                f.close()

        if update:
            f = repo.opener('thgstatus', 'wb', atomictemp=True)
            for dn in sorted(dirstatus):
                s = dirstatus[dn]
                f.write(s + dn + '\n')
                ui.note("%s %s\n" % (s, dn))
            f.rename()

else:
    def shell_notify(paths):
        if not paths:
            return
        notify = os.environ.get('THG_NOTIFY', '.tortoisehg/notify')
        if not os.path.isabs(notify):
            notify = os.path.join(os.path.expanduser('~'), notify)
            os.environ['THG_NOTIFY'] = notify
        if not os.path.isfile(notify):
            return
        f_notify = open(notify, 'w')
        try:
            f_notify.write('\n'.join([os.path.abspath(path) for path in paths]))
        finally:
            f_notify.close()

    def update_thgstatus(*args, **kws):
        pass