On 12.04.2009 10:44, TK Soh wrote:
> On Sun, Apr 12, 2009 at 6:00 AM, Steve Borho <[email protected]> wrote:
>> <note the subject change>
>>
>> On Sat, Apr 11, 2009 at 5:52 PM, Mark Hammond <[email protected]> 
>> wrote:
>>>> I would be more than happy to get an excellent solution like the one
>>>> you describe.
>>>>
>>>> This sounds a bit like we could get your help implementing this on
>>>> TortoiseHG? (which would be fantastic indeed...)
>>> Yes, happy to help - although it will only be in spare time, so may take
>>> some time to get moving :)  I'll keep my eye on the list and keep an eye
>>> for when I can help...
>> I think what would help the most right now would be a bit of guidance.
>> Can we take the C++ extension out of the tbzr installer as it is and write
>> our own Python process for it?  If not, what changes do we need to make
>> to the C++ code?
>>
>> As for the Python process, I think if you give us a bit more detail on how
>> it communicates with the C++ shim, a number of developers are willing
>> to start working on it.  Here's some specific questions:
>>
>> 1) How do the extensions discover the Python process?
>> 2) How does it communicate (stdio, pipes, sockets)?
>> 3) How does it handle multiple connections?
> 
> FWIW, I just pushed my prototype code onto bitbucket, updated to
> tortoisehg stable.
> 
>     http://bitbucket.org/tksoh/thg-shellext/
> 
> To install, you need to have mercurial installed (from source),
> mingw32 (with make utility) & inno setup. Then follow these [somewhat
> painful] steps:
> 
> 1. uninstall you existing tortoisehg shell extension, if any
> 
> 2. chdir into the c++ prototype tortoisehg repo clone
> 
> 3. register the tortoisehg python-based shellext (to provide some
> registry settings and the standard context menu to help debugging):
> 
>     python tortoisehg.py --debug
> 
> 4. build the ThgShell.DLL in tortoise/shellext directory:
> 
>     cd tortoish\shellext
>     make
> 
> 5. register the DLL using the ThgShell.iss inno setup file. Note: this
> will add experimental set of TortoiseHg submenus to explorer context
> menu.
> 
> 6. run the taskbar.py in tortoise directory to start the RPC server.
> This will add a TortoiseHg menu to Window's taskbar. You can terminal
> the RPC server with the taskbar's Exit menu at anytime.
> 
> 7. restart Explorer.
> 
> I simply share the work in case it would somehow be beneficial to the
> upcoming work by Mark Hammond and others, since I have no doubt Mark
> will be able to write the whole thing from the ground up and be surely
> of far better quality. So, please feel free to ignore if you don't see
> much value in it.
> 
> Warp speed, to TortoiseHG.
> 

Thanks for sharing that work.

I was able to compile and run it on my Windows XP SP3 (only tested the
overlays).

I then slowly started hacking on trying to use CuteHG's approach, which uses
hg's dirstate from C++, because I am still interested to see how fast this
would be.

I took dirstate.c from cutehg-crew/win32/shellext and adapted it
a bit (dirstate.cpp -- attached for reference).

Today I was able to compile and link my dirstate.cpp with MingW, but I didn't
yet test it.

I intend to use http://bitbucket.org/tksoh/thg-shellext/ and adapt that to
call my dirstate.cpp, instead of using the python taskbar.py RPC server --
for comparison.




// Copyright (C) 2009 Benjamin Pollack 
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>


#ifdef WIN32

#ifndef _WINBASE_
#include <windef.h>   // needed by winbase.h
#include <stdarg.h>   // needed by winbase.h
#include <winbase.h>
#endif

#include <string.h>
#include <_mingw.h>

#define MAX_PATH          260


static __int64 days_between_epochs = 134774; /* days between 1.1.1601 and 
1.1.1970 */
static __int64 secs_between_epochs = (__int64)days_between_epochs * 86400;

int lstat(const char* file, struct _stat* pstat)
{
        WIN32_FIND_DATA data;
        HANDLE hfind;
        __int64 temp;

        hfind = FindFirstFile(file, &data);
        if (hfind == INVALID_HANDLE_VALUE)
                return -1;
        FindClose(hfind);

        pstat->st_mtime = *(__int64*)&data.ftLastWriteTime / 10000000 - 
secs_between_epochs;
        pstat->st_size = (data.nFileSizeHigh << sizeof(data.nFileSizeHigh)) | 
data.nFileSizeLow;

        return 0;
}

#endif


#define HASH_LENGTH 20


typedef struct _direntry
{
    unsigned char state;
    unsigned mode;
    unsigned size;
    unsigned mtime;
    unsigned length;
    char *name;
    char *origname;
} direntry;


typedef struct _dirstate
{
    char parent1[HASH_LENGTH];
    char parent2[HASH_LENGTH];
    
    unsigned num_entries;
    direntry *entries;
    unsigned __entries_length;
} dirstate;


typedef struct _dirstatecache
{
        const dirstate* dstate;
        struct _dirstatecache* next;
        __time64_t mtime;
        char path[MAX_PATH];
} dirstatecache;


void *xalloc(size_t n, size_t size)
{
    void *p = calloc(n, size);
    if (!p) exit(1);
    return p;
}


void dirstate_add_entry(dirstate *pd, const direntry *pe)
{
    if (pd->num_entries == pd->__entries_length)
    {
        pd->__entries_length = pd->__entries_length ? 2 * pd->__entries_length 
: 1;
        pd->entries = (direntry*) realloc(pd->entries, pd->__entries_length * 
sizeof(direntry));
    }
    pd->entries[pd->num_entries++] = *pe;
}


typedef unsigned long uint32_t;
static uint32_t ntohl(uint32_t x)
{
        return ((x & 0x000000ffUL) << 24) |
               ((x & 0x0000ff00UL) <<  8) |
               ((x & 0x00ff0000UL) >>  8) |
               ((x & 0xff000000UL) >> 24);
}


dirstate *dirstate_new(const char *path)
{
    direntry e;
    FILE *f = NULL;
        dirstate *pd = NULL;
        
    f = fopen(path, "rb");
    if (f == NULL) return NULL;
    pd = (dirstate*)xalloc(1, sizeof(dirstate));
    fread(&pd->parent1, sizeof(char), HASH_LENGTH, f);
    fread(&pd->parent2, sizeof(char), HASH_LENGTH, f);

    while (fread(&e.state, sizeof(e.state), 1, f) == 1)
    {
        e.name = e.origname = 0;
        
        fread(&e.mode, sizeof(e.mode), 1, f);
        fread(&e.size, sizeof(e.size), 1, f);
        fread(&e.mtime, sizeof(e.mtime), 1, f);
        fread(&e.length, sizeof(e.length), 1, f);

        e.mode = ntohl(e.mode);
        e.size = ntohl(e.size);
        e.mtime = ntohl(e.mtime);
        e.length = ntohl(e.length);
        
        e.name = (char*) malloc(e.length * sizeof(char) + 1);
        fread(e.name, sizeof(char), e.length, f);
        e.name[e.length] = 0;
        dirstate_add_entry(pd, &e);
    }

        fclose(f);
    
    return pd;
}


void dirstate_free(const dirstate *pd)
{
    unsigned ix;
    for (ix = 0; ix < pd->num_entries; ++ix) free(pd->entries[ix].name);
    free(pd->entries);
    free((void*)pd);
}


dirstatecache* _cache = NULL;
const dirstate* dirstate_get(const char* hgroot)
{
        char path[MAX_PATH+1] = "";
        struct _stat stat;
        dirstatecache* head;

        strncat(path, hgroot, MAX_PATH);
        strncat(path, "/.hg/dirstate", MAX_PATH);

        if (0 != lstat(path, &stat))
                return NULL;

        head = _cache;
        while (head)
        {
        if (strncmp(path, head->path, MAX_PATH) == 0)
                        break;
                head = head->next;
        }

        if (!head)
        {
                head = (dirstatecache*)xalloc(1, sizeof(dirstatecache));
                head->next = _cache;
                _cache = head;
        head->path[0] = '\0';
        strncat(head->path, path, MAX_PATH);
        }

        if (head->mtime < stat.st_mtime)
        {
                head->mtime = stat.st_mtime;
                if (head->dstate)
                        dirstate_free(head->dstate);
                head->dstate = dirstate_new(path);
        }

        return head->dstate;
}


static char *revhash_string(const char revhash[HASH_LENGTH])
{
    unsigned ix;
    static char rev_string[HASH_LENGTH * 2 + 1];
    static char *hexval = "0123456789abcdef";
    for (ix = 0; ix < HASH_LENGTH; ++ix)
    {
        rev_string[ix * 2] = hexval[(revhash[ix] >> 4) & 0xf];
        rev_string[ix * 2 + 1] = hexval[revhash[ix] & 0xf];
    }
    rev_string[sizeof(rev_string)] = 0;
    return rev_string;
}


char mapdirstate(const direntry* entry, const struct _stat* stat)
{
        switch (entry->state)
        {
        case 'n':
                if (entry->mtime == (unsigned)stat->st_mtime
                        && entry->size == (unsigned)stat->st_size
#ifndef WIN32
                        && entry->mode == stat->st_mode
#endif
                        )
                        return 'C';
                else
                        return 'M';
        case 'm':
                return 'M';
        case 'r':
                return 'R';
        case 'a':
                return 'A';
        default:
                return '?';
        }
}


int HgQueryDirstate(const char* hgroot, const char* abspath, char* relpathloc, 
const dirstate** ppd,    struct _stat* pstat)
{
        char* temp;

        if (0 != lstat(abspath, pstat))
                return 0;

        *ppd = dirstate_get(hgroot);
        if (!*ppd)
                return 0;

        temp = relpathloc;
        while (*temp)
        {
                if (*temp == '\\')
                        *temp = '/';
                temp++;
        }

        return 1;
}


int HgQueryDirstateDirectory(const char* hgroot, char* abspath, char* 
relpathloc, char* outStatus)
{
        const dirstate* pd;
        struct _stat stat;
        unsigned ix;
        size_t len, rootlen;
        char temp[2*MAX_PATH+10];
        int a = 0, m = 0;

        if (!HgQueryDirstate(hgroot, abspath, relpathloc, &pd, &stat))
                return 0;

        rootlen = strlen(hgroot);
        len = strlen(relpathloc);
        for (ix = 0; ix < pd->num_entries && !a; ix++)
        {
                if (0 != strncmp(relpathloc, pd->entries[ix].name, len))
                        continue;

                switch (pd->entries[ix].state)
                {
                case 'n':
                        if (!m)
                        {
                temp[0] = '\0';
                strncat(temp, hgroot, MAX_PATH);
                strcat(temp, "/");
                strncat(temp, pd->entries[ix].name, MAX_PATH);
                                if (0 == lstat(temp, &stat))
                                        m = (mapdirstate(&pd->entries[ix], 
&stat) == 'M');
                        }
                        break;
                case 'm':
                        m = 1;
                        break;
                case 'a':
                        a = 1;
                        break;
                }
        }

        if (a)
                *outStatus = 'A';
        else if (m)
                *outStatus = 'M';
        else
                *outStatus = 'C';

        return 1;
}


int HgQueryDirstateFile(const char* hgroot, const char* abspath, char* 
relpathloc, char* outStatus)
{
        const dirstate* pd;
        struct _stat stat;
        unsigned ix;

        if (!HgQueryDirstate(hgroot, abspath, relpathloc, &pd, &stat))
                return 0;

        for (ix = 0; ix < pd->num_entries; ix++)
        {
                if (0 == strncmp(relpathloc, pd->entries[ix].name, MAX_PATH))
                {
                        *outStatus = mapdirstate(&pd->entries[ix], &stat);
                        return *outStatus != '?';
                }
        }

        return 0;
}


#if 0
int main(int argc, char *argv[])
{
    dirstate *pd = dirstate_new(".hg/dirstate");
    time_t t;
    char *s;
    unsigned ix;
    printf("parent1: %s\n", revhash_string(pd->parent1));
    printf("parent2: %s\n", revhash_string(pd->parent2));
    printf("entries: %d\n\n", pd->num_entries);
    for (ix = 0; ix < pd->num_entries; ++ix)
    {
        t = pd->entries[ix].mtime;
        s = ctime(&t);
        s[strlen(s) - 1] = '\0';
        printf("%s %s\n", s, pd->entries[ix].name);
    }
    dirstate_free(pd);
    return 0;
}
#endif
------------------------------------------------------------------------------
Stay on top of everything new and different, both inside and 
around Java (TM) technology - register by April 22, and save
$200 on the JavaOne (SM) conference, June 2-5, 2009, San Francisco.
300 plus technical and hands-on sessions. Register today. 
Use priority code J9JMT32. http://p.sf.net/sfu/p
_______________________________________________
Tortoisehg-develop mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/tortoisehg-develop

Reply via email to