aboutsummaryrefslogtreecommitdiffstats
path: root/compat/win32/headless.c
diff options
context:
space:
mode:
authorJiang Xin <worldhello.net@gmail.com>2023-08-16 07:24:56 +0800
committerJiang Xin <worldhello.net@gmail.com>2023-08-16 07:24:56 +0800
commit62a26b36bd9bf76feb6940e2bee732bd62b429f8 (patch)
tree3500be70c7d872566578e3fa324f1a8d1fe796e7 /compat/win32/headless.c
parent450f2c9e3e461e4785643730f9c82eb9a64faddd (diff)
parentf1ed9d7dc0e49dc1a044941d821c9d2342313c26 (diff)
downloadgit-62a26b36bd9bf76feb6940e2bee732bd62b429f8.tar.gz
Merge branch 'master' of github.com:git/git
* 'master' of github.com:git/git: (34 commits) Git 2.42-rc2 t4053: avoid writing to unopened pipe t4053: avoid race when killing background processes Git 2.42-rc1 git maintenance: avoid console window in scheduled tasks on Windows win32: add a helper to run `git.exe` without a foreground window t9001: remove excessive GIT_SEND_EMAIL_NOTTY=1 mv: handle lstat() failure correctly parse-options: disallow negating OPTION_SET_INT 0 repack: free geometry struct send-email: avoid creating more than one Term::ReadLine object send-email: drop FakeTerm hack t0040: declare non-tab indentation to be okay in this script advice: handle "rebase" in error_resolve_conflict() A few more topics before -rc1 mailmap: change primary address for Glen Choo gitignore: ignore clangd .cache directory docs: update when `git bisect visualize` uses `gitk` compat/mingw: implement a native locate_in_PATH() run-command: conditionally define locate_in_PATH() ...
Diffstat (limited to 'compat/win32/headless.c')
-rw-r--r--compat/win32/headless.c115
1 files changed, 115 insertions, 0 deletions
diff --git a/compat/win32/headless.c b/compat/win32/headless.c
new file mode 100644
index 0000000000..8b00dfe3bd
--- /dev/null
+++ b/compat/win32/headless.c
@@ -0,0 +1,115 @@
+/*
+ * headless Git - run Git without opening a console window on Windows
+ */
+
+#define STRICT
+#define WIN32_LEAN_AND_MEAN
+#define UNICODE
+#define _UNICODE
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <wchar.h>
+
+/*
+ * If `dir` contains the path to a Git exec directory, extend `PATH` to
+ * include the corresponding `bin/` directory (which is where all those
+ * `.dll` files needed by `git.exe` are, on Windows).
+ */
+static int extend_path(wchar_t *dir, size_t dir_len)
+{
+ const wchar_t *suffix = L"\\libexec\\git-core";
+ size_t suffix_len = wcslen(suffix);
+ wchar_t *env;
+ DWORD len;
+
+ if (dir_len < suffix_len)
+ return 0;
+
+ dir_len -= suffix_len;
+ if (memcmp(dir + dir_len, suffix, suffix_len * sizeof(wchar_t)))
+ return 0;
+
+ len = GetEnvironmentVariableW(L"PATH", NULL, 0);
+ if (!len)
+ return 0;
+
+ env = _alloca((dir_len + 5 + len) * sizeof(wchar_t));
+ wcsncpy(env, dir, dir_len);
+ wcscpy(env + dir_len, L"\\bin;");
+ if (!GetEnvironmentVariableW(L"PATH", env + dir_len + 5, len))
+ return 0;
+
+ SetEnvironmentVariableW(L"PATH", env);
+ return 1;
+}
+
+int WINAPI wWinMain(_In_ HINSTANCE instance,
+ _In_opt_ HINSTANCE previous_instance,
+ _In_ LPWSTR command_line, _In_ int show)
+{
+ wchar_t git_command_line[32768];
+ size_t size = sizeof(git_command_line) / sizeof(wchar_t);
+ const wchar_t *needs_quotes = L"";
+ int slash = 0, i;
+
+ STARTUPINFO startup_info = {
+ .cb = sizeof(STARTUPINFO),
+ .dwFlags = STARTF_USESHOWWINDOW,
+ .wShowWindow = SW_HIDE,
+ };
+ PROCESS_INFORMATION process_info = { 0 };
+ DWORD creation_flags = CREATE_UNICODE_ENVIRONMENT |
+ CREATE_NEW_CONSOLE | CREATE_NO_WINDOW;
+ DWORD exit_code;
+
+ /* First, determine the full path of argv[0] */
+ for (i = 0; _wpgmptr[i]; i++)
+ if (_wpgmptr[i] == L' ')
+ needs_quotes = L"\"";
+ else if (_wpgmptr[i] == L'\\')
+ slash = i;
+
+ if (slash >= size - 11)
+ return 127; /* Too long path */
+
+ /* If it is in Git's exec path, add the bin/ directory to the PATH */
+ extend_path(_wpgmptr, slash);
+
+ /* Then, add the full path of `git.exe` as argv[0] */
+ i = swprintf_s(git_command_line, size, L"%ls%.*ls\\git.exe%ls",
+ needs_quotes, slash, _wpgmptr, needs_quotes);
+ if (i < 0)
+ return 127; /* Too long path */
+
+ if (*command_line) {
+ /* Now, append the command-line arguments */
+ i = swprintf_s(git_command_line + i, size - i,
+ L" %ls", command_line);
+ if (i < 0)
+ return 127;
+ }
+
+ startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ startup_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+
+ if (!CreateProcess(NULL, /* infer argv[0] from the command line */
+ git_command_line, /* modified command line */
+ NULL, /* inherit process handles? */
+ NULL, /* inherit thread handles? */
+ FALSE, /* handles inheritable? */
+ creation_flags,
+ NULL, /* use this process' environment */
+ NULL, /* use this process' working directory */
+ &startup_info, &process_info))
+ return 129; /* could not start */
+ WaitForSingleObject(process_info.hProcess, INFINITE);
+ if (!GetExitCodeProcess(process_info.hProcess, &exit_code))
+ exit_code = 130; /* Could not determine exit code? */
+
+ CloseHandle(process_info.hProcess);
+ CloseHandle(process_info.hThread);
+
+ return (int)exit_code;
+}