Repository: pypa/pip

Issue:
pip understand @# as empty tag/href in git source and breaks installation
**Environment**

* pip version: 19.3.1
* Python version: python 3.7.3
* OS: Debian 10 (buster) linux kernel 4.19.0-6-amd64

**Description**

When a git url contains a '@#' becomes an empty tag or href, that does not match with any fetched refs or tags and produces a : "  fatal: ambiguous argument '': unknown revision or path not in the working tree." 

When running into requirements.txt, breaks installation at this repo point

**Expected behavior**

Ignore the empty tag/href and continues

**How to Reproduce**

1. Setup requirements.txt with a source having a '@#' . e.g:
`-e git+https://github.com/AlexIoannides/py-package-template.git@#egg=py_pkg`

2. Then run 'pip install -r requirements.txt'. Installations works

3. Then run install again and you get the error ... an error occurs
` fatal: ambiguous argument '': unknown revision or path not in the working tree.`



```
cat requirements.txt 
-e git+https://github.com/AlexIoannides/py-package-template.git@#egg=py_pkg

(j2f) jorge@debian:~/projects/personal/j2f $ pip install -r requirements.txt 
Obtaining py_pkg from git+https://github.com/AlexIoannides/py-package-template.git@#egg=py_pkg (from -r requirements.txt (line 1))
  Cloning https://github.com/AlexIoannides/py-package-template.git to /home/jorge/.virtualenvs/j2f/src/py-pkg
  Running command git clone -q https://github.com/AlexIoannides/py-package-template.git /home/jorge/.virtualenvs/j2f/src/py-pkg
  WARNING: Generating metadata for package py-pkg produced metadata for project name py-package-template. Fix your #egg=py-pkg fragments.
Collecting numpy
  Downloading https://files.pythonhosted.org/packages/9b/af/4fc72f9d38e43b092e91e5b8cb9956d25b2e3ff8c75aed95df5569e4734e/numpy-1.17.4-cp37-cp37m-manylinux1_x86_64.whl (20.0MB)
     |████████████████████████████████| 20.0MB 6.6MB/s 
Collecting requests
  Using cached https://files.pythonhosted.org/packages/51/bd/23c926cd341ea6b7dd0b2a00aba99ae0f828be89d72b2190f27c11d4b7fb/requests-2.22.0-py2.py3-none-any.whl
Collecting idna<2.9,>=2.5
  Using cached https://files.pythonhosted.org/packages/14/2c/cd551d81dbe15200be1cf41cd03869a46fe7226e7450af7a6545bfc474c9/idna-2.8-py2.py3-none-any.whl
Collecting urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1
  Using cached https://files.pythonhosted.org/packages/b4/40/a9837291310ee1ccc242ceb6ebfd9eb21539649f193a7c8c86ba15b98539/urllib3-1.25.7-py2.py3-none-any.whl
Collecting certifi>=2017.4.17
  Using cached https://files.pythonhosted.org/packages/18/b0/8146a4f8dd402f60744fa380bc73ca47303cccf8b9190fd16a827281eac2/certifi-2019.9.11-py2.py3-none-any.whl
Collecting chardet<3.1.0,>=3.0.2
  Using cached https://files.pythonhosted.org/packages/bc/a9/01ffebfb562e4274b6487b4bb1ddec7ca55ec7510b22e4c51f14098443b8/chardet-3.0.4-py2.py3-none-any.whl
Installing collected packages: numpy, idna, urllib3, certifi, chardet, requests, py-package-template
  Running setup.py develop for py-package-template
Successfully installed certifi-2019.9.11 chardet-3.0.4 idna-2.8 numpy-1.17.4 py-package-template requests-2.22.0 urllib3-1.25.7

(j2f) jorge@debian:~/projects/personal/j2f $ pip install -r requirements.txt 
Obtaining py_pkg from git+https://github.com/AlexIoannides/py-package-template.git@#egg=py_pkg (from -r requirements.txt (line 1))
  Updating /home/jorge/.virtualenvs/j2f/src/py-pkg clone
  Running command git fetch -q --tags
  WARNING: Did not find branch or tag '', assuming revision or ref.
  Running command git reset --hard -q ''
  fatal: ambiguous argument '': unknown revision or path not in the working tree.
  Use '--' to separate paths from revisions, like this:
  'git <command> [<revision>...] -- [<file>...]'
ERROR: Command errored out with exit status 128: git reset --hard -q '' Check the logs for full command output.
```



Discussion:
But why would the URL have such a combination? I feel the current behaviour is quite reasonable, and it’s up to the user to fix the URL.
> But why would the URL have such a combination? 

Due a human mistake

> I feel the current behaviour is quite reasonable, and it’s up to the user to fix the URL.

Well, it could be repaired by the user, but the user must know what is happening. The output message is very confusing.
Hi @jsenin, thanks for bringing this up.

If the revision part of the URL is empty then instead of continuing we could abort with an error message like:

> The URL `git+https://github.com/AlexIoannides/py-package-template.git@#egg=py_pkg` (from requirements.txt) has an empty revision (after `@`) which is not supported. Include a revision after `@` or remove `@` from the URL.

Is that what you're looking for?
Yes, this could help a lot 

El 27 de noviembre de 2019 2:11:44 CET, Christopher Hunt <notifications@github.com> escribió:
>Hi @jsenin, thanks for bringing this up.
>
>If the revision part of the URL is empty then instead of continuing we
>could abort with an error message like:
>
>> The URL
>`git+https://github.com/AlexIoannides/py-package-template.git@#egg=py_pkg`
>(from requirements.txt) has an empty revision (after `@`) which is not
>supported. Include a revision after `@` or remove `@` from the URL.
>
>Is that what you're looking for?
>
>-- 
>You are receiving this because you were mentioned.
>Reply to this email directly or view it on GitHub:
>https://github.com/pypa/pip/issues/7402#issuecomment-558883902

-- 
Enviado desde mi dispositivo Android con K-9 Mail. Por favor, disculpa mi brevedad.

Resolving patch:
diff --git a/src/pip/_internal/vcs/versioncontrol.py b/src/pip/_internal/vcs/versioncontrol.py
--- a/src/pip/_internal/vcs/versioncontrol.py
+++ b/src/pip/_internal/vcs/versioncontrol.py
@@ -11,7 +11,7 @@
 from pip._vendor import pkg_resources
 from pip._vendor.six.moves.urllib import parse as urllib_parse
 
-from pip._internal.exceptions import BadCommand
+from pip._internal.exceptions import BadCommand, InstallationError
 from pip._internal.utils.compat import samefile
 from pip._internal.utils.misc import (
     ask_path_exists,
@@ -436,6 +436,12 @@ def get_url_rev_and_auth(cls, url):
         rev = None
         if '@' in path:
             path, rev = path.rsplit('@', 1)
+            if not rev:
+                raise InstallationError(
+                    "The URL {!r} has an empty revision (after @) "
+                    "which is not supported. Include a revision after @ "
+                    "or remove @ from the URL.".format(url)
+                )
         url = urllib_parse.urlunsplit((scheme, netloc, path, query, ''))
         return url, rev, user_pass
 


Test patch:
diff --git a/tests/unit/test_vcs.py b/tests/unit/test_vcs.py
--- a/tests/unit/test_vcs.py
+++ b/tests/unit/test_vcs.py
@@ -5,7 +5,7 @@
 from mock import patch
 from pip._vendor.packaging.version import parse as parse_version
 
-from pip._internal.exceptions import BadCommand
+from pip._internal.exceptions import BadCommand, InstallationError
 from pip._internal.utils.misc import hide_url, hide_value
 from pip._internal.vcs import make_vcs_requirement_url
 from pip._internal.vcs.bazaar import Bazaar
@@ -292,6 +292,21 @@ def test_version_control__get_url_rev_and_auth__missing_plus(url):
     assert 'malformed VCS url' in str(excinfo.value)
 
 
+@pytest.mark.parametrize('url', [
+    # Test a URL with revision part as empty.
+    'git+https://github.com/MyUser/myProject.git@#egg=py_pkg',
+])
+def test_version_control__get_url_rev_and_auth__no_revision(url):
+    """
+    Test passing a URL to VersionControl.get_url_rev_and_auth() with
+    empty revision
+    """
+    with pytest.raises(InstallationError) as excinfo:
+        VersionControl.get_url_rev_and_auth(url)
+
+    assert 'an empty revision (after @)' in str(excinfo.value)
+
+
 @pytest.mark.parametrize('url, expected', [
     # Test http.
     ('bzr+http://bzr.myproject.org/MyProject/trunk/#egg=MyProject',