From 892dd4d97fb2d476b24b280279cbdadcfeb0d19c Mon Sep 17 00:00:00 2001 From: Gregory Becker Date: Fri, 22 Jan 2021 18:16:30 -0800 Subject: [PATCH] add `spack checkout` command --- lib/spack/spack/cmd/checkout.py | 78 +++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 lib/spack/spack/cmd/checkout.py diff --git a/lib/spack/spack/cmd/checkout.py b/lib/spack/spack/cmd/checkout.py new file mode 100644 index 00000000000..c368415ec50 --- /dev/null +++ b/lib/spack/spack/cmd/checkout.py @@ -0,0 +1,78 @@ +# Copyright 2013-2021 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) + +import sys + +import llnl.util.tty as tty +from spack.util.executable import ProcessError, which + +_SPACK_UPSTREAM = 'https://github.com/spack/spack' + +description = "update the spack prefix to a git ref" +section = "admin" +level = "long" + +git = None + +def setup_parser(subparser): + subparser.add_argument( + '-r', '--remote', action='store', default='origin', + help="name of the git remote from which to fetch") + subparser.add_argument( + '--url', action='store', default=None, + help="url to use if the remote does not already exist") + subparser.add_argument( + 'ref', help="git reference to checkout") + + +def fetch_remote(remote, url): + # Ensure we have the appropriate remote configured + remotes = git('remote', output=str, errror=str).split('\n') + if remote in remotes: + remote_url = git('remote', 'get-url', remote, output=str, error=str) + remote_url = remote_url.strip('\n') + if url and remote_url != url: + msg = "Git url %s does not match given url %s" % (remote_url, url) + msg += " for remote '%s'. Either use the git url or" % remote + msg += " specify a new remote name for the new url." + tty.die(msg) + elif not url: + msg = "Spack requires url to checkout from unknown remote %s" % remote + tty.die(msg) + else: + git('remote', 'add', remote, url) + git('fetch', remote) + + +def known_commit_or_tag(ref): + # No need to fetch for tags and commits if we have the ref already + # Fetch on other types rather than failing here because a tree ref could + # be ambiguous with a commit ref after fetching + ref_type = git('cat-file', '-t', ref, + output=str, error=str, fail_on_error=False).strip('\n ') + return ref_type in ('commit', 'tag') + + +def checkout(parser, args): + deployment_required_args = {'remote': 'origin'} + deployment.setup_deployment_args('checkout', args, + deployment_required_args) + + remote = args.remote or origin + url = args.url + ref = args.ref + + global git # make git available to called methods + git = which('git', required=True) + + # Always fetch branches + branches = map(lambda b: b.strip('* '), + git('branch', output=str, error=str).split('\n')) + if ref in branches or not known_commit_or_tag(ref): + fetch_remote(remote, url) + + # For branches, ensure we're getting the version from the correct remote + full_ref = '%s/%s' % (remote, ref) if ref in branches else ref + git('checkout', full_ref)