From 893b221982321303e1a31b2098cd115c65ca49ab Mon Sep 17 00:00:00 2001 From: Pat Thoyts Date: Wed, 27 Jun 2012 17:23:18 +0100 Subject: [PATCH] Added a compiled wrapper to replace the git.cmd script. This addresses github issue #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 --- src/git-wrapper/.gitignore | 4 ++ src/git-wrapper/Makefile | 16 +++++ src/git-wrapper/git-wrapper.c | 160 ++++++++++++++++++++++++++++++++++++++++++ src/git-wrapper/release.sh | 21 ++++++ 4 files changed, 201 insertions(+) create mode 100644 src/git-wrapper/.gitignore create mode 100644 src/git-wrapper/Makefile create mode 100644 src/git-wrapper/git-wrapper.c create mode 100755 src/git-wrapper/release.sh diff --git a/src/git-wrapper/.gitignore b/src/git-wrapper/.gitignore new file mode 100644 index 00000000..8d23a939 --- /dev/null +++ b/src/git-wrapper/.gitignore @@ -0,0 +1,4 @@ +*.exe +*.o +*~ + diff --git a/src/git-wrapper/Makefile b/src/git-wrapper/Makefile new file mode 100644 index 00000000..c75c9441 --- /dev/null +++ b/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 $@ diff --git a/src/git-wrapper/git-wrapper.c b/src/git-wrapper/git-wrapper.c new file mode 100644 index 00000000..bae6dcbf --- /dev/null +++ b/src/git-wrapper/git-wrapper.c @@ -0,0 +1,160 @@ +/* + * git-wrapper - replace cmd\git.cmd with an executable + * + * Copyright (C) 2012 Pat Thoyts + */ + +#define STRICT +#define WIN32_LEAN_AND_MEAN +#define UNICODE +#define _UNICODE +#include +#include +#include + +#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); +} diff --git a/src/git-wrapper/release.sh b/src/git-wrapper/release.sh new file mode 100755 index 00000000..6d02ac66 --- /dev/null +++ b/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" -- 2.11.4.GIT