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 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 get_submodules(cls, path: Path) -> List[Submodule]: submodules_info = GitOffline.get_submodules_info(path) submodules = [] for key in submodules_info.keys(): submodule_info = submodules_info[key] # TODO: Save url from config and gitmodules url = submodule_info['url'] submodule_path = Path(key) branch = submodule_info[ 'branch'] if 'branch' in submodule_info else None active = submodule_info[ 'active'] if 'active' in submodule_info else None active = True if active == 'true' else False submodule_commit = GitOffline.get_submodule_commit( path, submodule_path) submodule = Submodule(path, submodule_path, url=url, commit=submodule_commit, branch=branch, active=active) submodules.append(submodule) recursive_submodules = GitFactory.get_submodules(submodule.path) if recursive_submodules: submodules += recursive_submodules return sorted(submodules)
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 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 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 default_branch(self, url: str) -> Optional[RemoteBranch]: if GitOffline.is_repo_cloned(self.path): default_branch = GitOffline.get_default_branch(self.path, self.name) if default_branch is not None: return RemoteBranch(self.path, default_branch, self.name) default_branch = GitOnline.get_default_branch(url) if default_branch is None: return None git_dir = GitOffline.git_dir(self.path) if git_dir is not None and git_dir.is_dir(): GitOffline.save_default_branch(git_dir, self.name, default_branch) return RemoteBranch(self.path, default_branch, self.name)
def submodule_add(self, url: str, branch: Optional[str] = None, force: bool = False, name: Optional[str] = None, reference: Optional[str] = None, depth: Optional[int] = None, submodule_path: Optional[Path] = None) -> None: GitOffline.submodule_add(self.path, url, branch=branch, force=force, name=name, reference=reference, depth=depth, submodule_path=submodule_path)
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 check_ref_format(ref: str) -> bool: """Check if git ref is correctly formatted :param str ref: Git ref :return: True, if git ref is a valid format """ return GitOffline.check_ref_format(ref)
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 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 get_remote_branches_offline(cls, path: Path, remote: str) -> List[RemoteBranch]: branches, default_branch = GitOffline.get_remote_branches_info( path, remote) branches = [RemoteBranch(path, branch, remote) for branch in branches] if default_branch is not None: branches.append( RemoteBranch(path, default_branch, remote, is_default=True)) return sorted(branches)
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 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 get_tracking_branches(cls, path: Path) -> List[TrackingBranch]: branches = GitOffline.get_tracking_branches_info(path) tracking_branches = [] for local_branch, info in branches.items(): tracking_branch = TrackingBranch( path, local_branch=local_branch, upstream_branch=info['upstream_branch'], upstream_remote=info['upstream_remote'], push_branch=info['push_branch'], push_remote=info['push_remote']) tracking_branches.append(tracking_branch) return sorted(tracking_branches)
def formatted_ref(self) -> str: """Formatted project repo ref""" if self.is_detached: return Format.Git.ref(Format.escape(f'[HEAD @ {self.sha()}]')) current_branch_output = Format.Git.ref( Format.escape(f'[{self.current_branch}]')) local_commits_count = GitOffline.new_commits_count(self.path) no_local_commits = local_commits_count == 0 # TODO: Specify correct remote upstream_commits_count = GitOffline.new_commits_count(self.path, upstream=True) no_upstream_commits = upstream_commits_count == 0 if no_local_commits and no_upstream_commits: return current_branch_output local_commits_output = Format.yellow(f'+{local_commits_count}') upstream_commits_output = Format.red(f'-{upstream_commits_count}') return f'{current_branch_output}({local_commits_output}/{upstream_commits_output})'
def get_diff(cls, path: Path) -> Diff: diff_info = GitOffline.get_diff_index_info(path) changes = [] for change_type, changes_list in diff_info.items(): for change_info in changes_list: change = Change(path, file_path=Path(change_info['file_path']), change_type=change_type, old_permissions=change_info['old_permissions'], new_permissions=change_info['new_permissions'], old_sha=change_info['old_sha'], new_sha=change_info['new_sha']) changes.append(change) return Diff(path, changes=changes)
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 is_checked_out(self) -> bool: current_sha = GitOffline.current_head_commit_sha(self.path) tag_sha = GitOnline.get_remote_tag_sha(self.path, self.name) return current_sha == tag_sha
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)
def sha(self) -> str: """Commit sha""" return GitOffline.get_tag_commit_sha(self.path, self.name)
def rename(self, name: str) -> None: CONSOLE.stdout(f' - Rename remote {Format.Git.remote(self.name)} to {Format.Git.remote(name)}') GitOffline.rename_remote(self.path, old_name=self.name, new_name=name) self.name = name
def create(self, url: str, fetch: bool = False, tags: bool = False) -> None: if self.exists: CONSOLE.stdout(f' - Remote {Format.Git.remote(self.name)} already exists') return CONSOLE.stdout(f' - Create remote {Format.Git.remote(self.name)}') GitOffline.create_remote(self.path, name=self.name, url=url, fetch=fetch, tags=tags)
def push_url(self) -> str: return GitOffline.get_remote_push_url(self.path, self.name)
def fetch_url(self) -> str: return GitOffline.get_remote_fetch_url(self.path, self.name)