Skip to content

Commit

Permalink
Added a compiled wrapper to replace the git.cmd script.
Browse files Browse the repository at this point in the history
This addresses github issue msysgit#36 which points out problems in handling
git commit notations of the form tag^{commit} and HEAD^. The windows
command shell uses caret as a quote both on the command line and in
batch scripts and this results in requiring double quoting. To be
able to handle both "tag^{commit}" and tag^^{commit} (either style
of command prompt quoting) the script needs replacing. This also makes
it simpler to call git from batch scripts as it will no longer need to be
prefixed with 'call'.

Signed-off-by: Pat Thoyts <patthoyts@users.sourceforge.net>
  • Loading branch information
patthoyts committed Jun 27, 2012
1 parent cb9836b commit 893b221
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 0 deletions.
4 changes: 4 additions & 0 deletions src/git-wrapper/.gitignore
@@ -0,0 +1,4 @@
*.exe
*.o
*~

16 changes: 16 additions & 0 deletions src/git-wrapper/Makefile
@@ -0,0 +1,16 @@
CC = gcc
CFLAGS = -Wall -Wwrite-strings
LD = gcc
LDFLAGS = -Wall -s
LIBS =-lshell32 -lshlwapi

all: git-wrapper

git-wrapper: git-wrapper.o
$(LD) $(LDFLAGS) -o $@ $^ $(LIBS)

clean:
-rm -f git-wrapper.o git-wrapper.exe

%.o: %.c
$(CC) $(CFLAGS) -c $^ -o $@
160 changes: 160 additions & 0 deletions src/git-wrapper/git-wrapper.c
@@ -0,0 +1,160 @@
/*
* git-wrapper - replace cmd\git.cmd with an executable
*
* Copyright (C) 2012 Pat Thoyts <patthoyts@users.sourceforge.net>
*/

#define STRICT
#define WIN32_LEAN_AND_MEAN
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <shlwapi.h>
#include <shellapi.h>

#ifdef __MSC_VER__
int __stdcall wmain(void)
#else
int main(void)
#endif
{
int r = 1, wait = 1;
WCHAR exepath[MAX_PATH], exe[MAX_PATH];
LPWSTR cmd = NULL, path2 = NULL, exep = exe;
UINT codepage = 0;
int len;

/* get the installation location */
GetModuleFileName(NULL, exepath, MAX_PATH);
PathRemoveFileSpec(exepath);
PathRemoveFileSpec(exepath);

/* set the default exe module */
wcscpy(exe, exepath);
PathAppend(exe, L"bin\\git.exe");

/* if not set, set TERM to msys */
if (GetEnvironmentVariable(L"TERM", NULL, 0) == 0) {
SetEnvironmentVariable(L"TERM", L"msys");
}

/* if not set, set PLINK_PROTOCOL to ssh */
if (GetEnvironmentVariable(L"PLINK_PROTOCOL", NULL, 0) == 0) {
SetEnvironmentVariable(L"PLINK_PROTOCOL", L"ssh");
}

/* set HOME to %HOMEDRIVE%%HOMEPATH% or %USERPROFILE%
* With roaming profiles: HOMEPATH is the roaming location and
* USERPROFILE is the local location
*/
if (GetEnvironmentVariable(L"HOME", NULL, 0) == 0) {
LPWSTR e = NULL;
len = GetEnvironmentVariable(L"HOMEPATH", NULL, 0);
if (len == 0) {
len = GetEnvironmentVariable(L"USERPROFILE", NULL, 0);
if (len != 0) {
e = (LPWSTR)malloc(len * sizeof(WCHAR));
GetEnvironmentVariable(L"USERPROFILE", e, len);
SetEnvironmentVariable(L"HOME", e);
free(e);
}
} else {
int n;
len += GetEnvironmentVariable(L"HOMEDRIVE", NULL, 0);
e = (LPWSTR)malloc(sizeof(WCHAR) * (len + 2));
n = GetEnvironmentVariable(L"HOMEDRIVE", e, len);
GetEnvironmentVariable(L"HOMEPATH", &e[n], len-n);
SetEnvironmentVariable(L"HOME", e);
free(e);
}
}

/* extend the PATH */
len = GetEnvironmentVariable(L"PATH", NULL, 0);
len = sizeof(WCHAR) * (len + 2 * MAX_PATH);
path2 = (LPWSTR)malloc(len);
wcscpy(path2, exepath);
PathAppend(path2, L"bin;");
/* should do this only if it exists */
wcscat(path2, exepath);
PathAppend(path2, L"mingw\\bin;");
GetEnvironmentVariable(L"PATH", &path2[wcslen(path2)],
(len/sizeof(WCHAR))-wcslen(path2));
SetEnvironmentVariable(L"PATH", path2);
free(path2);


/* fix up the command line to call git.exe
* We have to be very careful about quoting here so we just
* trim off the first argument and replace it leaving the rest
* untouched.
*/
{
int wargc = 0, gui = 0;
LPWSTR cmdline = NULL;
LPWSTR *wargv = NULL, p = NULL;
cmdline = GetCommandLine();
wargv = CommandLineToArgvW(cmdline, &wargc);
cmd = (LPWSTR)malloc(sizeof(WCHAR) * (wcslen(cmdline) + MAX_PATH));
if (wargc > 1 && wcsicmp(L"gui", wargv[1]) == 0) {
wait = 0;
if (wargc > 2 && wcsicmp(L"citool", wargv[2]) == 0) {
wait = 1;
wcscpy(cmd, L"git.exe");
} else {
WCHAR script[MAX_PATH];
gui = 1;
wcscat(script, exepath);
PathAppend(script, L"libexec\\git-core\\git-gui");
PathQuoteSpaces(script);
wcscpy(cmd, L"wish.exe ");
wcscat(cmd, script);
wcscat(cmd, L" --");
exep = NULL; /* find the module from the commandline */
}
} else {
wcscpy(cmd, L"git.exe");
}
/* find the first space after the initial parameter then append all */
p = wcschr(&cmdline[wcslen(wargv[0])], L' ');
if (p && *p) {
/* for git gui subcommands, remove the 'gui' word */
if (gui) {
while (*p == L' ') ++p;
p = wcschr(p, L' ');
}
if (p && *p)
wcscat(cmd, p);
}
LocalFree(wargv);
}

/* set the console to ANSI/GUI codepage */
codepage = GetConsoleCP();
SetConsoleCP(GetACP());

{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
CreateProcess(exep,/* module: null means use command line */
cmd, /* modified command line */
NULL, /* process handle inheritance */
NULL, /* thread handle inheritance */
TRUE, /* handles inheritable? */
CREATE_UNICODE_ENVIRONMENT,
NULL, /* environment: use parent */
NULL, /* starting directory: use parent */
&si, &pi);
if (wait)
WaitForSingleObject(pi.hProcess, INFINITE);
}

free(cmd);

/* reset the console codepage */
SetConsoleCP(codepage);
ExitProcess(r);
}
21 changes: 21 additions & 0 deletions src/git-wrapper/release.sh
@@ -0,0 +1,21 @@
#!/bin/sh

cd "$(dirname "$0")"

DEST=/cmd/git.exe

die () {
echo "$*" >&2
exit 1
}

rmscript () {
test -f /cmd/git.cmd && rm /cmd/git.cmd || true
}

make &&
index=$(/share/msysGit/pre-install.sh) &&
rmscript &&
install -m 775 git-wrapper.exe $DEST &&
/share/msysGit/post-install.sh $index "Updated git wrapper exe" ||
die "Failed to update git wrapper executable"

0 comments on commit 893b221

Please sign in to comment.