Skip to content

Commit a2114cf

Browse files
committed
Merge branch 'js/mingw-redirection' into pu
* js/mingw-redirection: mingw: document the experimental standard handle redirection mingw: special-case GIT_REDIRECT_STDERR=2>&1 mingw: add experimental feature to redirect standard handles
2 parents 8afb990 + dd43483 commit a2114cf

File tree

3 files changed

+87
-0
lines changed

3 files changed

+87
-0
lines changed

Documentation/git.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,23 @@ of clones and fetches.
719719
'key[=value]'. Presence of unknown keys and values must be
720720
ignored.
721721

722+
`GIT_REDIRECT_STDIN`::
723+
`GIT_REDIRECT_STDOUT`::
724+
`GIT_REDIRECT_STDERR`::
725+
(EXPERIMENTAL) Windows-only: allow redirecting the standard
726+
input/output/error handles. This is particularly useful in
727+
multi-threaded applications where the canonical way to pass
728+
standard handles via `CreateProcess()` is not an option because
729+
it would require the handles to be marked inheritable (and
730+
consequently *every* spawned process would inherit them, possibly
731+
blocking regular Git operations). The primary intended use case
732+
is to use named pipes for communication.
733+
+
734+
Two special values are supported: `off` will simply close the
735+
corresponding standard handle, and if `GIT_REDIRECT_STDERR` is
736+
`2>&1`, standard error will be redirected to the same handle as
737+
standard output.
738+
722739
Discussion[[Discussion]]
723740
------------------------
724741

compat/mingw.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2139,13 +2139,71 @@ 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+
if (std_id == STD_ERROR_HANDLE && !wcscmp(buf, L"2>&1")) {
2164+
handle = GetStdHandle(STD_OUTPUT_HANDLE);
2165+
if (handle == INVALID_HANDLE_VALUE) {
2166+
close(fd);
2167+
handle = GetStdHandle(std_id);
2168+
if (handle != INVALID_HANDLE_VALUE)
2169+
CloseHandle(handle);
2170+
} else {
2171+
int new_fd = _open_osfhandle((intptr_t)handle, O_BINARY);
2172+
SetStdHandle(std_id, handle);
2173+
dup2(new_fd, fd);
2174+
/* do *not* close the new_fd: that would close stdout */
2175+
}
2176+
return;
2177+
}
2178+
handle = CreateFileW(buf, desired_access, 0, NULL, create_flag,
2179+
flags, NULL);
2180+
if (handle != INVALID_HANDLE_VALUE) {
2181+
int new_fd = _open_osfhandle((intptr_t)handle, O_BINARY);
2182+
SetStdHandle(std_id, handle);
2183+
dup2(new_fd, fd);
2184+
close(new_fd);
2185+
}
2186+
}
2187+
2188+
static void maybe_redirect_std_handles(void)
2189+
{
2190+
maybe_redirect_std_handle(L"GIT_REDIRECT_STDIN", STD_INPUT_HANDLE, 0,
2191+
GENERIC_READ, FILE_ATTRIBUTE_NORMAL);
2192+
maybe_redirect_std_handle(L"GIT_REDIRECT_STDOUT", STD_OUTPUT_HANDLE, 1,
2193+
GENERIC_WRITE, FILE_ATTRIBUTE_NORMAL);
2194+
maybe_redirect_std_handle(L"GIT_REDIRECT_STDERR", STD_ERROR_HANDLE, 2,
2195+
GENERIC_WRITE, FILE_FLAG_NO_BUFFERING);
2196+
}
2197+
21422198
void mingw_startup(void)
21432199
{
21442200
int i, maxlen, argc;
21452201
char *buffer;
21462202
wchar_t **wenv, **wargv;
21472203
_startupinfo si;
21482204

2205+
maybe_redirect_std_handles();
2206+
21492207
/* get wide char arguments and environment */
21502208
si.newmode = 0;
21512209
if (__wgetmainargs(&argc, &wargv, &wenv, _CRT_glob, &si) < 0)

t/t0001-init.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,4 +453,16 @@ 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+
test_must_fail env \
461+
GIT_REDIRECT_STDOUT=output.txt \
462+
GIT_REDIRECT_STDERR="2>&1" \
463+
git rev-parse --git-dir --verify refs/invalid &&
464+
printf ".git\nfatal: Needed a single revision\n" >expect &&
465+
test_cmp expect output.txt
466+
'
467+
456468
test_done

0 commit comments

Comments
 (0)