def _create_remote(self, remote, url, remove_dir=False): """Create new remote""" remote_names = [r.name for r in self.repo.remotes] if remote in remote_names: return 0 remote_output = fmt.remote_string(remote) try: self._print(' - Create remote ' + remote_output) self.repo.create_remote(remote, url) return 0 except GitError as err: message = colored(' - Failed to create remote ', 'red') if remove_dir: remove_directory(self.repo_path) self._print(message + remote_output) self._print(fmt.error(err)) self._exit( fmt.parallel_exception_error(self.repo_path, message, remote_output)) except (KeyboardInterrupt, SystemExit): if remove_dir: remove_directory(self.repo_path) self._exit()
def _checkout_new_repo_tag(self, tag: str, remote: str, depth: int, remove_dir: bool = False) -> None: """Checkout tag or fail and delete repo if it doesn't exist :param str tag: Tag name :param str remote: Remote name :param int depth: Git clone depth. 0 indicates full clone, otherwise must be a positive integer :param bool remove_dir: Whether to remove the directory if commands fail """ remote_tag = self._get_remote_tag(tag, remote, depth=depth, remove_dir=remove_dir) if remote_tag is None: return try: CONSOLE.stdout(f' - Checkout tag {fmt.ref(tag)}') self.repo.git.checkout(remote_tag) except BaseException: LOG.error(f'Failed to checkout tag {fmt.ref(tag)}') if remove_dir: remove_directory(self.repo_path, check=False) raise
def _get_remote_tag(self, tag: str, remote: str, depth: int = 0, remove_dir: bool = False) -> Optional[Tag]: """Returns Tag object :param str tag: Tag name :param str remote: Remote name :param int depth: Git clone depth. 0 indicates full clone, otherwise must be a positive integer :param bool remove_dir: Whether to remove the directory if commands fail :return: GitPython Tag object if it exists, otherwise None """ self._remote(remote, remove_dir=remove_dir) self.fetch(remote, depth=depth, ref=GitRef(tag=tag), remove_dir=remove_dir) try: return self.repo.tags[tag] except (GitError, IndexError) as err: LOG.error(f'No existing tag {fmt.ref(tag)}') if remove_dir: remove_directory(self.repo_path, check=False) raise LOG.debug(error=err) return None except BaseException: LOG.error('Failed to get tag') if remove_dir: remove_directory(self.repo_path, check=False) raise
def _create_branch_local_tracking(self, branch: str, remote: str, depth: int, fetch: bool = True, remove_dir: bool = False) -> None: """Create and checkout tracking branch :param str branch: Branch name :param str remote: Remote name :param int depth: Git clone depth. 0 indicates full clone, otherwise must be a positive integer :param bool fetch: Whether to fetch before creating branch :param bool remove_dir: Whether to remove the directory if commands fail """ origin = self._remote(remote, remove_dir=remove_dir) if fetch: self.fetch(remote, depth=depth, ref=GitRef(branch=branch), remove_dir=remove_dir) try: CONSOLE.stdout(f' - Create branch {fmt.ref(branch)}') self.repo.create_head(branch, origin.refs[branch]) except BaseException: LOG.error(f'Failed to create branch {fmt.ref(branch)}') if remove_dir: remove_directory(self.repo_path, check=False) raise else: self._set_tracking_branch(remote, branch, remove_dir=remove_dir) self._checkout_branch_local(branch, remove_dir=remove_dir)
def _checkout_branch_local(self, branch, remove_dir=False): """Checkout local branch .. py:function:: _checkout_branch_local(branch, remove_dir=False) :param str branch: Branch name :param Optional[bool] remove_dir: Whether to remove the directory if commands fail """ branch_output = fmt.ref_string(branch) try: self._print(' - Checkout branch ' + branch_output) default_branch = self.repo.heads[branch] default_branch.checkout() except GitError as err: if remove_dir: remove_directory(self.repo_path) message = colored(' - Failed to checkout branch ', 'red') self._print(message + branch_output) self._print(fmt.error(err)) self._exit( fmt.parallel_exception_error(self.repo_path, message, branch_output)) except (KeyboardInterrupt, SystemExit): if remove_dir: remove_directory(self.repo_path) self._exit()
def _set_tracking_branch(self, remote, branch, remove_dir=False): """Set tracking branch .. py:function:: _set_tracking_branch(remote, branch, remove_dir=False) :param str remote: Remote name :param str branch: Branch name :param Optional[bool] remove_dir: Whether to remove the directory if commands fail """ branch_output = fmt.ref_string(branch) remote_output = fmt.remote_string(remote) origin = self._remote(remote) try: local_branch = self.repo.heads[branch] remote_branch = origin.refs[branch] self._print(' - Set tracking branch ' + branch_output + ' -> ' + remote_output + ' ' + branch_output) local_branch.set_tracking_branch(remote_branch) except GitError as err: message = colored(' - Failed to set tracking branch ', 'red') + branch_output if remove_dir: remove_directory(self.repo_path) self._print(message) self._print(fmt.error(err)) self._exit(message) except (KeyboardInterrupt, SystemExit): if remove_dir: remove_directory(self.repo_path) self._exit()
def _init_repo(self): """Initialize repository :raise OSError: """ if existing_git_repository(self.repo_path): return try: self._print(' - Initialize repo at ' + fmt.get_path(self.repo_path)) if not os.path.isdir(self.repo_path): try: os.makedirs(self.repo_path) except OSError as err: if err.errno != os.errno.EEXIST: raise self.repo = Repo.init(self.repo_path) except GitError as err: remove_directory(self.repo_path) message = colored(' - Failed to initialize repository', 'red') self._print(message) self._print(fmt.error(err)) self._exit(message) except (KeyboardInterrupt, SystemExit): remove_directory(self.repo_path) self._exit()
def _set_tracking_branch(self, remote, branch, remove_dir=False): """Set tracking branch""" branch_output = fmt.ref_string(branch) remote_output = fmt.remote_string(remote) origin = self._remote(remote) try: local_branch = self.repo.heads[branch] remote_branch = origin.refs[branch] self._print(' - Set tracking branch ' + branch_output + ' -> ' + remote_output + ' ' + branch_output) local_branch.set_tracking_branch(remote_branch) return 0 except GitError as err: message = colored(' - Failed to set tracking branch ', 'red') + branch_output if remove_dir: remove_directory(self.repo_path) self._print(message) self._print(fmt.error(err)) self._exit(message) except (KeyboardInterrupt, SystemExit): if remove_dir: remove_directory(self.repo_path) self._exit()
def _create_remote(self, remote, url, remove_dir=False): """Create new remote .. py:function:: _create_remote(remote, url, remove_dir=False) :param str remote: Remote name :param str url: URL of repo :param Optional[bool] remove_dir: Whether to remove the directory if commands fail """ remote_names = [r.name for r in self.repo.remotes] if remote in remote_names: return 0 remote_output = fmt.remote_string(remote) try: self._print(' - Create remote ' + remote_output) self.repo.create_remote(remote, url) return 0 except GitError as err: message = colored(' - Failed to create remote ', 'red') if remove_dir: remove_directory(self.repo_path) self._print(message + remote_output) self._print(fmt.error(err)) self._exit( fmt.parallel_exception_error(self.repo_path, message, remote_output)) except (KeyboardInterrupt, SystemExit): if remove_dir: remove_directory(self.repo_path) self._exit()
def _checkout_new_repo_branch(self, branch: str, depth: int) -> None: """Checkout remote branch or fail and delete repo if it doesn't exist :param str branch: Branch name :param int depth: Git clone depth. 0 indicates full clone, otherwise must be a positive integer :raise ClowderGitError: """ self._remote(self.remote, remove_dir=True) self.fetch(self.remote, depth=depth, ref=GitRef(branch=branch), remove_dir=True) if not self.has_remote_branch(branch, self.remote): remove_directory(self.repo_path, check=False) raise ClowderGitError( f'No existing remote branch {fmt.remote(self.remote)} {fmt.ref(branch)}' ) self._create_branch_local_tracking(branch, self.remote, depth=depth, fetch=False, remove_dir=True)
def _checkout_new_repo_commit(self, commit, remote, depth): """Checkout commit or fail and delete repo if it doesn't exist :param str commit: Commit sha :param str remote: Remote name :param int depth: Git clone depth. 0 indicates full clone, otherwise must be a positive integer """ commit_output = fmt.ref_string(commit) self._remote(remote, remove_dir=True) self.fetch(remote, depth=depth, ref=commit, remove_dir=True) self._print(' - Checkout commit ' + commit_output) try: self.repo.git.checkout(commit) except GitError as err: remove_directory(self.repo_path) message = colored(' - Failed to checkout commit ', 'red') self._print(message + commit_output) self._print(fmt.error(err)) self._exit( fmt.parallel_exception_error(self.repo_path, message, commit_output)) except (KeyboardInterrupt, SystemExit): remove_directory(self.repo_path) self._exit()
def init_exit_handler(self): """Exit handler for deleting files if init fails""" if os.path.isdir(self.clowder_path): clowder_yaml = os.path.join(self.root_directory, 'clowder.yaml') if not os.path.isfile(clowder_yaml): remove_directory(self.clowder_path) sys.exit(1)
def _remote(self, remote: str, remove_dir: bool = False) -> Remote: """Get GitPython Remote instance :param str remote: Remote name :param bool remove_dir: Whether to remove the directory if commands fail :return: GitPython Remote instance """ try: return self.repo.remotes[remote] except GitError: LOG.error(f'No existing remote {fmt.remote(remote)}') if remove_dir: remove_directory(self.repo_path, check=False) raise
def _remote(self, remote, remove_dir=False): """Get remote""" remote_output = fmt.remote_string(remote) try: return self.repo.remotes[remote] except GitError as err: message = colored(' - No existing remote ', 'red') + remote_output if remove_dir: remove_directory(self.repo_path) self._print(message) self._print(fmt.error(err)) self._exit(message) except (KeyboardInterrupt, SystemExit): self._exit()
def _init_repo(self) -> None: """Initialize repository""" if existing_git_repo(self.repo_path): # TODO: Raise exception here? return try: CONSOLE.stdout(f' - Initialize repo at {fmt.path(self.repo_path)}') if not self.repo_path.is_dir(): make_dir(self.repo_path) self.repo = Repo.init(self.repo_path) except BaseException: LOG.error('Failed to initialize repository') remove_directory(self.repo_path, check=False) raise
def fetch(self, remote, **kwargs): """Fetch from a specific remote ref .. py:function:: fetch(remote, ref=None, depth=0, remove_dir=False, allow_failure=False) :param str remote: Remote name Keyword Args: ref (str): Ref to fetch depth (int): Git clone depth. 0 indicates full clone, otherwise must be a positive integer remove_dir (bool): Whether to remove the directory if commands fail allow_failure (bool): Whether to allow failure """ ref = kwargs.get('ref', None) depth = kwargs.get('depth', 0) remove_dir = kwargs.get('remove_dir', False) allow_failure = kwargs.get('allow_failure', False) remote_output = fmt.remote_string(remote) if depth == 0: self._print(' - Fetch from ' + remote_output) message = colored(' - Failed to fetch from ', 'red') error = message + remote_output command = ['git fetch', remote, '--prune --tags'] elif ref is None: command = ['git fetch', remote, '--depth', str(depth), '--prune --tags'] message = colored(' - Failed to fetch remote ', 'red') error = message + remote_output else: ref_output = fmt.ref_string(truncate_ref(ref)) self._print(' - Fetch from ' + remote_output + ' ' + ref_output) message = colored(' - Failed to fetch from ', 'red') error = message + remote_output + ' ' + ref_output command = ['git fetch', remote, truncate_ref(ref), '--depth', str(depth), '--prune --tags'] try: execute_command(command, self.repo_path, print_output=self._print_output) except ClowderError: if remove_dir: remove_directory(self.repo_path) if not allow_failure: self._print(error) self._exit(error)
def _checkout_branch_local(self, branch: str, remove_dir: bool = False) -> None: """Checkout local branch :param str branch: Branch name :param bool remove_dir: Whether to remove the directory if commands fail :raise: """ try: CONSOLE.stdout(f' - Checkout branch {fmt.ref(branch)}') default_branch = self.repo.heads[branch] default_branch.checkout() except BaseException: LOG.error(f'Failed to checkout branch {fmt.ref(branch)}') if remove_dir: remove_directory(self.repo_path, check=False) raise
def _checkout_new_repo_branch(self, branch, depth): """Checkout remote branch or fail and delete repo if it doesn't exist""" branch_output = fmt.ref_string(branch) remote_output = fmt.remote_string(self.remote) self._remote(self.remote, remove_dir=True) self.fetch(self.remote, depth=depth, ref=branch, remove_dir=True) if not self.existing_remote_branch(branch, self.remote): remove_directory(self.repo_path) message = colored(' - No existing remote branch ', 'red') + remote_output + ' ' + branch_output self._print(message) self._exit(fmt.parallel_exception_error(self.repo_path, message)) self._create_branch_local_tracking(branch, self.remote, depth=depth, fetch=False, remove_dir=True)
def fetch(self, remote: str, ref: Optional[GitRef] = None, depth: int = 0, remove_dir: bool = False, allow_failure: bool = False) -> None: """Fetch from a specific remote ref :param str remote: Remote name :param Optional[GitRef] ref: Ref to fetch :param int depth: Git clone depth. 0 indicates full clone, otherwise must be a positive integer :param bool remove_dir: Whether to remove the directory if commands fail :param bool allow_failure: Whether to allow failure """ if depth == 0 or ref is None: CONSOLE.stdout(f' - Fetch from {fmt.remote(remote)}') error_message = f'Failed to fetch from remote {fmt.remote(remote)}' else: CONSOLE.stdout( f' - Fetch from {fmt.remote(remote)} {fmt.ref(ref.short_ref)}') error_message = f'Failed to fetch from {fmt.remote(remote)} {fmt.ref(ref.short_ref)}' try: if depth == 0: execute_command(f'git fetch {remote} --prune --tags', self.repo_path) elif ref is None: command = f'git fetch {remote} --depth {depth} --prune --tags' execute_command(command, self.repo_path) else: command = f'git fetch {remote} {ref.short_ref} --depth {depth} --prune --tags' execute_command(command, self.repo_path) except BaseException as err: LOG.error(error_message) if remove_dir: remove_directory(self.repo_path, check=False) if allow_failure: LOG.debug(error=err) return raise
def _checkout_new_repo_commit(self, commit, remote, depth): """Checkout commit or fail and delete repo if it doesn't exist""" commit_output = fmt.ref_string(commit) self._remote(remote, remove_dir=True) self.fetch(remote, depth=depth, ref=commit, remove_dir=True) self._print(' - Checkout commit ' + commit_output) try: self.repo.git.checkout(commit) except GitError as err: remove_directory(self.repo_path) message = colored(' - Failed to checkout commit ', 'red') self._print(message + commit_output) self._print(fmt.error(err)) self._exit( fmt.parallel_exception_error(self.repo_path, message, commit_output)) except (KeyboardInterrupt, SystemExit): remove_directory(self.repo_path) self._exit()
def _create_branch_local_tracking(self, branch, remote, depth, **kwargs): """Create and checkout tracking branch .. py:function:: _create_branch_local_tracking(self, branch, remote, depth, fetch=True, remove_dir=False) :param str branch: Branch name :param str remote: Remote name :param int depth: Git clone depth. 0 indicates full clone, otherwise must be a positive integer Keyword Args: fetch (bool): Whether to fetch before creating branch remove_dir (bool): Whether to remove the directory if commands fail """ fetch = kwargs.get('fetch', True) remove_dir = kwargs.get('remove_dir', False) branch_output = fmt.ref_string(branch) origin = self._remote(remote, remove_dir=remove_dir) if fetch: self.fetch(remote, depth=depth, ref=branch, remove_dir=remove_dir) try: self._print(' - Create branch ' + branch_output) self.repo.create_head(branch, origin.refs[branch]) except (GitError, IndexError) as err: message = colored(' - Failed to create branch ', 'red') + branch_output if remove_dir: remove_directory(self.repo_path) self._print(message) self._print(fmt.error(err)) self._exit(message) except (KeyboardInterrupt, SystemExit): if remove_dir: remove_directory(self.repo_path) self._exit() else: self._set_tracking_branch(remote, branch, remove_dir=remove_dir) self._checkout_branch_local(branch, remove_dir=remove_dir)
def _create_branch_local_tracking(self, branch, remote, depth, fetch=True, remove_dir=False): """Create and checkout tracking branch""" branch_output = fmt.ref_string(branch) origin = self._remote(remote, remove_dir=remove_dir) if fetch: return_code = self.fetch(remote, depth=depth, ref=branch, remove_dir=remove_dir) if return_code != 0: return return_code try: self._print(' - Create branch ' + branch_output) self.repo.create_head(branch, origin.refs[branch]) except (GitError, IndexError) as err: message = colored(' - Failed to create branch ', 'red') + branch_output if remove_dir: remove_directory(self.repo_path) self._print(message) self._print(fmt.error(err)) self._exit(message) except (KeyboardInterrupt, SystemExit): if remove_dir: remove_directory(self.repo_path) self._exit() else: return_code = self._set_tracking_branch(remote, branch, remove_dir=remove_dir) if return_code != 0: return return_code return self._checkout_branch_local(branch, remove_dir=remove_dir)
def _checkout_branch_local(self, branch, remove_dir=False): """Checkout local branch""" branch_output = fmt.ref_string(branch) try: self._print(' - Checkout branch ' + branch_output) default_branch = self.repo.heads[branch] default_branch.checkout() return 0 except GitError as err: if remove_dir: remove_directory(self.repo_path) message = colored(' - Failed to checkout branch ', 'red') self._print(message + branch_output) self._print(fmt.error(err)) self._exit( fmt.parallel_exception_error(self.repo_path, message, branch_output)) except (KeyboardInterrupt, SystemExit): if remove_dir: remove_directory(self.repo_path) self._exit()
def _checkout_new_repo_commit(self, commit: str, remote: str, depth: int) -> None: """Checkout commit or fail and delete repo if it doesn't exist :param str commit: Commit sha :param str remote: Remote name :param int depth: Git clone depth. 0 indicates full clone, otherwise must be a positive integer """ self._remote(remote, remove_dir=True) self.fetch(remote, depth=depth, ref=GitRef(commit=commit), remove_dir=True) CONSOLE.stdout(f' - Checkout commit {fmt.ref(commit)}') try: self.repo.git.checkout(commit) except BaseException: LOG.error(f'Failed to checkout commit {fmt.ref(commit)}') remove_directory(self.repo_path, check=False) raise
def _get_remote_tag(self, tag, remote, depth=0, remove_dir=False): """Returns Tag object .. py:function:: _get_remote_tag(tag, remote, depth=0, remove_dir=False) :param str tag: Tag name :param str remote: Remote name :param Optional[int] depth: Git clone depth. 0 indicates full clone, otherwise must be a positive integer :param Optional[bool] remove_dir: Whether to remove the directory if commands fail :return: GitPython Tag object if it exists, otherwise None :rtype: Tag """ tag_output = fmt.ref_string(tag) self._remote(remote, remove_dir=remove_dir) self.fetch(remote, depth=depth, ref='refs/tags/' + tag, remove_dir=remove_dir) try: return self.repo.tags[tag] except (GitError, IndexError): message = ' - No existing tag ' if remove_dir: remove_directory(self.repo_path) self._print(colored(message, 'red') + tag_output) self._exit( fmt.parallel_exception_error(self.repo_path, colored(message, 'red'), tag_output)) if self._print_output: self._print(message + tag_output) return None except (KeyboardInterrupt, SystemExit): if remove_dir: remove_directory(self.repo_path) self._exit()
def _remote(self, remote, remove_dir=False): """Get GitPython Remote instance .. py:function:: _remote(remote, remove_dir=False) :param str remote: Remote name :param Optional[bool] remove_dir: Whether to remove the directory if commands fail :return: GitPython Remote instance :rtype: Remote """ remote_output = fmt.remote_string(remote) try: return self.repo.remotes[remote] except GitError as err: message = colored(' - No existing remote ', 'red') + remote_output if remove_dir: remove_directory(self.repo_path) self._print(message) self._print(fmt.error(err)) self._exit(message) except (KeyboardInterrupt, SystemExit): self._exit()
def _create_remote(self, remote: str, url: str, remove_dir: bool = False) -> None: """Create new remote :param str remote: Remote name :param str url: URL of repo :param bool remove_dir: Whether to remove the directory if commands fail """ remote_names = [r.name for r in self.repo.remotes] if remote in remote_names: return try: CONSOLE.stdout(f' - Create remote {fmt.remote(remote)}') self.repo.create_remote(remote, url) except BaseException: LOG.error(f'Failed to create remote {fmt.remote(remote)}') if remove_dir: remove_directory(self.repo_path, check=False) raise
def _checkout_new_repo_tag(self, tag, remote, depth, remove_dir=False): """Checkout tag or fail and delete repo if it doesn't exist .. py:function:: _checkout_new_repo_tag(tag, remote, depth, remove_dir=False) :param str tag: Tag name :param str remote: Remote name :param int depth: Git clone depth. 0 indicates full clone, otherwise must be a positive integer :param Optional[bool] remove_dir: Whether to remove the directory if commands fail """ remote_tag = self._get_remote_tag(tag, remote, depth=depth, remove_dir=remove_dir) if remote_tag is None: return 1 tag_output = fmt.ref_string(tag) try: self._print(' - Checkout tag ' + tag_output) self.repo.git.checkout(remote_tag) return 0 except GitError as err: message = colored(' - Failed to checkout tag ', 'red') self._print(message + tag_output) self._print(fmt.error(err)) if remove_dir: remove_directory(self.repo_path) self._exit( fmt.parallel_exception_error(self.repo_path, message, tag_output)) return 1 except (KeyboardInterrupt, SystemExit): if remove_dir: remove_directory(self.repo_path) self._exit()
def _checkout_new_repo_tag(self, tag, remote, depth, remove_dir=False): """Checkout tag or fail and delete repo if it doesn't exist""" tag_output = fmt.ref_string(tag) self._remote(remote, remove_dir=remove_dir) self.fetch(remote, depth=depth, ref='refs/tags/' + tag, remove_dir=remove_dir) try: remote_tag = self.repo.tags[tag] except (GitError, IndexError): message = ' - No existing tag ' if remove_dir: remove_directory(self.repo_path) self._print(colored(message, 'red') + tag_output) self._exit( fmt.parallel_exception_error(self.repo_path, colored(message, 'red'), tag_output)) if self.print_output: self._print(message + tag_output) return 1 except (KeyboardInterrupt, SystemExit): if remove_dir: remove_directory(self.repo_path) self._exit() else: try: self._print(' - Checkout tag ' + tag_output) self.repo.git.checkout(remote_tag) return 0 except GitError as err: message = colored(' - Failed to checkout tag ', 'red') self._print(message + tag_output) self._print(fmt.error(err)) if remove_dir: remove_directory(self.repo_path) self._exit( fmt.parallel_exception_error(self.repo_path, message, tag_output)) return 1 except (KeyboardInterrupt, SystemExit): if remove_dir: remove_directory(self.repo_path) self._exit()
def fetch(self, remote, ref=None, depth=0, remove_dir=False): """Fetch from a specific remote ref""" remote_output = fmt.remote_string(remote) if depth == 0: self._print(' - Fetch from ' + remote_output) message = colored(' - Failed to fetch from ', 'red') error = message + remote_output command = ['git fetch', remote, '--prune --tags'] elif ref is None: command = [ 'git fetch', remote, '--depth', str(depth), '--prune --tags' ] message = colored(' - Failed to fetch remote ', 'red') error = message + remote_output else: ref_output = fmt.ref_string(GitRepo.truncate_ref(ref)) self._print(' - Fetch from ' + remote_output + ' ' + ref_output) message = colored(' - Failed to fetch from ', 'red') error = message + remote_output + ' ' + ref_output command = [ 'git fetch', remote, GitRepo.truncate_ref(ref), '--depth', str(depth), '--prune --tags' ] return_code = execute_command(command, self.repo_path, print_output=self.print_output) if return_code != 0: if remove_dir: remove_directory(self.repo_path) self._print(error) self._exit(error) return return_code