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