Fix git-related commands not working in worktrees

If spack is checked out in a git worktree (see [1]), all git-related
commands fail because the `spack_is_git_repo()`-check is not thorough
enough.

When developing in a feature-branch in a seperate worktree, this is
annoying as all unittests regarding git-related spack commands fail,
cluttering the test results with false-positives.

[1]: https://git-scm.com/docs/git-worktree

Change-Id: I94b573a2c0e058e9ccc169e7ee6561626fbb06fd
This commit is contained in:
Oliver Breitwieser 2019-12-04 16:37:40 +01:00 committed by Peter Scheibel
parent dea5d913db
commit 1cff717ca8
2 changed files with 87 additions and 3 deletions

View File

@ -9,6 +9,7 @@
import re import re
import sys import sys
import argparse import argparse
import ruamel.yaml as yaml
import six import six
@ -16,7 +17,7 @@
from llnl.util.lang import attr_setdefault, index_by from llnl.util.lang import attr_setdefault, index_by
from llnl.util.tty.colify import colify from llnl.util.tty.colify import colify
from llnl.util.tty.color import colorize from llnl.util.tty.color import colorize
from llnl.util.filesystem import working_dir from llnl.util.filesystem import join_path
import spack.config import spack.config
import spack.error import spack.error
@ -26,6 +27,7 @@
import spack.store import spack.store
import spack.util.spack_json as sjson import spack.util.spack_json as sjson
import spack.util.string import spack.util.string
from ruamel.yaml.error import MarkedYAMLError
# cmd has a submodule called "list" so preserve the python list module # cmd has a submodule called "list" so preserve the python list module
@ -433,8 +435,23 @@ def format_list(specs):
def spack_is_git_repo(): def spack_is_git_repo():
"""Ensure that this instance of Spack is a git clone.""" """Ensure that this instance of Spack is a git clone."""
with working_dir(spack.paths.prefix): return is_git_repo(spack.paths.prefix)
return os.path.isdir('.git')
def is_git_repo(path):
dotgit_path = join_path(path, '.git')
if os.path.isdir(dotgit_path):
# we are in a regular git repo
return True
if os.path.isfile(dotgit_path):
# we might be in a git worktree
try:
with open(dotgit_path, "rb") as f:
dotgit_content = yaml.load(f)
return os.path.isdir(dotgit_content.get("gitdir", dotgit_path))
except MarkedYAMLError:
pass
return False
class PythonNameError(spack.error.SpackError): class PythonNameError(spack.error.SpackError):

View File

@ -0,0 +1,67 @@
# Copyright 2013-2020 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from __future__ import print_function
import spack
import pytest
from llnl.util.filesystem import mkdirp
from spack.util.executable import which
from spack.version import ver
git = which("git")
git_required_version = '2.17.0'
def check_git_version():
"""Check if git version is new enough for worktree functionality.
Return True if requirements are met.
The latest required functionality is `worktree remove` which was only added
in 2.17.0.
Refer:
https://github.com/git/git/commit/cc73385cf6c5c229458775bc92e7dbbe24d11611
"""
git_version = ver(git('--version', output=str).lstrip('git version '))
return git_version >= ver(git_required_version)
pytestmark = pytest.mark.skipif(
not git or not check_git_version(),
reason="we need git to test if we are in a git repo"
)
@pytest.fixture(scope="function")
def git_tmp_worktree(tmpdir):
"""Create new worktree in a temporary folder and monkeypatch
spack.paths.prefix to point to it.
"""
worktree_root = str(tmpdir.join("tmp_worktree"))
mkdirp(worktree_root)
git("worktree", "add", "--detach", worktree_root, "HEAD")
yield worktree_root
git("worktree", "remove", "--force", worktree_root)
def test_is_git_repo_in_worktree(git_tmp_worktree):
"""Verify that spack.cmd.spack_is_git_repo() can identify a git repository
in a worktree.
"""
assert spack.cmd.is_git_repo(git_tmp_worktree)
def test_spack_is_git_repo_nongit(tmpdir, monkeypatch):
"""Verify that spack.cmd.spack_is_git_repo() correctly returns False if we
are in a non-git directory.
"""
assert not spack.cmd.is_git_repo(str(tmpdir))