Skip to content

Commit 93f1192

Browse files
authored
Merge pull request #2255 from gitpython-developers/fix-polish-url
fix(repo): preserve literal separate Git directory paths
2 parents 71b9545 + 9349ff5 commit 93f1192

5 files changed

Lines changed: 62 additions & 3 deletions

File tree

‎doc/source/changes.rst‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ Security fixes for
99

1010
* https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-w8jc-g24h-crhw
1111
* https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-m64x-33q8-m5h7
12+
* https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-fx3j-rwgx-fr94
13+
14+
If you can, also try and provide feedback on the upcoming v4 branch
15+
https://github.com/gitpython-developers/GitPython/pull/2177 - patches welcome.
16+
17+
See the following for all changes.
18+
https://github.com/gitpython-developers/GitPython/releases/tag/3.2.1
1219

1320
3.2.0
1421
=====
@@ -25,7 +32,7 @@ If you can, also try and provide feedback on the upcoming v4 branch
2532
https://github.com/gitpython-developers/GitPython/pull/2177 - patches welcome.
2633

2734
See the following for all changes.
28-
https://github.com/gitpython-developers/GitPython/releases/tag/3.1.63
35+
https://github.com/gitpython-developers/GitPython/releases/tag/3.2.0
2936

3037
3.1.62
3138
======

‎git/repo/base.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1537,7 +1537,7 @@ def _clone(
15371537
clone_path = Git.polish_url(path) if Git.is_cygwin() and "bare" in kwargs else path
15381538
sep_dir = kwargs.get("separate_git_dir")
15391539
if sep_dir:
1540-
kwargs["separate_git_dir"] = Git.polish_url(sep_dir)
1540+
kwargs["separate_git_dir"] = Git.polish_url(os.fspath(sep_dir), expand_vars=False)
15411541
multi = None
15421542
if multi_options:
15431543
multi = shlex.split(" ".join(multi_options))

‎git/repo/fun.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ def find_submodule_git_dir(d: PathLike) -> Optional[PathLike]:
151151
# Cygwin creates submodules prefixed with `/cygdrive/...`.
152152
# Cygwin git understands Cygwin paths much better than Windows ones.
153153
# Also the Cygwin tests are assuming Cygwin paths.
154-
path = cygpath(path)
154+
path = cygpath(path, expand_vars=False)
155155
if not osp.isabs(path):
156156
path = osp.normpath(osp.join(osp.dirname(d), path))
157157
return path if is_git_dir(path) else None

‎test/test_clone.py‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,28 @@
2121
import pytest
2222

2323

24+
@pytest.mark.parametrize("clone_method", ["clone", "clone_from"])
25+
@pytest.mark.parametrize("path_type", [str, Path, PathLikeMock])
26+
@pytest.mark.parametrize("name", ["$GITPYTHON_TEST_SECRET", "${GITPYTHON_TEST_SECRET}", "%GITPYTHON_TEST_SECRET%"])
27+
def test_clone_preserves_literal_separate_git_dir(tmp_path, monkeypatch, caplog, clone_method, path_type, name):
28+
monkeypatch.setenv("GITPYTHON_TEST_SECRET", "sensitive-value")
29+
caplog.set_level("DEBUG", logger="git.cmd")
30+
separate_git_dir = tmp_path / name
31+
options = {"separate_git_dir": path_type(str(separate_git_dir)), "allow_unsafe_options": True}
32+
33+
with Repo.init(tmp_path / "source") as source:
34+
if clone_method == "clone":
35+
cloned = source.clone(tmp_path / "clone", **options)
36+
else:
37+
cloned = Repo.clone_from(source.git_dir, tmp_path / "clone", **options)
38+
with cloned:
39+
assert (separate_git_dir / "HEAD").is_file()
40+
assert separate_git_dir.samefile(cloned.git_dir)
41+
42+
assert not (tmp_path / "sensitive-value").exists()
43+
assert "sensitive-value" not in caplog.text
44+
45+
2446
class TestClone(TestBase):
2547
@with_rw_directory
2648
def test_checkout_in_non_empty_dir(self, rw_dir):

‎test/test_submodule.py‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,36 @@ def _patch_git_config(name, value):
5252
yield
5353

5454

55+
@pytest.mark.parametrize(
56+
"name", ["module", "$GITPYTHON_TEST_SECRET", "prefix-${GITPYTHON_TEST_SECRET}-suffix", "%GITPYTHON_TEST_SECRET%"]
57+
)
58+
def test_submodule_update_preserves_literal_name(tmp_path, monkeypatch, caplog, name):
59+
monkeypatch.setenv("GITPYTHON_TEST_SECRET", "sensitive-value")
60+
caplog.set_level("DEBUG", logger="git.cmd")
61+
with git.Repo.init(tmp_path / "source") as source, git.Repo.init(tmp_path / "parent") as parent:
62+
source.git.symbolic_ref("HEAD", "refs/heads/master")
63+
source.index.commit("Initial commit")
64+
with _patch_git_config("protocol.file.allow", "always"):
65+
parent.git.submodule("add", "--name", name, source.working_tree_dir, "module")
66+
parent.index.commit("Add submodule")
67+
68+
with git.Repo.clone_from(parent.working_tree_dir, tmp_path / "clone") as clone:
69+
clone.submodule_update(init=True, recursive=True)
70+
71+
modules_dir = Path(clone.git_dir, "modules")
72+
assert {path.name for path in modules_dir.iterdir()} == {name}
73+
# Exercise relative gitfile conversion on every platform.
74+
with mock.patch.object(Git, "is_cygwin", return_value=True):
75+
resolved_git_dir = find_submodule_git_dir(Path(clone.working_tree_dir, "module", ".git"))
76+
assert resolved_git_dir is not None
77+
assert (modules_dir / name).samefile(resolved_git_dir)
78+
with clone.submodules[0].module() as module:
79+
assert (modules_dir / name).samefile(module.git_dir)
80+
assert module.head.commit == source.head.commit
81+
82+
assert "sensitive-value" not in caplog.text
83+
84+
5585
@pytest.fixture
5686
def movable_submodule(tmp_path):
5787
"""Create a committed local submodule whose logical name stays fixed when moved."""

0 commit comments

Comments
 (0)