From 89bbf6168680a41af2367a30c8b7e42caf81cdb9 Mon Sep 17 00:00:00 2001 From: Dirk Thomas Date: Wed, 21 Apr 2021 22:47:00 -0700 Subject: [PATCH] support git version starting with heads/ or tags/ (#171) * support git version starting with heads/ or tags/ * use tags/ in tests * use heads/ in tests * avoid heads/ ending up in detached state * use file:// url --- test/import.txt | 2 +- test/import_shallow.txt | 2 +- test/list.repos | 4 +- test/test_commands.py | 3 +- test/validate.txt | 4 +- vcstool/clients/git.py | 131 ++++++++++++++++++++++++++++------------ 6 files changed, 101 insertions(+), 45 deletions(-) diff --git a/test/import.txt b/test/import.txt index 846d4e26..78725ee8 100644 --- a/test/import.txt +++ b/test/import.txt @@ -25,7 +25,7 @@ Downloaded tarball from 'https://github.com/dirk-thomas/vcstool/archive/377d5b3d Downloaded zipfile from 'https://github.com/dirk-thomas/vcstool/archive/377d5b3d03c212f015cc832fdb368f4534d0d583.zip' and unpacked it === ./immutable/tag (git) === Cloning into '.'... -Note: switching to '0.1.27'. +Note: switching to 'tags/0.1.27'. You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this diff --git a/test/import_shallow.txt b/test/import_shallow.txt index f9785186..b17ec473 100644 --- a/test/import_shallow.txt +++ b/test/import_shallow.txt @@ -31,7 +31,7 @@ Initialized empty Git repository in ./immutable/tag/.git/ From https://github.com/dirk-thomas/vcstool * [new tag] 0.1.27 -> 0.1.27 -Note: switching to '0.1.27'. +Note: switching to 'tags/0.1.27'. You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this diff --git a/test/list.repos b/test/list.repos index 04a88125..d9211829 100644 --- a/test/list.repos +++ b/test/list.repos @@ -14,11 +14,11 @@ repositories: immutable/tag: type: git url: https://github.com/dirk-thomas/vcstool.git - version: 0.1.27 + version: tags/0.1.27 vcstool: type: git url: https://github.com/dirk-thomas/vcstool.git - version: master + version: heads/master without_version: type: git url: https://github.com/dirk-thomas/vcstool.git diff --git a/test/test_commands.py b/test/test_commands.py index 5cc0f84d..ebf82b91 100644 --- a/test/test_commands.py +++ b/test/test_commands.py @@ -10,8 +10,7 @@ from vcstool.util import rmtree # noqa: E402 REPOS_FILE = os.path.join(os.path.dirname(__file__), 'list.repos') -REPOS_FILE_URL = \ - 'https://raw.githubusercontent.com/dirk-thomas/vcstool/master/test/list.repos' # noqa: E501 +REPOS_FILE_URL = 'file://' + REPOS_FILE REPOS2_FILE = os.path.join(os.path.dirname(__file__), 'list2.repos') TEST_WORKSPACE = os.path.join( os.path.dirname(os.path.dirname(__file__)), 'test_workspace') diff --git a/test/validate.txt b/test/validate.txt index 1ccf213b..7235b55c 100644 --- a/test/validate.txt +++ b/test/validate.txt @@ -6,8 +6,8 @@ Tarball url 'https://github.com/dirk-thomas/vcstool/archive/377d5b3d03c212f015cc === immutable/hash_zip (zip) === Zip url 'https://github.com/dirk-thomas/vcstool/archive/377d5b3d03c212f015cc832fdb368f4534d0d583.zip' exists === immutable/tag (git) === -Found git repository 'https://github.com/dirk-thomas/vcstool.git' with ref '0.1.27' +Found git repository 'https://github.com/dirk-thomas/vcstool.git' with tag '0.1.27' === vcstool (git) === -Found git repository 'https://github.com/dirk-thomas/vcstool.git' with ref 'master' +Found git repository 'https://github.com/dirk-thomas/vcstool.git' with branch 'master' === without_version (git) === Found git repository 'https://github.com/dirk-thomas/vcstool.git' with default branch diff --git a/vcstool/clients/git.py b/vcstool/clients/git.py index eb2db6f5..ae1ae8bb 100644 --- a/vcstool/clients/git.py +++ b/vcstool/clients/git.py @@ -314,7 +314,7 @@ def import_(self, command): # fetch updates for existing repo cmd_fetch = [GitClient._executable, 'fetch', remote] if command.shallow: - result_version_type = self._check_version_type( + result_version_type, version_name = self._check_version_type( command.url, checkout_version) if result_version_type['returncode']: return result_version_type @@ -322,46 +322,63 @@ def import_(self, command): if version_type == 'branch': cmd_fetch.append( 'refs/heads/%s:refs/remotes/%s/%s' % - (checkout_version, remote, checkout_version)) + (version_name, remote, version_name)) elif version_type == 'hash': cmd_fetch.append(checkout_version) elif version_type == 'tag': cmd_fetch.append( '+refs/tags/%s:refs/tags/%s' % - (checkout_version, checkout_version)) + (version_name, version_name)) else: assert False cmd_fetch += ['--depth', '1'] + else: + version_type = None result_fetch = self._run_command(cmd_fetch, retry=command.retry) if result_fetch['returncode']: return result_fetch cmd = result_fetch['cmd'] output = result_fetch['output'] + if not command.shallow and checkout_version is not None: + if checkout_version.startswith('heads/'): + version_name = checkout_version[6:] + version_type = 'branch' + elif checkout_version.startswith('tags/'): + version_name = checkout_version[5:] + version_type = 'tag' + else: + version_type = None # ensure that a tracking branch exists which can be checked out - if command.shallow and version_type == 'branch': + if version_type == 'branch': cmd_show_ref = [ GitClient._executable, 'show-ref', - 'refs/heads/%s' % checkout_version] + 'refs/heads/%s' % version_name] result_show_ref = self._run_command(cmd_show_ref) if result_show_ref['returncode']: + if not command.shallow: + result_show_ref['output'] = \ + "Could not find branch '%s': %s" % \ + (version_name, result_show_ref['output']) + return result_show_ref # creating tracking branch cmd_branch = [ - GitClient._executable, 'branch', checkout_version, - '%s/%s' % (remote, checkout_version)] + GitClient._executable, 'branch', version_name, + '%s/%s' % (remote, version_name)] result_branch = self._run_command(cmd_branch) if result_branch['returncode']: result_branch['output'] = \ "Could not create branch '%s': %s" % \ - (checkout_version, result_branch['output']) + (version_name, result_branch['output']) return result_branch cmd += ' && ' + ' '.join(cmd_branch) output = '\n'.join([output, result_branch['output']]) + checkout_version = version_name else: version_type = None if command.version: - result_version_type = self._check_version_type( + result_version_type, version_name = self._check_version_type( command.url, command.version) if result_version_type['returncode']: return result_version_type @@ -370,7 +387,7 @@ def import_(self, command): if not command.shallow or version_type in (None, 'branch'): cmd_clone = [GitClient._executable, 'clone', command.url, '.'] if version_type == 'branch': - cmd_clone += ['-b', command.version] + cmd_clone += ['-b', version_name] checkout_version = None else: checkout_version = command.version @@ -409,7 +426,7 @@ def import_(self, command): elif version_type == 'tag': cmd_fetch.append( 'refs/tags/%s:refs/tags/%s' % - (command.version, command.version)) + (version_name, version_name)) else: assert False cmd_fetch += ['--depth', '1'] @@ -481,15 +498,30 @@ def _get_remote_urls(self): } def _check_version_type(self, url, version): + # check if version starts with heads/ or tags/ + prefixes = { + 'heads/': 'branch', + 'tags/': 'tag', + } + for prefix, version_type in prefixes.items(): + if version.startswith(prefix): + return { + 'cmd': None, + 'cwd': None, + 'output': None, + 'returncode': 0, + 'version_type': version_type, + }, version[len(prefix):] + cmd = [GitClient._executable, 'ls-remote', url, version] result = self._run_command(cmd) if result['returncode']: result['output'] = 'Could not determine ref type of version: ' + \ result['output'] - return result + return result, None if not result['output']: result['version_type'] = 'hash' - return result + return result, None refs = {} for hash_, ref in self._get_hash_ref_tuples(result['output']): @@ -502,14 +534,15 @@ def _check_version_type(self, url, version): result['returncode'] = 1 result['output'] = 'The version ref is a branch as well as ' \ 'tag but with different hashes' - return result + return result, None if tag_ref in refs: result['version_type'] = 'tag' elif branch_ref in refs: result['version_type'] = 'branch' else: result['version_type'] = 'hash' - return result + return result, \ + version if result['version_type'] in ('tag', 'branch') else None def log(self, command): self._check_executable() @@ -645,6 +678,8 @@ def validate(self, command): if command.version: hashes = [] refs = [] + tags = [] + branches = [] for hash_and_ref in self._get_hash_ref_tuples( result_ls_remote['output'] @@ -654,37 +689,59 @@ def validate(self, command): # ignore pull request refs if not hash_and_ref[1].startswith('refs/pull/'): if hash_and_ref[1].startswith('refs/tags/'): - refs.append(hash_and_ref[1][10:]) + tags.append(hash_and_ref[1][10:]) elif hash_and_ref[1].startswith('refs/heads/'): - refs.append(hash_and_ref[1][11:]) + branches.append(hash_and_ref[1][11:]) else: refs.append(hash_and_ref[1]) - # test for refs first - ref_found = command.version in refs - - if not ref_found: + if command.version in refs: + version_type = 'ref' + version_name = command.version + elif ( + command.version.startswith('heads/') and + command.version[6:] in branches + ): + version_type = 'branch' + version_name = command.version[6:] + elif ( + command.version.startswith('tags/') and + command.version[5:] in tags + ): + version_type = 'tag' + version_name = command.version[5:] + elif ( + command.version in branches and + command.version not in tags + ): + version_type = 'branch' + version_name = command.version + elif ( + command.version in tags and + command.version not in branches + ): + version_type = 'tag' + version_name = command.version + else: for _hash in hashes: if _hash.startswith(command.version): - ref_found = True break + else: + cmd = result_ls_remote['cmd'] + output = "Found git repository '%s' but " % command.url + \ + 'unable to verify non-branch / non-tag ref ' + \ + "'%s' without cloning the repo" % command.version - if not ref_found: - cmd = result_ls_remote['cmd'] - output = "Found git repository '%s' but " % command.url + \ - 'unable to verify non-branch / non-tag ref ' + \ - "'%s' without cloning the repo" % command.version + return { + 'cmd': cmd, + 'cwd': self.path, + 'output': output, + 'returncode': 0 + } - return { - 'cmd': cmd, - 'cwd': self.path, - 'output': output, - 'returncode': 0 - } - else: - cmd = result_ls_remote['cmd'] - output = "Found git repository '%s' with ref '%s'" % \ - (command.url, command.version) + cmd = result_ls_remote['cmd'] + output = "Found git repository '%s' with %s '%s'" % \ + (command.url, version_type, version_name) else: cmd = result_ls_remote['cmd'] output = "Found git repository '%s' with default branch" % \