Skip to content

Commit 6029211

Browse files
committed
This fixes the path search bug where the current directory is included on Windows, by setting NoDefaultCurrentDirectoryInExePath for the caller. (Setting for the callee env would not work.) This sets it only on Windows, only for the duration of the Popen call, and then automatically unsets it or restores its old value. NoDefaultCurrentDirectoryInExePath is documented at: https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-needcurrentdirectoryforexepathw It automatically affects the behavior of subprocess.Popen on Windows, due to the way Popen uses the Windows API. (In contrast, it does not, at least currently on CPython, affect the behavior of shutil.which. But shutil.which is not being used to find git.exe.)
1 parent e19abe7 commit 6029211

File tree

1 file changed

+21
-17
lines changed

1 file changed

+21
-17
lines changed

Diff for: git/cmd.py

+21-17
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
66
from __future__ import annotations
77
import re
8-
from contextlib import contextmanager
8+
import contextlib
99
import io
1010
import logging
1111
import os
@@ -14,6 +14,7 @@
1414
import subprocess
1515
import threading
1616
from textwrap import dedent
17+
import unittest.mock
1718

1819
from git.compat import (
1920
defenc,
@@ -963,8 +964,11 @@ def execute(
963964
redacted_command,
964965
'"kill_after_timeout" feature is not supported on Windows.',
965966
)
967+
# Only search PATH, not CWD. This must be in the *caller* environment. The "1" can be any value.
968+
patch_caller_env = unittest.mock.patch.dict(os.environ, {"NoDefaultCurrentDirectoryInExePath": "1"})
966969
else:
967970
cmd_not_found_exception = FileNotFoundError # NOQA # exists, flake8 unknown @UndefinedVariable
971+
patch_caller_env = contextlib.nullcontext()
968972
# end handle
969973

970974
stdout_sink = PIPE if with_stdout else getattr(subprocess, "DEVNULL", None) or open(os.devnull, "wb")
@@ -980,21 +984,21 @@ def execute(
980984
istream_ok,
981985
)
982986
try:
983-
proc = Popen(
984-
command,
985-
env=env,
986-
cwd=cwd,
987-
bufsize=-1,
988-
stdin=istream or DEVNULL,
989-
stderr=PIPE,
990-
stdout=stdout_sink,
991-
shell=shell is not None and shell or self.USE_SHELL,
992-
close_fds=is_posix, # unsupported on windows
993-
universal_newlines=universal_newlines,
994-
creationflags=PROC_CREATIONFLAGS,
995-
**subprocess_kwargs,
996-
)
997-
987+
with patch_caller_env:
988+
proc = Popen(
989+
command,
990+
env=env,
991+
cwd=cwd,
992+
bufsize=-1,
993+
stdin=istream or DEVNULL,
994+
stderr=PIPE,
995+
stdout=stdout_sink,
996+
shell=shell is not None and shell or self.USE_SHELL,
997+
close_fds=is_posix, # unsupported on windows
998+
universal_newlines=universal_newlines,
999+
creationflags=PROC_CREATIONFLAGS,
1000+
**subprocess_kwargs,
1001+
)
9981002
except cmd_not_found_exception as err:
9991003
raise GitCommandNotFound(redacted_command, err) from err
10001004
else:
@@ -1144,7 +1148,7 @@ def update_environment(self, **kwargs: Any) -> Dict[str, Union[str, None]]:
11441148
del self._environment[key]
11451149
return old_env
11461150

1147-
@contextmanager
1151+
@contextlib.contextmanager
11481152
def custom_environment(self, **kwargs: Any) -> Iterator[None]:
11491153
"""
11501154
A context manager around the above ``update_environment`` method to restore the

0 commit comments

Comments
 (0)