def pull(self, remote: Optional[str] = None, branch: Optional[str] = None, rebase: bool = False, prune: bool = False, tags: bool = False, jobs: Optional[int] = None, no_edit: bool = False, autostash: bool = False, depth: Optional[int] = None) -> None: # TODO: Check if detached message = f' - Pull' if rebase: message += ' with rebase' CONSOLE.stdout(message) GitOnline.pull(self.path, remote=remote, branch=branch, rebase=rebase, prune=prune, tags=tags, jobs=jobs, no_edit=no_edit, autostash=autostash, depth=depth)
def checkout(self, check: bool = True, track: bool = False) -> None: current_commit = GitOffline.current_head_commit_sha(self.path) if current_commit == self.sha: CONSOLE.stdout(' - On correct commit for tag') return CONSOLE.stdout(f' - Checkout tag {self.name}') super().checkout(check=check, track=track)
def checkout(self, ref: str, track: bool = False) -> None: if self.is_dirty: CONSOLE.stdout( ' - Dirty repo. Please stash, commit, or discard your changes') self.status(verbose=True) return GitOffline.checkout(self.path, ref=ref, track=track)
def fetch(self, prune: bool = False, prune_tags: bool = False, tags: bool = False, depth: Optional[int] = None, remote: Optional[str] = None, branch: Optional[str] = None, unshallow: bool = False, jobs: Optional[int] = None, fetch_all: bool = False, check: bool = True, print_output: bool = True): # FIXME: Consolidate this implementation with the one for Remotes CONSOLE.stdout(f' - Fetch repo') try: GitOnline.fetch(self.path, remote=remote, prune=prune, prune_tags=prune_tags, tags=tags, depth=depth, branch=branch, unshallow=unshallow, jobs=jobs, fetch_all=fetch_all, print_output=print_output) except Exception: # noqa message = f'Failed to fetch repo' if check: GIT_LOG.error(message) raise CONSOLE.stdout(message)
def reset(self, ref: Union[Ref, str] = HEAD, hard: bool = False) -> None: if isinstance(ref, TrackingBranch): ref = ref.upstream_branch.short_ref elif isinstance(ref, Ref): ref = ref.short_ref CONSOLE.stdout(f' - Reset repo to {Format.Git.ref(ref)}') GitOffline.reset(self.path, ref=ref, hard=hard)
def wrapper(*args, **kwargs): if isinstance(message, Callable): instance = args[0] msg: str = message(instance) else: msg: str = str(message) CONSOLE.stdout(msg) return func(*args, **kwargs)
def print_validation(self, allow_missing: bool = False) -> None: """Print validation message""" if not self.exists or self.is_valid(allow_missing=allow_missing): return self.status() CONSOLE.stdout( f'Dirty repo. Please stash, commit, or discard your changes')
def print_local_branches(self) -> None: """Print local git branches""" for branch in self.get_local_branches(): if branch.name == self.current_branch: CONSOLE.stdout(f'* {Format.green(branch.name)}') else: CONSOLE.stdout(branch)
def checkout(self, check: bool = True, track: bool = False) -> None: current_commit = GitOffline.current_head_commit_sha(self.path) # TODO: Should this always check out detached HEAD? if current_commit == self.sha and GitOffline.is_detached(self.path): CONSOLE.stdout(' - On correct commit') return CONSOLE.stdout(f' - Checkout commit {Format.Git.ref(self.short_ref)}') super().checkout(check=check, track=track)
def wrapper(*args, **kwargs): """Wrapper""" instance = args[0] if GitOffline.is_detached(instance.path): CONSOLE.stdout(' - HEAD is detached') raise Exception('Detached HEAD') return func(*args, **kwargs)
def checkout(self, check: bool = True, track: bool = False) -> None: current_branch = GitOffline.current_branch(self.path) if current_branch == self.name: CONSOLE.stdout( f' - Branch {Format.Git.ref(self.short_ref)} already checked out' ) return CONSOLE.stdout(f' - Checkout branch {Format.Git.ref(self.short_ref)}') super().checkout(check=check, track=track)
def reset_timestamp(self, timestamp: str, ref: Ref, author: Optional[str] = None) -> None: CONSOLE.stdout(' - Reset timestamp') GitOffline.reset_timestamp(self.path, timestamp=timestamp, ref=ref.short_ref, author=author)
def checkout(self, check: bool = True, track: bool = False) -> None: try: GitOffline.checkout(self.path, ref=self.short_ref, track=track) except Exception: # noqa message = f'Failed to checkout' if check: GIT_LOG.error(message) raise CONSOLE.stdout(f' - {message}')
def push(self, remote: Optional[str] = None, branch: Optional[str] = None, force: bool = False, set_upstream: bool = False) -> None: # TODO: Check if detached CONSOLE.stdout(' - Push local changes') GitOnline.push(self.path, local_branch=self.name, remote_branch=branch, remote=remote, force=force, set_upstream=set_upstream)
def update_git_config(self, config: GitConfig) -> None: """Update custom git config :param GitConfig config: Custom git config """ CONSOLE.stdout(" - Update local git config") for key, value in config.items(): GitOffline.git_config_unset_all_local(self.path, key) GitOffline.git_config_add_local(self.path, key, value)
def set_upstream(self) -> None: CONSOLE.stdout( f' - Set tracking branch {Format.Git.ref(self.name)} -> ' f'{Format.Git.remote(self.upstream_branch.remote.name)} ' f'{Format.Git.ref(self.upstream_branch.name)}') GitOffline.set_upstream_branch( self.path, local_branch=self.name, upstream_branch=self.upstream_branch.name, remote=self.upstream_branch.remote.name)
def add(self, files: Union[Path, str, List[str], List[Path]]) -> None: if isinstance(files, str): files = [files] elif isinstance(files, Path): files = [str(files)] elif isinstance(files, list): files = [str(f) for f in files] else: raise Exception('Wrong type for files parameter') CONSOLE.stdout(' - Add files to git index') GitOffline.add(self.path, files=files)
def delete(self) -> None: if not self.exists: CONSOLE.stdout( f" - Remote tag {Format.Git.ref(self.short_ref)} doesn't exist" ) return CONSOLE.stdout( f' - Delete remote tag {Format.Git.ref(self.short_ref)}') GitOnline.delete_remote_tag(self.path, tag=self.name, remote=self.remote.name, force=True)
def delete(self) -> None: from pygoodle.git.model.factory import GitFactory if not GitFactory.has_remote_branch_online(self.path, self.name, self.remote.name): CONSOLE.stdout( f" - Remote branch {Format.Git.ref(self.name)} doesn't exist") return CONSOLE.stdout(f' - Delete remote branch {Format.Git.ref(self.name)}') GitOnline.delete_remote_branch(self.path, branch=self.name, remote=self.remote.name, force=True)
def clone(self, path: Path, url: str, depth: Optional[int] = None, branch: Optional[str] = None, jobs: Optional[int] = None, origin: Optional[str] = None) -> 'Repo': CONSOLE.stdout(' - Clone repo') GitOnline.clone(path, url=url, depth=depth, branch=branch, jobs=jobs, origin=origin) return Repo(path)
def save(self, contents: dict) -> None: """Save yaml file to disk :param dict contents: Parsed YAML python object :raise ExistingFileError: """ if self.path.is_file(): raise ExistingFileError(f"File already exists: {Format.path(self.path)}") CONSOLE.stdout(f" - Save yaml to file at {Format.path(self.path)}") try: with self.path.open(mode="w") as raw_file: pyyaml.safe_dump(contents, raw_file, default_flow_style=False, indent=2, sort_keys=False) except pyyaml.YAMLError: # LOG.error(f"Failed to save file {Format.path(yaml_file)}") raise
def create(self, branch: Optional[str] = None, remote: Optional[str] = None, track: bool = True) -> None: if self.exists: CONSOLE.stdout(f' - Local branch {Format.Git.ref(self.short_ref)} already exists') return CONSOLE.stdout(f' - Create local branch {Format.Git.ref(self.short_ref)}') if branch is not None: if remote is not None: from pygoodle.git.model.branch.remote_branch import RemoteBranch remote_branch = RemoteBranch(self.path, branch, remote) if not remote_branch.exists: branch = None remote = None else: local_branch = LocalBranch(self.path, branch) if not local_branch.exists: branch = None GitOffline.create_local_branch(self.path, self.name, branch=branch, remote=remote, track=track)
def download(self, path: Path) -> Path: file_type = self._file_type(path) if file_type is FileType.FILE: files = [FileInfo(path, self._get_file_size(path))] elif file_type is FileType.DIR: with self.reserve_ftp() as ftp: directory_info = DirectoryInfo(ftp, path) self._make_download_dirs(path, directory_info) files = directory_info.files else: raise Exception('Unknown file type') download_path = self._get_download_path(path) class DownloadTaskPool(ProgressTaskPool): def after_tasks(self, tasks: List[ProgressTask]) -> None: super().after_tasks(tasks) if self.cancelled: CONSOLE.stdout(f'Remove {download_path}', force=True) fs.remove(download_path) ftp = self class DownloadTask(ProgressTask): def __init__(self, file: FileInfo): super().__init__(file.name, units='bytes', total=file.size) self._file: FileInfo = file def run(self) -> None: def callback(data): if self.cancelled: raise CancelledDownloadError( f'FTP download of {self._file.name} was cancelled') self.progress.update_subtask(self._file.name, advance=len(data)) ftp.download_file(self._file, callback) download_tasks = [DownloadTask(file) for file in files] pool = DownloadTaskPool(jobs=self.pool_size, title='Files', units='files') pool.run(download_tasks) CONSOLE.stdout(f'Downloaded {len(files)} files', force=True) return download_path
def fetch(self, prune: bool = False, prune_tags: bool = False, tags: bool = False, depth: Optional[int] = None, branch: Optional[str] = None, unshallow: bool = False, jobs: Optional[int] = None, fetch_all: bool = False, check: bool = True, print_output: bool = True) -> None: output = self.name if branch is not None: branch = branch output = f'{output} {branch}' CONSOLE.stdout(f'Fetch from {output}') try: GitOnline.fetch(self.path, remote=self.name, prune=prune, prune_tags=prune_tags, tags=tags, depth=depth, branch=branch, unshallow=unshallow, jobs=jobs, fetch_all=fetch_all, print_output=print_output) except Exception: # noqa message = f'Failed to fetch from {output}' if check: GIT_LOG.error(message) raise CONSOLE.stdout(f' - {message}')
def clean(self, untracked_directories: bool = False, force: bool = False, ignored: bool = False, untracked_files: bool = False) -> None: """Discard changes for repo :param bool untracked_directories: ``d`` Remove untracked directories in addition to untracked files :param bool force: ``f`` Delete directories with .git sub directory or file :param bool ignored: ``X`` Remove only files ignored by git :param bool untracked_files: ``x`` Remove all untracked files """ CONSOLE.stdout(' - Clean repo') GitOffline.clean(self.path, untracked_directories=untracked_directories, force=force, ignored=ignored, untracked_files=untracked_files)
def create(self, branch: Optional[str] = None, remote: Optional[str] = None) -> None: from pygoodle.git.model.factory import GitFactory if GitFactory.has_remote_branch_online(self.path, self.name, self.remote.name): CONSOLE.stdout( f' - Remote branch {Format.Git.ref(self.name)} already exists') return CONSOLE.stdout(f' - Create remote branch {Format.Git.ref(self.name)}') branch = self.name if branch is None else branch from pygoodle.git.model.branch.local_branch import LocalBranch local_branch = LocalBranch(self.path, branch) GitOnline.fetch(self.path, prune=True) has_existing_branch = local_branch.exists if not has_existing_branch: local_branch.create(branch=branch, remote=remote) local_branch.push(remote=self.remote.name, branch=self.name) if not has_existing_branch: GitOffline.delete_local_branch(self.path, branch) GitOnline.fetch(self.path, prune=True)
def create(self) -> None: if GitOffline.has_tracking_branch(self.path, self.local_branch.name): CONSOLE.stdout(' - Tracking branch already exists') return # TODO: Add Format util to format tracking branch output: local_branch -> remote remote_branch CONSOLE.stdout( f' - Create tracking branch {Format.Git.ref(self.name)}') GitOnline.fetch(self.path, prune=True) # local and remote branches exist if self.local_branch.exists and self.upstream_branch.exists: self.set_upstream() return # only local branch exists if self.local_branch.exists: # GitOnline.push(self.path, # local_branch=self.name, # remote_branch=self.upstream_branch.name, # remote=self.upstream_branch.remote.name, # set_upstream=True) self.upstream_branch.create(branch=self.local_branch.name) self.set_upstream() GitOnline.fetch(self.path, prune=True) return # only remote branch exists if self.upstream_branch.exists: self.local_branch.create(branch=self.upstream_branch.name, remote=self.upstream_branch.remote.name) # GitOffline.create_local_branch(self.path, self.name, # branch=self.upstream_branch.name, # remote=self.upstream_branch.remote) return # local and remote branches DO NOT exist self.upstream_branch.create() GitOnline.fetch(self.path, prune=True) self.local_branch.create(branch=self.upstream_branch.name, remote=self.upstream_branch.remote.name)
def submodule_update(self, init: bool = False, depth: Optional[int] = None, single_branch: bool = False, jobs: Optional[int] = None, recursive: bool = False, remote: bool = False, checkout: bool = False, rebase: bool = False, merge: bool = False, paths: Optional[List[Path]] = None) -> None: CONSOLE.stdout(' - Update submodules') GitOnline.submodule_update(self.path, init=init, depth=depth, single_branch=single_branch, jobs=jobs, recursive=recursive, remote=remote, checkout=checkout, merge=merge, rebase=rebase, paths=paths)
def create(self) -> None: if self.exists: CONSOLE.stdout( f' - Remote tag {Format.Git.ref(self.name)} already exists') return raise NotImplementedError
def delete(self) -> None: if not self.exists: CONSOLE.stdout(f" - Local tag {Format.Git.ref(self.short_ref)} doesn't exist") return CONSOLE.stdout(f' - Delete local tag {Format.Git.ref(self.short_ref)}') GitOffline.delete_local_tag(self.path, name=self.name)