diff --git a/biscuit/core/components/git/__init__.py b/biscuit/core/components/git/__init__.py index 5f9fec64..cae20577 100644 --- a/biscuit/core/components/git/__init__.py +++ b/biscuit/core/components/git/__init__.py @@ -1,7 +1,12 @@ +from __future__ import annotations + import os import re +import typing from tkinter import messagebox +from ..floating.palette.actionset import ActionSet + git_available = True try: import git @@ -11,13 +16,27 @@ messagebox.showerror("Git not found", "Git is not installed on your PC. Install and add Git to the PATH to use Biscuit") git_available = False + +if typing.TYPE_CHECKING: + from biscuit import App + URL = re.compile(r'^(?:http)s?://') class Git(git.Git): - def __init__(self, master, *args, **kwargs) -> None: + def __init__(self, master: App, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self.base = master self.repo = None + self.branches = {} + + self.actionset = ActionSet( + "Manage git branches", "branch:", + self.branches, + pinned=[["Create new branch: {}", lambda branch=None: self.repo.create_branch(branch)]], + ) + + def late_setup(self) -> None: + self.base.palette.register_actionset(lambda: self.actionset) def check_git(self) -> None: if not (git_available and self.base.active_directory): @@ -27,9 +46,21 @@ def check_git(self) -> None: try: self.repo = GitRepo(self, self.base.active_directory) self.base.git_found = True + self.update_repo_info() except git.exc.InvalidGitRepositoryError: self.base.git_found = False + def update_repo_info(self) -> None: + self.branches = {} + + for branch in self.repo.branches: + latest_commit = next(self.repo.iter_commits(branch)) + self.branches[branch] = latest_commit.committed_datetime + self.branches = sorted(self.branches.items(), key=lambda x: x[1], reverse=True) + + # TODO: make use of the commit_time in palette items + self.actionset.update([(str(branch), lambda e=None, b=branch: self.repo.switch_to_branch(b)) for branch, commit_time in self.branches]) + def get_version(self) -> str: if not git_available: return diff --git a/biscuit/core/components/git/repo.py b/biscuit/core/components/git/repo.py index 69a80328..ef2d6a59 100644 --- a/biscuit/core/components/git/repo.py +++ b/biscuit/core/components/git/repo.py @@ -1,10 +1,17 @@ +from __future__ import annotations + +import typing + import git +if typing.TYPE_CHECKING: + from . import Git class GitRepo(git.Repo): - def __init__(self, master=None, path=None, *args, **kwargs) -> None: + def __init__(self, master: Git=None, path=None, *args, **kwargs) -> None: super().__init__(path, *args, **kwargs) self.master = master + self.base = master.base self.path = path self.config = self.config_reader() @@ -12,6 +19,20 @@ def __init__(self, master=None, path=None, *args, **kwargs) -> None: self.author_email = self.config.get_value("user", "email") self.author = git.Actor(self.author_name, self.author_email) + def switch_to_branch(self, branch: git.Head): + self.git.checkout(str(branch)) + self.master.update_repo_info() + self.base.statusbar.update_git_info() + self.base.explorer.directory.refresh_root() + + def create_branch(self, branch: str): + if not branch: + self.base.notifications.error("Branch name cannot be empty") + return + + self.create_head(branch.strip()) + self.switch_to_branch(branch) + def get_untracked_files(self) -> list: return list(self.untracked_files) @@ -41,7 +62,7 @@ def get_commit_filedata(self, filename) -> str: def stage_files(self, *paths) -> None: for path, change_type in paths: - # change type can be 0, 1, 2, 3 + # change type can be 0, 1, 2, 3 # respectively represents Deleted, Added, Modified, Untracked if change_type == 0: self.do(self.index.remove, [path]) diff --git a/biscuit/core/gui.py b/biscuit/core/gui.py index 982db338..d5c6db28 100644 --- a/biscuit/core/gui.py +++ b/biscuit/core/gui.py @@ -107,6 +107,7 @@ def late_setup(self) -> None: self.editorsmanager.generate_actionsets() self.settings.late_setup() self.history.generate_actionsets() + self.git.late_setup() # force set focus on this window self.focus_set() diff --git a/biscuit/core/layout/statusbar/__init__.py b/biscuit/core/layout/statusbar/__init__.py index 35c18869..9563bac6 100644 --- a/biscuit/core/layout/statusbar/__init__.py +++ b/biscuit/core/layout/statusbar/__init__.py @@ -40,17 +40,10 @@ def __init__(self, master: Root, *args, **kwargs) -> None: super().__init__(master, *args, **kwargs) self.config(bg=self.base.theme.layout.statusbar.background) - # TODO add a button for toggling panel, left side, "terminal-bash", color - self.branch = TerminalButton(self, icon="terminal-bash", function=self.toggle_terminal, description="Toggle terminal", padx=10) - self.branch.pack(side=tk.LEFT) - - # git info - self.git_actionset = ActionSet( - "Manage git branches", "branch:", #TODO pinned `create new branch` item - [("main", lambda e=None: print("main", e)), - ("rewrite", lambda e=None: print("rewrite", e))], - ) - self.base.palette.register_actionset(lambda: self.git_actionset) + self.terminal_toggle = TerminalButton(self, icon="terminal-bash", function=self.toggle_terminal, description="Toggle terminal", padx=10) + self.terminal_toggle.pack(side=tk.LEFT) + + # git branch info self.branch = SButton(self, text="master", icon="source-control", function=self.base.events.change_git_branch, description="Checkout branch") self.branch.set_pack_data(side=tk.LEFT, padx=(2, 0)) @@ -134,7 +127,9 @@ def update_git_info(self) -> None: if self.base.git_found: self.branch.show() self.branch.change_text("{0}".format(self.base.git.active_branch)) - self.git_actionset.update([(str(branch), lambda e=None: self.base.git.checkout(str(branch))) for branch in self.base.git.repo.branches]) + + # following has been moved to `git.update_repo_info` + # self.git_actionset.update([(str(branch), lambda e=None: self.base.git.checkout(str(branch))) for branch in self.base.git.repo.branches]) else: self.branch.hide()