From 1cff717ca86c031c912cf22a7e315d822ab83e46 Mon Sep 17 00:00:00 2001 From: Oliver Breitwieser Date: Wed, 4 Dec 2019 16:37:40 +0100 Subject: 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 --- lib/spack/spack/cmd/__init__.py | 23 +++++++++-- lib/spack/spack/test/cmd/is_git_repo.py | 67 +++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 lib/spack/spack/test/cmd/is_git_repo.py (limited to 'lib') diff --git a/lib/spack/spack/cmd/__init__.py b/lib/spack/spack/cmd/__init__.py index 594b5e3a8e..5172bdee07 100644 --- a/lib/spack/spack/cmd/__init__.py +++ b/lib/spack/spack/cmd/__init__.py @@ -9,6 +9,7 @@ import os import re import sys import argparse +import ruamel.yaml as yaml import six @@ -16,7 +17,7 @@ import llnl.util.tty as tty from llnl.util.lang import attr_setdefault, index_by from llnl.util.tty.colify import colify 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.error @@ -26,6 +27,7 @@ import spack.spec import spack.store import spack.util.spack_json as sjson import spack.util.string +from ruamel.yaml.error import MarkedYAMLError # cmd has a submodule called "list" so preserve the python list module @@ -433,8 +435,23 @@ def display_specs(specs, args=None, **kwargs): def spack_is_git_repo(): """Ensure that this instance of Spack is a git clone.""" - with working_dir(spack.paths.prefix): - return os.path.isdir('.git') + return is_git_repo(spack.paths.prefix) + + +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): diff --git a/lib/spack/spack/test/cmd/is_git_repo.py b/lib/spack/spack/test/cmd/is_git_repo.py new file mode 100644 index 0000000000..724925e5e4 --- /dev/null +++ b/lib/spack/spack/test/cmd/is_git_repo.py @@ -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)) -- cgit v1.2.3-70-g09d2