From f6444ea7c2e68d5e39c669b817180d3042743876 Mon Sep 17 00:00:00 2001 From: Kaz Nishimura Date: Wed, 28 May 2025 12:34:43 +0900 Subject: [PATCH 1/8] Unquote the revision extracted from URL --- src/pip/_internal/vcs/versioncontrol.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pip/_internal/vcs/versioncontrol.py b/src/pip/_internal/vcs/versioncontrol.py index 4e91ccd4c4c..e52f11a122f 100644 --- a/src/pip/_internal/vcs/versioncontrol.py +++ b/src/pip/_internal/vcs/versioncontrol.py @@ -397,6 +397,7 @@ def get_url_rev_and_auth(cls, url: str) -> tuple[str, str | None, AuthInfo]: "which is not supported. Include a revision after @ " "or remove @ from the URL." ) + rev = urllib.parse.unquote(rev) url = urllib.parse.urlunsplit((scheme, netloc, path, query, "")) return url, rev, user_pass From 917934fd3d8f1f458e069617a34cc98a68f86f90 Mon Sep 17 00:00:00 2001 From: Kaz Nishimura Date: Wed, 28 May 2025 15:08:31 +0900 Subject: [PATCH 2/8] Quote the revision in new URLs --- src/pip/_internal/vcs/versioncontrol.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pip/_internal/vcs/versioncontrol.py b/src/pip/_internal/vcs/versioncontrol.py index e52f11a122f..9078428a1b7 100644 --- a/src/pip/_internal/vcs/versioncontrol.py +++ b/src/pip/_internal/vcs/versioncontrol.py @@ -62,8 +62,9 @@ def make_vcs_requirement_url( repo_url: the remote VCS url, with any needed VCS prefix (e.g. "git+"). project_name: the (unescaped) project name. """ + quoted_rev = urllib.parse.quote(rev) egg_project_name = project_name.replace("-", "_") - req = f"{repo_url}@{rev}#egg={egg_project_name}" + req = f"{repo_url}@{quoted_rev}#egg={egg_project_name}" if subdir: req += f"&subdirectory={subdir}" From 4a22981decd34b9260395486b523c30ebc02c960 Mon Sep 17 00:00:00 2001 From: Kaz Nishimura Date: Wed, 28 May 2025 18:27:00 +0900 Subject: [PATCH 3/8] Add test parameter. --- tests/unit/test_vcs.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/unit/test_vcs.py b/tests/unit/test_vcs.py index 3726591ea4c..978d9d51731 100644 --- a/tests/unit/test_vcs.py +++ b/tests/unit/test_vcs.py @@ -51,6 +51,11 @@ def test_ensure_svn_available() -> None: ("git+https://example.com/pkg", "dev", "zope-interface"), "git+https://example.com/pkg@dev#egg=zope_interface", ), + # Test a revision with special characters. + ( + ("git+https://example.com/pkg", "dev@1#2", "myproj"), + "git+https://example.com/pkg@dev%401%232#egg=myproj", + ), ], ) def test_make_vcs_requirement_url(args: tuple[Any, ...], expected: str) -> None: From 5494a4a2391cb3544e441c2333201074b3c43706 Mon Sep 17 00:00:00 2001 From: Kaz Nishimura Date: Wed, 28 May 2025 18:46:31 +0900 Subject: [PATCH 4/8] Add another test parameter. --- tests/unit/test_vcs.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/unit/test_vcs.py b/tests/unit/test_vcs.py index 978d9d51731..0d83039a713 100644 --- a/tests/unit/test_vcs.py +++ b/tests/unit/test_vcs.py @@ -397,6 +397,11 @@ def test_git__get_url_rev__idempotent() -> None: "svn+https://svn.example.com/My+Project", ("https://svn.example.com/My+Project", None, (None, None)), ), + # Test percent-encoded characters in revision. + ( + "svn+https://svn.example.com/MyProject@dev%401%232", + ("https://svn.example.com/MyProject", "dev@1#2", (None, None)), + ), ], ) def test_version_control__get_url_rev_and_auth( From 2a957a872be82ab3f6c20e28c28604f3975b1818 Mon Sep 17 00:00:00 2001 From: Kaz Nishimura Date: Thu, 29 May 2025 12:40:02 +0900 Subject: [PATCH 5/8] Add a news file --- news/13407.bugfix.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 news/13407.bugfix.rst diff --git a/news/13407.bugfix.rst b/news/13407.bugfix.rst new file mode 100644 index 00000000000..098660a129b --- /dev/null +++ b/news/13407.bugfix.rst @@ -0,0 +1 @@ +Make the revision part percent-encoded in version control URLs From 454315c7230c335c2dbafabd979b350e88007461 Mon Sep 17 00:00:00 2001 From: Kaz Nishimura Date: Sun, 8 Jun 2025 20:24:20 +0900 Subject: [PATCH 6/8] =?UTF-8?q?13407.bugfix.rst=20=E3=81=AE=E6=9B=B4?= =?UTF-8?q?=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- news/13407.bugfix.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/news/13407.bugfix.rst b/news/13407.bugfix.rst index 098660a129b..7ff29fa4171 100644 --- a/news/13407.bugfix.rst +++ b/news/13407.bugfix.rst @@ -1 +1,2 @@ -Make the revision part percent-encoded in version control URLs +Make the revision part percent-encoded in version control URLs, +e.g. ``git+https://example.com/repo.git@issue%231`` to specify branch ``issue#1``. From 80588e81b4e4b30c2b8cf0e346a03a211f8e61d3 Mon Sep 17 00:00:00 2001 From: Kaz Nishimura Date: Sun, 29 Jun 2025 12:45:30 +0900 Subject: [PATCH 7/8] Update documentation to clarify percent-encoding for version control URLs --- news/13407.bugfix.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/news/13407.bugfix.rst b/news/13407.bugfix.rst index 7ff29fa4171..0b3d7721306 100644 --- a/news/13407.bugfix.rst +++ b/news/13407.bugfix.rst @@ -1,2 +1,3 @@ -Make the revision part percent-encoded in version control URLs, -e.g. ``git+https://example.com/repo.git@issue%231`` to specify branch ``issue#1``. +Modified to percent-encode the revision part in version control URLs. +For example, use ``git+https://example.com/repo.git@issue%231`` to specify the branch ``issue#1``. +If you previously used a branch name containing a ``%`` character in a version control URL, you now need to replace it with ``%25`` to ensure correct percent-encoding. From c887642cc3f74a411265bd6e6ce3a3b258af328b Mon Sep 17 00:00:00 2001 From: Kaz Nishimura Date: Mon, 4 Aug 2025 14:57:15 +0900 Subject: [PATCH 8/8] Specify safe characters explicitly. --- src/pip/_internal/vcs/versioncontrol.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pip/_internal/vcs/versioncontrol.py b/src/pip/_internal/vcs/versioncontrol.py index 9078428a1b7..95cefb49e24 100644 --- a/src/pip/_internal/vcs/versioncontrol.py +++ b/src/pip/_internal/vcs/versioncontrol.py @@ -62,7 +62,7 @@ def make_vcs_requirement_url( repo_url: the remote VCS url, with any needed VCS prefix (e.g. "git+"). project_name: the (unescaped) project name. """ - quoted_rev = urllib.parse.quote(rev) + quoted_rev = urllib.parse.quote(rev, "/") egg_project_name = project_name.replace("-", "_") req = f"{repo_url}@{quoted_rev}#egg={egg_project_name}" if subdir: