Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7127707

Browse files
author
terminalchai
committed
fix: handle cross-drive ValueError in _adjust_args_and_chdir on Windows
On Windows, os.path.relpath() raises ValueError when the path and the current working directory are on different drives (e.g. config on C: but the git repo is on D:). Introduce a small _try_relpath() helper that wraps os.path.relpath in a try/except ValueError and returns the original path unchanged when the drives differ. All four os.path.relpath calls in _adjust_args_and_chdir (config, files, commit_msg_filename, repo) are updated to use this helper, so every cross-drive combination is covered. Fixes #2530
1 parent f5678bf commit 7127707

2 files changed

Lines changed: 43 additions & 4 deletions

File tree

pre_commit/main.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,16 @@ def _add_run_options(parser: argparse.ArgumentParser) -> None:
172172
)
173173

174174

175+
def _try_relpath(path: str) -> str:
176+
"""Return os.path.relpath(path), or path unchanged on Windows when path
177+
and the current working directory are on different drives (which would
178+
raise ValueError: path is on mount 'C:', start on mount 'D:')."""
179+
try:
180+
return os.path.relpath(path)
181+
except ValueError:
182+
return path
183+
184+
175185
def _adjust_args_and_chdir(args: argparse.Namespace) -> None:
176186
# `--config` was specified relative to the non-root working directory
177187
if os.path.exists(args.config):
@@ -188,15 +198,15 @@ def _adjust_args_and_chdir(args: argparse.Namespace) -> None:
188198
toplevel = git.get_root()
189199
os.chdir(toplevel)
190200

191-
args.config = os.path.relpath(args.config)
201+
args.config = _try_relpath(args.config)
192202
if args.command in {'run', 'try-repo'}:
193-
args.files = [os.path.relpath(filename) for filename in args.files]
203+
args.files = [_try_relpath(filename) for filename in args.files]
194204
if args.commit_msg_filename is not None:
195-
args.commit_msg_filename = os.path.relpath(
205+
args.commit_msg_filename = _try_relpath(
196206
args.commit_msg_filename,
197207
)
198208
if args.command == 'try-repo' and os.path.exists(args.repo):
199-
args.repo = os.path.relpath(args.repo)
209+
args.repo = _try_relpath(args.repo)
200210

201211

202212
def main(argv: Sequence[str] | None = None) -> int:

tests/main_test.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,35 @@ def test_adjust_args_try_repo_repo_relative(in_git_dir):
8989
assert args.repo == 'foo'
9090

9191

92+
def test_try_relpath_returns_relpath_when_same_drive():
93+
# Normal case: same drive / same filesystem -- should behave like relpath
94+
result = main._try_relpath(os.path.abspath('.'))
95+
assert result == os.path.relpath(os.path.abspath('.'))
96+
97+
98+
def test_try_relpath_returns_original_on_cross_drive_valueerror():
99+
# On Windows, os.path.relpath raises ValueError when paths are on
100+
# different drives (#2530). _try_relpath must return the path unchanged.
101+
abs_path = 'C:\\Users\\me\\.pre-commit-config.yaml'
102+
with mock.patch('os.path.relpath', side_effect=ValueError('different mount')):
103+
assert main._try_relpath(abs_path) == abs_path
104+
105+
106+
@pytest.mark.skipif(os.name != 'nt', reason='windows cross-drive only')
107+
def test_adjust_args_config_on_different_drive(in_git_dir): # pragma: posix no cover
108+
# Simulate a config whose absolute path is on a drive other than the git
109+
# root so that os.path.relpath would raise ValueError.
110+
abs_config = 'C:\\Users\\me\\.pre-commit-config.yaml'
111+
with mock.patch('pre_commit.main.os.path.relpath', side_effect=ValueError('x')):
112+
args = _args(config=abs_config)
113+
# abspath is called before chdir, so mock exists to avoid a real chdir
114+
with mock.patch('os.path.exists', return_value=False):
115+
with mock.patch('pre_commit.git.get_root', return_value=str(in_git_dir)):
116+
main._adjust_args_and_chdir(args)
117+
# config must be preserved (not explode with ValueError)
118+
assert args.config == abs_config
119+
120+
92121
FNS = (
93122
'autoupdate', 'clean', 'gc', 'hook_impl', 'install', 'install_hooks',
94123
'migrate_config', 'run', 'sample_config', 'uninstall',

0 commit comments

Comments
 (0)