Skip to content

Commit 3f94442

Browse files
dschogitster
authored andcommitted
mingw: add experimental feature to redirect standard handles
Particularly when calling Git from applications, such as Visual Studio's Team Explorer, it is important that stdin/stdout/stderr are closed properly. However, when spawning processes on Windows, those handles must be marked as inheritable if we want to use them, but that flag is a global flag and may very well be used by other spawned processes which then do not know to close those handles. Let's introduce a set of environment variables (GIT_REDIRECT_STDIN and friends) that specify paths to files, or even better, named pipes (which are similar to Unix sockets) and that are used by the spawned Git process. This helps work around above-mentioned issue: those named pipes will be opened in a non-inheritable way upon startup, and no handles are passed around (and therefore no inherited handles need to be closed by any spawned child). This feature shipped with Git for Windows (marked as experimental) since v2.11.0(2), so it has seen some serious testing in the meantime. Signed-off-by: Johannes Schindelin <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent cb5918a commit 3f94442

File tree

2 files changed

+49
-0
lines changed

2 files changed

+49
-0
lines changed

compat/mingw.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2139,13 +2139,56 @@ static char *wcstoutfdup_startup(char *buffer, const wchar_t *wcs, size_t len)
21392139
return memcpy(malloc_startup(len), buffer, len);
21402140
}
21412141

2142+
static void maybe_redirect_std_handle(const wchar_t *key, DWORD std_id, int fd,
2143+
DWORD desired_access, DWORD flags)
2144+
{
2145+
DWORD create_flag = fd ? OPEN_ALWAYS : OPEN_EXISTING;
2146+
wchar_t buf[MAX_PATH];
2147+
DWORD max = ARRAY_SIZE(buf);
2148+
HANDLE handle;
2149+
DWORD ret = GetEnvironmentVariableW(key, buf, max);
2150+
2151+
if (!ret || ret >= max)
2152+
return;
2153+
2154+
/* make sure this does not leak into child processes */
2155+
SetEnvironmentVariableW(key, NULL);
2156+
if (!wcscmp(buf, L"off")) {
2157+
close(fd);
2158+
handle = GetStdHandle(std_id);
2159+
if (handle != INVALID_HANDLE_VALUE)
2160+
CloseHandle(handle);
2161+
return;
2162+
}
2163+
handle = CreateFileW(buf, desired_access, 0, NULL, create_flag,
2164+
flags, NULL);
2165+
if (handle != INVALID_HANDLE_VALUE) {
2166+
int new_fd = _open_osfhandle((intptr_t)handle, O_BINARY);
2167+
SetStdHandle(std_id, handle);
2168+
dup2(new_fd, fd);
2169+
close(new_fd);
2170+
}
2171+
}
2172+
2173+
static void maybe_redirect_std_handles(void)
2174+
{
2175+
maybe_redirect_std_handle(L"GIT_REDIRECT_STDIN", STD_INPUT_HANDLE, 0,
2176+
GENERIC_READ, FILE_ATTRIBUTE_NORMAL);
2177+
maybe_redirect_std_handle(L"GIT_REDIRECT_STDOUT", STD_OUTPUT_HANDLE, 1,
2178+
GENERIC_WRITE, FILE_ATTRIBUTE_NORMAL);
2179+
maybe_redirect_std_handle(L"GIT_REDIRECT_STDERR", STD_ERROR_HANDLE, 2,
2180+
GENERIC_WRITE, FILE_FLAG_NO_BUFFERING);
2181+
}
2182+
21422183
void mingw_startup(void)
21432184
{
21442185
int i, maxlen, argc;
21452186
char *buffer;
21462187
wchar_t **wenv, **wargv;
21472188
_startupinfo si;
21482189

2190+
maybe_redirect_std_handles();
2191+
21492192
/* get wide char arguments and environment */
21502193
si.newmode = 0;
21512194
if (__wgetmainargs(&argc, &wargv, &wenv, _CRT_glob, &si) < 0)

t/t0001-init.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,4 +453,10 @@ test_expect_success 're-init from a linked worktree' '
453453
)
454454
'
455455

456+
test_expect_success MINGW 'redirect std handles' '
457+
GIT_REDIRECT_STDOUT=output.txt git rev-parse --git-dir &&
458+
test .git = "$(cat output.txt)" &&
459+
test -z "$(GIT_REDIRECT_STDOUT=off git rev-parse --git-dir)"
460+
'
461+
456462
test_done

0 commit comments

Comments
 (0)