diff --git a/vcstool/clients/git.py b/vcstool/clients/git.py index ae1ae8bb..c3c774b2 100644 --- a/vcstool/clients/git.py +++ b/vcstool/clients/git.py @@ -1,6 +1,9 @@ import os from shutil import which import subprocess +from urllib.parse import urlparse +import urllib.parse +import urllib from vcstool.executor import USE_COLOR @@ -100,10 +103,10 @@ def export(self, command): remote = remote[len(prefix):] # determine url of remote - result_url = self._get_remote_url(remote) + result_url = self._get_remote_url(remote, os.path.abspath(self.path)) if result_url['returncode']: return result_url - url = result_url['output'] + url = self._to_relative_url(result_url['output']) # the result is the remote url and the branch name return { @@ -213,15 +216,68 @@ def export(self, command): 'returncode': 1, } - def _get_remote_url(self, remote): + def _get_remote_url(self, remote, cwd=None): cmd_url = [ GitClient._executable, 'config', '--get', 'remote.%s.url' % remote] - result_url = self._run_command(cmd_url) + result_url = self._run_command(cmd_url, cwd) if result_url['returncode']: result_url['output'] = 'Could not determine remote url: ' + \ result_url['output'] return result_url + def _get_base_url(self): + # Find a containing git repo + base_path = os.path.abspath(self.path) + while base_path != '/': + base_path = os.path.realpath(os.path.join(base_path, '..')) + if os.path.exists(os.path.join(base_path, '.git')): + break + base_path = os.path.abspath(self.path) if base_path == '/' else base_path + + # Use git to look for an appropriate remote url + remote = 'origin' + result_remote_url = self._get_remote_url(remote, base_path) + if result_remote_url['returncode']: + # Could not resolve abs path + return '' + + return result_remote_url['output'] + + def _to_relative_url(self, url): + # We need to check if relative resolution is possible + base_url = self._get_base_url() + + # Then we need to specify the relative path from base_repo to this url + base_url_parse = urlparse(base_url) + url_parse = urlparse(url) + + if base_url_parse.scheme == url_parse.scheme: + common_url = os.path.commonprefix([base_url_parse.path, url_parse.path]) + if common_url == '/': + # no relative path found + return url + else: + return os.path.relpath(url_parse.path, common_url) + else: + # no relative path found + return url + + + def _resolve_relative_urls(self, url): + if url.startswith('git@'): + return url + elif urlparse(url).scheme: + return url + elif os.path.isabs(url): + return url + else: # Looks as if we have a relative path + # Find .git repo somewhere up the tree and take the containing folder as base path + base_url = self._get_base_url() + + # Resolve relative expressions + resolved_url = urllib.parse.urljoin(base_url, url) + return resolved_url + def import_(self, command): if not command.url: return { @@ -231,6 +287,9 @@ def import_(self, command): 'returncode': 1 } + # Resolve relative git urls + command.url = self._resolve_relative_urls(command.url) + self._check_executable() if GitClient.is_repository(self.path): # verify that existing repository is the same diff --git a/vcstool/clients/vcs_base.py b/vcstool/clients/vcs_base.py index 56526905..d2e94e16 100644 --- a/vcstool/clients/vcs_base.py +++ b/vcstool/clients/vcs_base.py @@ -33,9 +33,10 @@ def _not_applicable(self, command, message=None): 'returncode': NotImplemented } - def _run_command(self, cmd, env=None, retry=0): + def _run_command(self, cmd, cwd=None, env=None, retry=0): + cwd = cwd if cwd else os.path.abspath(self.path) for i in range(retry + 1): - result = run_command(cmd, os.path.abspath(self.path), env=env) + result = run_command(cmd, cwd, env=env) if not result['returncode']: # return successful result break