init: clone the manifest repository, don't fetch it
West init without --mr is leaving the manifest repository in a detached HEAD state. Fixes: #522 The convoluted logic we use to "clone" the manifest repository using init and fetch is only in place to allow using a magic GitHub pull request ref as --manifest-rev. This small benefit is no longer worth the downsides. To fix it, just use 'git clone' instead. This also lets us drop a bunch of extra logic trying to figure out what the remote wants us to use by default. It also avoids getting the now-infamous "Using 'master' as the name for the initial branch." message on the first west init if the user doesn't have init.defaultBranch set. Preserve 'west init --manifest-rev foo' using 'git clone --branch foo'. This works fine with branches or tags, but you'll no longer be able to initialize a workspace from a 'pull/1234/head' GitHub pull request reference or a SHA. Affected users are going to need to do 'west init', then fetch the revision they need before running 'west update'. Unlike other branch related options (like 'git init --initial branch'), we've had 'git clone --branch' since somewhere in the git v1.x days, so it's safe to use unconditionally. Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
This commit is contained in:
parent
49b8695f68
commit
e283d9986f
|
@ -9,7 +9,7 @@ import argparse
|
|||
from functools import partial
|
||||
import logging
|
||||
import os
|
||||
from os.path import abspath, relpath, exists
|
||||
from os.path import abspath, relpath
|
||||
from pathlib import PurePath, Path
|
||||
import multiprocessing
|
||||
import shutil
|
||||
|
@ -239,7 +239,11 @@ With neither, -m {MANIFEST_URL_DEFAULT} is assumed.
|
|||
log.banner('Initializing in', topdir)
|
||||
|
||||
manifest_url = args.manifest_url or MANIFEST_URL_DEFAULT
|
||||
manifest_rev = args.manifest_rev or self.get_head_branch(manifest_url)
|
||||
if args.manifest_rev:
|
||||
# This works with tags, too.
|
||||
branch_opt = ['--branch', args.manifest_rev]
|
||||
else:
|
||||
branch_opt = []
|
||||
west_dir = topdir / WEST_DIR
|
||||
|
||||
try:
|
||||
|
@ -257,7 +261,12 @@ With neither, -m {MANIFEST_URL_DEFAULT} is assumed.
|
|||
log.dbg('removing existing temporary manifest directory', tempdir)
|
||||
shutil.rmtree(tempdir)
|
||||
try:
|
||||
self.clone_manifest(manifest_url, manifest_rev, os.fspath(tempdir))
|
||||
log.small_banner(
|
||||
f'Cloning manifest repository from {manifest_url}' +
|
||||
(f', rev. {args.manifest_rev}' if args.manifest_rev else ''))
|
||||
|
||||
self.check_call(['git', 'clone'] + branch_opt +
|
||||
[manifest_url, os.fspath(tempdir)])
|
||||
except subprocess.CalledProcessError:
|
||||
shutil.rmtree(tempdir, ignore_errors=True)
|
||||
raise
|
||||
|
@ -268,8 +277,9 @@ With neither, -m {MANIFEST_URL_DEFAULT} is assumed.
|
|||
if not temp_manifest.is_file():
|
||||
log.die(f'can\'t init: no {temp_manifest_filename} found in '
|
||||
f'{tempdir}\n'
|
||||
f' Hint: check --manifest-url={manifest_url} and '
|
||||
f'--manifest-rev={manifest_rev}\n'
|
||||
f' Hint: check --manifest-url={manifest_url}' +
|
||||
(f' and --manifest-rev={args.manifest_rev}'
|
||||
if args.manifest_rev else '') +
|
||||
f' You may need to remove {west_dir} before retrying.')
|
||||
|
||||
# Parse the manifest to get the manifest path, if it declares one.
|
||||
|
@ -314,85 +324,6 @@ With neither, -m {MANIFEST_URL_DEFAULT} is assumed.
|
|||
except Exception as e:
|
||||
log.die(f"Can't create {directory}: {e}")
|
||||
|
||||
def get_head_branch(self, url: str) -> str:
|
||||
# Get the branch which url's HEAD points to. Errors out if it
|
||||
# can't, prints a banner if it can.
|
||||
|
||||
if self.git_version_info < (2, 8, 0):
|
||||
# This recipe requires git 2.8.0 or later. Fall back
|
||||
# if we're running on something that's too old.
|
||||
return 'master'
|
||||
|
||||
err_msg = (f'failed getting the default branch from {url}; '
|
||||
'please provide the --manifest-rev option')
|
||||
|
||||
# The '--quiet' option disables printing the URL to stderr.
|
||||
try:
|
||||
output = self.check_output(
|
||||
('git', 'ls-remote', '--quiet', '--symref', url, 'HEAD')
|
||||
).decode('utf-8')
|
||||
except subprocess.CalledProcessError:
|
||||
log.die(err_msg)
|
||||
|
||||
for line in output.splitlines():
|
||||
if not line.startswith('ref: '):
|
||||
continue
|
||||
# The output looks like this:
|
||||
#
|
||||
# ref: refs/heads/foo HEAD
|
||||
# 6145ab537fcb3adc3ee77db5f5f95e661f1e91e6 HEAD
|
||||
#
|
||||
# So this is the 'ref: ...' case.
|
||||
#
|
||||
# Per git-check-ref-format(1), references can't have tabs
|
||||
# in them, so this doesn't have any weird edge cases.
|
||||
without_ref = line[len('ref: '):]
|
||||
if not without_ref:
|
||||
continue
|
||||
ret = without_ref.split('\t')[0]
|
||||
log.small_banner('no --manifest-rev was given; '
|
||||
f"using remote's default branch: {ret}")
|
||||
return ret
|
||||
|
||||
log.die(err_msg)
|
||||
|
||||
def clone_manifest(self, url: str, rev: str, dest: str,
|
||||
exist_ok=False) -> None:
|
||||
log.small_banner(f'Cloning manifest repository from {url}, rev. {rev}')
|
||||
if not exist_ok and exists(dest):
|
||||
log.die(f'refusing to clone into existing location {dest}')
|
||||
|
||||
self.check_call(('git', 'init', dest))
|
||||
self.check_call(('git', 'remote', 'add', 'origin', '--', url),
|
||||
cwd=dest)
|
||||
maybe_sha = _maybe_sha(rev)
|
||||
if maybe_sha:
|
||||
# Fetch the ref-space and hope the SHA is contained in
|
||||
# that ref-space
|
||||
self.check_call(('git', 'fetch', 'origin', '--tags',
|
||||
'--', 'refs/heads/*:refs/remotes/origin/*'),
|
||||
cwd=dest)
|
||||
else:
|
||||
# Fetch the ref-space similar to git clone plus the ref
|
||||
# given by user. Redundancy is ok, for example if the user
|
||||
# specifies 'heads/my-branch'. This allows users to specify:
|
||||
# pull/<no>/head for pull requests
|
||||
self.check_call(('git', 'fetch', 'origin', '--tags', '--',
|
||||
rev, 'refs/heads/*:refs/remotes/origin/*'),
|
||||
cwd=dest)
|
||||
|
||||
try:
|
||||
# Using show-ref to determine if rev is available in local repo.
|
||||
self.check_call(('git', 'show-ref', '--', rev), cwd=dest)
|
||||
local_rev = True
|
||||
except subprocess.CalledProcessError:
|
||||
local_rev = False
|
||||
|
||||
if local_rev or maybe_sha:
|
||||
self.check_call(('git', 'checkout', rev), cwd=dest)
|
||||
else:
|
||||
self.check_call(('git', 'checkout', 'FETCH_HEAD'), cwd=dest)
|
||||
|
||||
class List(_ProjectCommand):
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
|
|
Loading…
Reference in New Issue