Repository: spack/spack

Issue:
Problem with git versions
### Steps to reproduce

It seems that #35681 broke something about git versions

```
spack spec -I libtree@git.master
```

### Error message

```console
$ spack spec -I libtree@git.master
==> Error: object of type 'NoneType' has no len()
```

```console
Traceback (most recent call last):
  File "/project/csstaff/ialberto/opt/spack-daint/bin/spack", line 54, in <module>
    sys.exit(main())
  File "/project/csstaff/ialberto/opt/spack-daint/lib/spack/spack_installable/main.py", line 37, in main
    sys.exit(spack.main.main(argv))
  File "/project/csstaff/ialberto/opt/spack-daint/lib/spack/spack/main.py", line 1004, in main
    return _main(argv)
  File "/project/csstaff/ialberto/opt/spack-daint/lib/spack/spack/main.py", line 959, in _main
    return finish_parse_and_run(parser, cmd_name, env_format_error)
  File "/project/csstaff/ialberto/opt/spack-daint/lib/spack/spack/main.py", line 987, in finish_parse_and_run
    return _invoke_command(command, parser, args, unknown)
  File "/project/csstaff/ialberto/opt/spack-daint/lib/spack/spack/main.py", line 636, in _invoke_command
    return_val = command(parser, args)
  File "/project/csstaff/ialberto/opt/spack-daint/lib/spack/spack/cmd/spec.py", line 103, in spec
    concretized_specs = spack.cmd.parse_specs(args.specs, concretize=True)
  File "/project/csstaff/ialberto/opt/spack-daint/lib/spack/spack/cmd/__init__.py", line 222, in parse_specs
    spec.concretize(tests=tests)  # implies normalize
  File "/project/csstaff/ialberto/opt/spack-daint/lib/spack/spack/spec.py", line 2821, in concretize
    self._new_concretize(tests)
  File "/project/csstaff/ialberto/opt/spack-daint/lib/spack/spack/spec.py", line 2796, in _new_concretize
    result = solver.solve([self], tests=tests)
  File "/project/csstaff/ialberto/opt/spack-daint/lib/spack/spack/solver/asp.py", line 2539, in solve
    result, _, _ = self.driver.solve(setup, specs, reuse=reusable_specs, output=output)
  File "/project/csstaff/ialberto/opt/spack-daint/lib/spack/spack/solver/asp.py", line 686, in solve
    setup.setup(self, specs, reuse=reuse)
  File "/project/csstaff/ialberto/opt/spack-daint/lib/spack/spack/solver/asp.py", line 2137, in setup
    self.define_version_constraints()
  File "/project/csstaff/ialberto/opt/spack-daint/lib/spack/spack/solver/asp.py", line 1881, in define_version_constraints
    v for v in sorted(self.possible_versions[pkg_name]) if v.intersects(versions)
  File "/project/csstaff/ialberto/opt/spack-daint/lib/spack/spack/solver/asp.py", line 1881, in <listcomp>
    v for v in sorted(self.possible_versions[pkg_name]) if v.intersects(versions)
  File "/project/csstaff/ialberto/opt/spack-daint/lib/spack/spack/version.py", line 111, in coercing_method
    return getattr(ca, method.__name__)(cb, *args, **kwargs)
  File "/project/csstaff/ialberto/opt/spack-daint/lib/spack/spack/version.py", line 1059, in intersects
    return self.overlaps(other)
  File "/project/csstaff/ialberto/opt/spack-daint/lib/spack/spack/version.py", line 108, in coercing_method
    return method(a, b, *args, **kwargs)
  File "/project/csstaff/ialberto/opt/spack-daint/lib/spack/spack/version.py", line 1050, in overlaps
    if self[s].overlaps(other[o]):
  File "/project/csstaff/ialberto/opt/spack-daint/lib/spack/spack/version.py", line 488, in overlaps
    return self.intersects(other)
  File "/project/csstaff/ialberto/opt/spack-daint/lib/spack/spack/version.py", line 111, in coercing_method
    return getattr(ca, method.__name__)(cb, *args, **kwargs)
  File "/project/csstaff/ialberto/opt/spack-daint/lib/spack/spack/version.py", line 108, in coercing_method
    return method(a, b, *args, **kwargs)
  File "/project/csstaff/ialberto/opt/spack-daint/lib/spack/spack/version.py", line 619, in intersects
    n = min(len(v1), len(v2))
TypeError: object of type 'NoneType' has no len()
```

### Information on your system

Problem seems to start from PR #35681

* **Spack:** 0.20.0.dev0 (99893a64754047f1ad539509fb5b79b780d7477f)
* **Python:** 3.6.15
* **Platform:** cray-sles15-haswell
* **Concretizer:** clingo

### General information

- [X] I have run `spack debug report` and reported the version of Spack/Python/Platform
- [X] I have searched the issues of this repo and believe this is not a duplicate
- [X] I have run the failing commands in debug mode and reported the output


Discussion:
Reproduced as in the description.
A smaller reproducer:
```console
$ spack python -c 'from spack.spec import Spec; s = Spec("libtree@git.master"); s.intersects("libtree@1.1.0")'
Traceback (most recent call last):
  File "/usr/lib/python3.8/code.py", line 90, in runcode
    exec(code, self.locals)
  File "<input>", line 1, in <module>
  File "/home/culpo/github/spack/lib/spack/spack/spec.py", line 3553, in intersects
    if not self.versions.intersects(other.versions):
  File "/home/culpo/github/spack/lib/spack/spack/version.py", line 1059, in intersects
    return self.overlaps(other)
  File "/home/culpo/github/spack/lib/spack/spack/version.py", line 108, in coercing_method
    return method(a, b, *args, **kwargs)
  File "/home/culpo/github/spack/lib/spack/spack/version.py", line 1050, in overlaps
    if self[s].overlaps(other[o]):
  File "/home/culpo/github/spack/lib/spack/spack/version.py", line 488, in overlaps
    return self.intersects(other)
  File "/home/culpo/github/spack/lib/spack/spack/version.py", line 111, in coercing_method
    return getattr(ca, method.__name__)(cb, *args, **kwargs)
  File "/home/culpo/github/spack/lib/spack/spack/version.py", line 108, in coercing_method
    return method(a, b, *args, **kwargs)
  File "/home/culpo/github/spack/lib/spack/spack/version.py", line 619, in intersects
    n = min(len(v1), len(v2))
TypeError: object of type 'NoneType' has no len()

```
The issue seems to be in the code that downloads the repo and tries to infer a "reference version". A workaround is to supply the reference version explicitly:
```console
$ spack spec -I libtree@git.master=master
```
Lowering the impact since there is a workaround
The issue faced by the user seems to be solved in #35057 , in the sense that the code path taken to infer certain properties for clingo is different. The underlying bug in https://github.com/spack/spack/issues/36134#issuecomment-1471614594 instead stays also with that PR.

Resolving patch:
diff --git a/lib/spack/spack/solver/asp.py b/lib/spack/spack/solver/asp.py
--- a/lib/spack/spack/solver/asp.py
+++ b/lib/spack/spack/solver/asp.py
@@ -1880,8 +1880,9 @@ def define_version_constraints(self):
         """Define what version_satisfies(...) means in ASP logic."""
         for pkg_name, versions in sorted(self.version_constraints):
             # version must be *one* of the ones the spec allows.
+            # Also, "possible versions" contain only concrete versions, so satisfies is appropriate
             allowed_versions = [
-                v for v in sorted(self.possible_versions[pkg_name]) if v.intersects(versions)
+                v for v in sorted(self.possible_versions[pkg_name]) if v.satisfies(versions)
             ]
 
             # This is needed to account for a variable number of


Test patch:
diff --git a/lib/spack/spack/test/versions.py b/lib/spack/spack/test/versions.py
--- a/lib/spack/spack/test/versions.py
+++ b/lib/spack/spack/test/versions.py
@@ -771,3 +771,40 @@ def test_version_intersects_satisfies_semantic(lhs_str, rhs_str, expected):
     assert lhs.intersects(rhs) is rhs.intersects(lhs)
     assert lhs.satisfies(rhs) is lhs_sat_rhs
     assert rhs.satisfies(lhs) is rhs_sat_lhs
+
+
+@pytest.mark.parametrize(
+    "spec_str,tested_intersects,tested_satisfies",
+    [
+        (
+            "git-test-commit@git.1.x",
+            [("@:2", True), ("@:1", True), ("@:0", False), ("@1.3:", False)],
+            [("@:2", True), ("@:1", True), ("@:0", False), ("@1.3:", False)],
+        ),
+        (
+            "git-test-commit@git.v2.0",
+            [("@:2", True), ("@:1", False), ("@:0", False), ("@1.3:", True)],
+            [("@:2", True), ("@:1", False), ("@:0", False), ("@1.3:", True)],
+        ),
+    ],
+)
+@pytest.mark.skipif(sys.platform == "win32", reason="Not supported on Windows (yet)")
+def test_git_versions_without_explicit_reference(
+    spec_str,
+    tested_intersects,
+    tested_satisfies,
+    mock_git_version_info,
+    mock_packages,
+    monkeypatch,
+):
+    repo_path, filename, commits = mock_git_version_info
+    monkeypatch.setattr(
+        spack.package_base.PackageBase, "git", "file://%s" % repo_path, raising=False
+    )
+    spec = spack.spec.Spec(spec_str)
+
+    for test_str, expected in tested_intersects:
+        assert spec.intersects(test_str) is expected, test_str
+
+    for test_str, expected in tested_intersects:
+        assert spec.intersects(test_str) is expected, test_str