Skip to content

Commit 7e49dca

Browse files
authored
Require the revision to be percent-encoded in VSC URLs (#13407)
This changes how the revision part is handled in version control URLs. When a branch name contains special characters such as '@' or '#', they must be percent-encoded in URLs. This is backwards incompatible, but the fallout should be negligible. In most cases, it will result in an one-time error. It's extremely unlikely that people have percent-encoding in their branch names (which could be misinterpreted). It is also extremely unlikely that someone has both branches with and without percent-escaping names that could cause an unnoticed behavioural change.
1 parent e351bbb commit 7e49dca

File tree

3 files changed

+16
-1
lines changed

3 files changed

+16
-1
lines changed

news/13407.bugfix.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Revisions in version control URLs now must be percent-encoded.
2+
For example, use ``git+https://example.com/repo.git@issue%231`` to specify the branch ``issue#1``.
3+
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.

src/pip/_internal/vcs/versioncontrol.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,9 @@ def make_vcs_requirement_url(
6262
repo_url: the remote VCS url, with any needed VCS prefix (e.g. "git+").
6363
project_name: the (unescaped) project name.
6464
"""
65+
quoted_rev = urllib.parse.quote(rev, "/")
6566
egg_project_name = project_name.replace("-", "_")
66-
req = f"{repo_url}@{rev}#egg={egg_project_name}"
67+
req = f"{repo_url}@{quoted_rev}#egg={egg_project_name}"
6768
if subdir:
6869
req += f"&subdirectory={subdir}"
6970

@@ -397,6 +398,7 @@ def get_url_rev_and_auth(cls, url: str) -> tuple[str, str | None, AuthInfo]:
397398
"which is not supported. Include a revision after @ "
398399
"or remove @ from the URL."
399400
)
401+
rev = urllib.parse.unquote(rev)
400402
url = urllib.parse.urlunsplit((scheme, netloc, path, query, ""))
401403
return url, rev, user_pass
402404

tests/unit/test_vcs.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ def test_ensure_svn_available() -> None:
5252
("git+https://example.com/pkg", "dev", "zope-interface"),
5353
"git+https://example.com/pkg@dev#egg=zope_interface",
5454
),
55+
# Test a revision with special characters.
56+
(
57+
("git+https://example.com/pkg", "dev@1#2", "myproj"),
58+
"git+https://example.com/pkg@dev%401%232#egg=myproj",
59+
),
5560
],
5661
)
5762
def test_make_vcs_requirement_url(args: tuple[Any, ...], expected: str) -> None:
@@ -394,6 +399,11 @@ def test_git__get_url_rev__idempotent() -> None:
394399
"svn+https://svn.example.com/My+Project",
395400
("https://svn.example.com/My+Project", None, (None, None)),
396401
),
402+
# Test percent-encoded characters in revision.
403+
(
404+
"svn+https://svn.example.com/MyProject@dev%401%232",
405+
("https://svn.example.com/MyProject", "dev@1#2", (None, None)),
406+
),
397407
],
398408
)
399409
def test_version_control__get_url_rev_and_auth(

0 commit comments

Comments
 (0)