diff --git a/quickgit/git_operations.py b/quickgit/git_operations.py index 77b58ad..df126fc 100644 --- a/quickgit/git_operations.py +++ b/quickgit/git_operations.py @@ -26,12 +26,15 @@ class GitOperations: Returns: 是否成功 """ + from .utils import Colors + if self.is_git_repo(): OutputFormatter.warning("当前目录已经是Git仓库") return True + print(f"{Colors.CYAN}{'-' * 60}{Colors.ENDC}") # 初始化Git仓库 - OutputFormatter.info("正在初始化Git仓库...") + OutputFormatter.step(1, "初始化Git仓库...") success, _ = self.executor.run("git init", show_output=False) if not success: OutputFormatter.error("Git初始化失败") @@ -39,7 +42,7 @@ class GitOperations: OutputFormatter.success("Git仓库初始化成功") # 创建main分支 - OutputFormatter.info("正在创建main分支...") + OutputFormatter.step(2, "创建main分支...") success, _ = self.executor.run(f"git checkout -b {Config.DEFAULT_BRANCH}", show_output=False) if success: OutputFormatter.success("main分支创建成功") @@ -47,10 +50,11 @@ class GitOperations: OutputFormatter.warning("main分支创建失败,将使用默认分支") # 创建.gitignore文件 + OutputFormatter.step(3, "创建.gitignore文件...") self._create_gitignore() # 首次提交 - OutputFormatter.info("正在进行首次提交...") + OutputFormatter.step(4, "进行首次提交...") self.executor.run("git add .", show_output=False) success, _ = self.executor.run('git commit -m "first commit"', show_output=False) if success: @@ -58,11 +62,11 @@ class GitOperations: else: OutputFormatter.warning("首次提交失败(可能没有文件可提交)") + print(f"{Colors.CYAN}{'-' * 60}{Colors.ENDC}") return True def _create_gitignore(self): """创建.gitignore文件""" - OutputFormatter.info("正在创建.gitignore文件...") try: with open('.gitignore', 'w', encoding='utf-8') as f: f.write(Config.GITIGNORE_TEMPLATE) @@ -104,7 +108,7 @@ class GitOperations: OutputFormatter.error("添加文件失败") return success - def commit(self, message: str = None) -> bool: + def commit(self, message: str) -> bool: """ 提交更改 @@ -117,7 +121,7 @@ class GitOperations: if not message: message = f"update: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" - OutputFormatter.info("正在提交更改...") + OutputFormatter.status('running', f"提交更改: {message}") success, _ = self.executor.run(f'git commit -m "{message}"', show_output=True) if success: OutputFormatter.success("提交成功") @@ -137,7 +141,7 @@ class GitOperations: return branch.strip() return Config.DEFAULT_BRANCH - def push(self, remote: str, branch: str = None) -> bool: + def push(self, remote: str, branch: str = "") -> bool: """ 推送到远程仓库 @@ -151,7 +155,7 @@ class GitOperations: if not branch: branch = self.get_current_branch() - OutputFormatter.info(f"正在推送到 {remote}...") + OutputFormatter.status('running', f"推送到 {remote}/{branch}...") # 先尝试直接推送 success, _ = self.executor.run(f"git push {remote} {branch}", show_output=True) @@ -168,7 +172,7 @@ class GitOperations: return success - def pull(self, remote: str, branch: str = None) -> bool: + def pull(self, remote: str, branch: str = "") -> bool: """ 从远程仓库拉取 @@ -182,7 +186,7 @@ class GitOperations: if not branch: branch = self.get_current_branch() - OutputFormatter.info(f"正在从 {remote} 拉取 {branch} 分支...") + OutputFormatter.status('running', f"从 {remote}/{branch} 拉取更新...") success, _ = self.executor.run(f"git pull {remote} {branch}", show_output=True) if success: @@ -196,12 +200,15 @@ class GitOperations: """显示仓库状态""" from .utils import Colors - print(f"{Colors.CYAN}当前目录:{Colors.ENDC} {self.current_dir}\n") + print(f"{Colors.CYAN}{'-' * 60}{Colors.ENDC}") + OutputFormatter.key_value(">> 当前目录", self.current_dir, Colors.BRIGHT_YELLOW) + print(f"{Colors.CYAN}{'-' * 60}{Colors.ENDC}") # Git状态 - OutputFormatter.info("Git状态:") + print(f"{Colors.BRIGHT_BLUE}>> Git状态:{Colors.ENDC}") self.executor.run("git status", show_output=True) # 最近提交 - print(f"\n{Colors.CYAN}最近3次提交:{Colors.ENDC}") + print(f"\n{Colors.BRIGHT_MAGENTA}>> 最近3次提交:{Colors.ENDC}") self.executor.run("git log --oneline -3", show_output=True) + print(f"{Colors.CYAN}{'-' * 60}{Colors.ENDC}") diff --git a/quickgit/remote_manager.py b/quickgit/remote_manager.py index 326b841..853b128 100644 --- a/quickgit/remote_manager.py +++ b/quickgit/remote_manager.py @@ -27,42 +27,46 @@ class RemoteManager: def show_remotes(self): """显示所有远程仓库""" from .utils import Colors - print(f"\n{Colors.CYAN}远程仓库:{Colors.ENDC}") + print(f"{Colors.CYAN}{'-' * 60}{Colors.ENDC}") + print(f"{Colors.BRIGHT_CYAN}>> 远程仓库列表:{Colors.ENDC}") self.executor.run("git remote -v", show_output=True) + print(f"{Colors.CYAN}{'-' * 60}{Colors.ENDC}") - def add_github_remote(self, repo_name: str = None) -> bool: + def add_github_remote(self, repo_name: str = "") -> bool: """ 添加GitHub远程仓库 Args: - repo_name: 仓库名(如果为None则提示用户输入) + repo_name: 仓库名(如果为空则提示用户输入) Returns: 是否成功 """ if not repo_name: - repo_name = InputValidator.get_input("\n请输入GitHub仓库名: ") + from .utils import Colors + repo_name = InputValidator.get_input(f"{Colors.BRIGHT_CYAN}>> 请输入GitHub仓库名: {Colors.ENDC}") remote_url = f"git@github.com:{Config.GITHUB_USER}/{repo_name}.git" return self._add_remote("github", remote_url) - def add_gitea_remote(self, user: str = None, repo_name: str = None) -> bool: + def add_gitea_remote(self, user: str = "", repo_name: str = "") -> bool: """ 添加Gitea远程仓库 Args: - user: Gitea用户名(如果为None则提示用户输入) - repo_name: 仓库名(如果为None则提示用户输入) + user: Gitea用户名(如果为空则提示用户输入) + repo_name: 仓库名(如果为空则提示用户输入) Returns: 是否成功 """ + from .utils import Colors if not user: - user = InputValidator.get_input("\n请输入Gitea用户名: ") + user = InputValidator.get_input(f"{Colors.BRIGHT_CYAN}>> 请输入Gitea用户名: {Colors.ENDC}") if not repo_name: - repo_name = InputValidator.get_input("请输入Gitea仓库名: ") + repo_name = InputValidator.get_input(f"{Colors.BRIGHT_CYAN}>> 请输入Gitea仓库名: {Colors.ENDC}") remote_url = f"ssh://git@{Config.GITEA_HOST}:{Config.GITEA_PORT}/{user}/{repo_name}.git" @@ -95,16 +99,17 @@ class RemoteManager: return success - def remove_remote(self, name: str = None) -> bool: + def remove_remote(self, name: str = "") -> bool: """ 删除远程仓库 Args: - name: 远程仓库名(如果为None则提示用户选择) + name: 远程仓库名(如果为空则提示用户选择) Returns: 是否成功 """ + from .utils import Colors remotes = self.get_remotes() if not remotes: @@ -113,12 +118,14 @@ class RemoteManager: if not name: # 让用户选择要删除的远程仓库 - print("\n当前远程仓库:") + print(f"{Colors.CYAN}{'-' * 60}{Colors.ENDC}") + print(f"{Colors.BRIGHT_YELLOW}>> 当前远程仓库:{Colors.ENDC}") for idx, remote in enumerate(remotes, 1): - print(f"{idx}. {remote}") + OutputFormatter.menu_item(idx, remote) + print(f"{Colors.CYAN}{'-' * 60}{Colors.ENDC}") choice = InputValidator.get_choice( - f"\n请选择要删除的远程仓库 [1-{len(remotes)}]: ", + f"{Colors.BRIGHT_CYAN}>> 请选择要删除的远程仓库 [1-{len(remotes)}]: {Colors.ENDC}", range(1, len(remotes) + 1) ) @@ -137,15 +144,20 @@ class RemoteManager: def configure_remotes_interactive(self): """交互式配置远程仓库""" - OutputFormatter.info("\n配置远程仓库...") + from .utils import Colors + print(f"\n{Colors.BRIGHT_MAGENTA}>> 配置远程仓库{Colors.ENDC}") + print(f"{Colors.CYAN}{'-' * 60}{Colors.ENDC}") - print("\n请选择要配置的远程仓库:") - print("1. GitHub") - print("2. Gitea") - print("3. 两者都配置") - print("4. 跳过") + OutputFormatter.menu_item(1, "GitHub") + OutputFormatter.menu_item(2, "Gitea") + OutputFormatter.menu_item(3, "两者都配置") + OutputFormatter.menu_item(4, "跳过") + print(f"{Colors.CYAN}{'-' * 60}{Colors.ENDC}") - choice = InputValidator.get_choice("\n请选择 [1-4]: ", range(1, 5)) + choice = InputValidator.get_choice( + f"{Colors.BRIGHT_CYAN}>> 请选择 [1-4]: {Colors.ENDC}", + range(1, 5) + ) if choice == 1: self.add_github_remote() @@ -164,19 +176,22 @@ class RemoteManager: Returns: 选中的远程仓库列表 """ + from .utils import Colors remotes = self.get_remotes() if not remotes: OutputFormatter.warning("没有配置远程仓库") return [] - print("\n可用的远程仓库:") + print(f"\n{Colors.BRIGHT_YELLOW}>> 可用的远程仓库:{Colors.ENDC}") + print(f"{Colors.CYAN}{'-' * 60}{Colors.ENDC}") for idx, remote in enumerate(remotes, 1): - print(f"{idx}. {remote}") - print(f"{len(remotes) + 1}. 全部推送") + OutputFormatter.menu_item(idx, remote) + OutputFormatter.menu_item(len(remotes) + 1, "全部推送") + print(f"{Colors.CYAN}{'-' * 60}{Colors.ENDC}") choice = InputValidator.get_choice( - f"\n请选择要推送的远程仓库 [1-{len(remotes) + 1}]: ", + f"{Colors.BRIGHT_CYAN}>> 请选择 [1-{len(remotes) + 1}]: {Colors.ENDC}", range(1, len(remotes) + 2) ) @@ -190,17 +205,27 @@ class RemoteManager: 选择要拉取的远程仓库 Returns: - 选中的远程仓库名 + 选中的远程仓库名,如果没有配置则返回空字符串 """ + from .utils import Colors remotes = self.get_remotes() if not remotes: OutputFormatter.warning("没有配置远程仓库") - return None + return "" - print("\n可用的远程仓库:") + print(f"\n{Colors.BRIGHT_YELLOW}>> 可用的远程仓库:{Colors.ENDC}") + print(f"{Colors.CYAN}{'-' * 60}{Colors.ENDC}") for idx, remote in enumerate(remotes, 1): - print(f"{idx}. {remote}") + OutputFormatter.menu_item(idx, remote) + print(f"{Colors.CYAN}{'-' * 60}{Colors.ENDC}") + + choice = InputValidator.get_choice( + f"{Colors.BRIGHT_CYAN}>> 请选择 [1-{len(remotes)}]: {Colors.ENDC}", + range(1, len(remotes) + 1) + ) + + return remotes[choice - 1] choice = InputValidator.get_choice( f"\n请选择要拉取的远程仓库 [1-{len(remotes)}]: ", diff --git a/quickgit/ui.py b/quickgit/ui.py index 8d10328..ffcd399 100644 --- a/quickgit/ui.py +++ b/quickgit/ui.py @@ -16,19 +16,33 @@ class GitManagerUI: def show_welcome(self): """显示欢迎信息""" - OutputFormatter.header("萌芽一键Git管理工具 - QuickGit") - print(f"{Colors.CYAN}当前目录:{Colors.ENDC} {self.git_ops.current_dir}") - print(f"{Colors.CYAN}Git仓库:{Colors.ENDC} {'是' if self.git_ops.is_git_repo() else '否'}") + from .utils import Colors + + # 简洁的标题 + print(f"\n{Colors.BRIGHT_CYAN}{'=' * 60}") + print(f"{Colors.BRIGHT_MAGENTA}{Colors.BOLD} QuickGit - 萌芽一键Git管理工具 v1.0{Colors.ENDC}") + print(f"{Colors.BRIGHT_CYAN}{'=' * 60}{Colors.ENDC}") + + # 显示当前状态 + is_repo = self.git_ops.is_git_repo() + repo_status = f"{Colors.BRIGHT_GREEN}[已初始化]{Colors.ENDC}" if is_repo else f"{Colors.BRIGHT_RED}[未初始化]{Colors.ENDC}" + + print(f"{Colors.BRIGHT_YELLOW}当前目录:{Colors.ENDC} {Colors.WHITE}{self.git_ops.current_dir}{Colors.ENDC}") + print(f"{Colors.BRIGHT_YELLOW}Git状态:{Colors.ENDC} {repo_status}") + print(f"{Colors.CYAN}{'-' * 60}{Colors.ENDC}") def show_main_menu(self): """显示主菜单""" - print(f"\n{Colors.BOLD}请选择操作:{Colors.ENDC}") - print("1. 初始化Git仓库") - print("2. 提交并推送更改") - print("3. 从远程仓库拉取") - print("4. 查看仓库状态") - print("5. 管理远程仓库") - print("6. 退出") + print(f"\n{Colors.BRIGHT_MAGENTA}{Colors.BOLD}>> 主菜单{Colors.ENDC}") + print(f"{Colors.CYAN}{'-' * 60}{Colors.ENDC}") + + OutputFormatter.menu_item(1, "初始化Git仓库") + OutputFormatter.menu_item(2, "提交并推送更改") + OutputFormatter.menu_item(3, "从远程仓库拉取") + OutputFormatter.menu_item(4, "查看仓库状态") + OutputFormatter.menu_item(5, "管理远程仓库") + OutputFormatter.menu_item(6, "退出程序") + print(f"{Colors.CYAN}{'-' * 60}{Colors.ENDC}") def handle_init_repo(self): """处理初始化仓库""" @@ -47,20 +61,21 @@ class GitManagerUI: return # 检查是否有更改 - OutputFormatter.info("检查文件更改...") + OutputFormatter.status('running', "检查文件更改中...") if not self.git_ops.has_changes(): OutputFormatter.warning("没有文件更改,无需提交") return # 显示更改 - print("\n当前更改的文件:") + print(f"\n{Colors.BRIGHT_YELLOW}>> 当前更改的文件:{Colors.ENDC}") _, status = self.git_ops.get_status() - print(status) + print(f"{Colors.CYAN}{status}{Colors.ENDC}") + print(f"{Colors.CYAN}{'-' * 60}{Colors.ENDC}") # 输入提交信息 commit_msg = InputValidator.get_input( - "\n请输入提交信息 (直接回车使用默认信息): ", + f"{Colors.BRIGHT_CYAN}>> 请输入提交信息 (回车使用默认): {Colors.ENDC}", allow_empty=True ) @@ -68,7 +83,7 @@ class GitManagerUI: if not self.git_ops.add_all(): return - if not self.git_ops.commit(commit_msg if commit_msg else None): + if not self.git_ops.commit(commit_msg or ""): return # 推送到远程仓库 @@ -111,14 +126,20 @@ class GitManagerUI: return while True: - print("\n远程仓库管理:") - print("1. 查看远程仓库") - print("2. 添加GitHub远程仓库") - print("3. 添加Gitea远程仓库") - print("4. 删除远程仓库") - print("5. 返回主菜单") + print(f"\n{Colors.BRIGHT_BLUE}{Colors.BOLD}>> 远程仓库管理{Colors.ENDC}") + print(f"{Colors.CYAN}{'-' * 60}{Colors.ENDC}") - choice = InputValidator.get_choice("\n请选择 [1-5]: ", range(1, 6)) + OutputFormatter.menu_item(1, "查看远程仓库") + OutputFormatter.menu_item(2, "添加GitHub远程仓库") + OutputFormatter.menu_item(3, "添加Gitea远程仓库") + OutputFormatter.menu_item(4, "删除远程仓库") + OutputFormatter.menu_item(5, "返回主菜单") + print(f"{Colors.CYAN}{'-' * 60}{Colors.ENDC}") + + choice = InputValidator.get_choice( + f"{Colors.BRIGHT_CYAN}>> 请选择 [1-5]: {Colors.ENDC}", + range(1, 6) + ) if choice == 1: self.remote_mgr.show_remotes() @@ -139,7 +160,7 @@ class GitManagerUI: self.show_main_menu() choice = InputValidator.get_choice( - f"\n{Colors.BOLD}请输入选项 [1-6]: {Colors.ENDC}", + f"{Colors.BRIGHT_MAGENTA}>> 请输入选项 [1-6]: {Colors.ENDC}", range(1, 7) ) @@ -154,5 +175,7 @@ class GitManagerUI: elif choice == 5: self.handle_manage_remotes() elif choice == 6: - OutputFormatter.success("感谢使用萌芽Git管理工具!") + print(f"\n{Colors.BRIGHT_GREEN}{'=' * 60}") + print(f"{Colors.BRIGHT_GREEN}{Colors.BOLD} 感谢使用QuickGit!祝您工作愉快!{Colors.ENDC}") + print(f"{Colors.BRIGHT_GREEN}{'=' * 60}{Colors.ENDC}\n") break diff --git a/quickgit/utils.py b/quickgit/utils.py index 77ff090..ece8493 100644 --- a/quickgit/utils.py +++ b/quickgit/utils.py @@ -7,14 +7,41 @@ import subprocess class Colors: """控制台颜色""" - HEADER = '\033[95m' - BLUE = '\033[94m' - CYAN = '\033[96m' - GREEN = '\033[92m' + # 基础颜色 + HEADER = '\033[95m' # 紫色 + BLUE = '\033[94m' # 蓝色 + CYAN = '\033[96m' # 青色 + GREEN = '\033[92m' # 绿色 + YELLOW = '\033[93m' # 黄色 + RED = '\033[91m' # 红色 + MAGENTA = '\033[35m' # 品红 + WHITE = '\033[97m' # 白色 + + # 亮色系 + BRIGHT_CYAN = '\033[96m\033[1m' # 亮青色 + BRIGHT_GREEN = '\033[92m\033[1m' # 亮绿色 + BRIGHT_YELLOW = '\033[93m\033[1m' # 亮黄色 + BRIGHT_RED = '\033[91m\033[1m' # 亮红色 + BRIGHT_BLUE = '\033[94m\033[1m' # 亮蓝色 + BRIGHT_MAGENTA = '\033[95m\033[1m' # 亮品红 + + # 背景色 + BG_BLUE = '\033[44m' + BG_GREEN = '\033[42m' + BG_YELLOW = '\033[43m' + BG_RED = '\033[41m' + BG_CYAN = '\033[46m' + BG_MAGENTA = '\033[45m' + + # 样式 + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + REVERSE = '\033[7m' + ENDC = '\033[0m' + + # 兼容旧代码 WARNING = '\033[93m' FAIL = '\033[91m' - ENDC = '\033[0m' - BOLD = '\033[1m' class CommandExecutor: @@ -58,30 +85,79 @@ class OutputFormatter: @staticmethod def header(text: str): - """打印标题""" - print(f"\n{Colors.HEADER}{Colors.BOLD}{'='*50}") - print(f" {text}") - print(f"{'='*50}{Colors.ENDC}\n") + """打印标题 - 顶部大标题""" + line = "=" * 60 + print(f"\n{Colors.BRIGHT_CYAN}{line}") + print(f"{Colors.BRIGHT_MAGENTA}{Colors.BOLD}>> {text}") + print(f"{Colors.BRIGHT_CYAN}{line}{Colors.ENDC}") + + @staticmethod + def section(text: str): + """打印分节标题""" + print(f"\n{Colors.BRIGHT_YELLOW}{'=' * 60}") + print(f"{Colors.BRIGHT_BLUE}{Colors.BOLD}>> {text}") + print(f"{Colors.BRIGHT_YELLOW}{'=' * 60}{Colors.ENDC}") + + @staticmethod + def divider(): + """打印分割线""" + print(f"{Colors.CYAN}{'-' * 60}{Colors.ENDC}") + + @staticmethod + def thin_divider(): + """打印细分割线""" + print(f"{Colors.CYAN}{'·' * 60}{Colors.ENDC}") @staticmethod def success(text: str): """打印成功信息""" - print(f"{Colors.GREEN}✓ {text}{Colors.ENDC}") + print(f"{Colors.BRIGHT_GREEN}[√]{Colors.ENDC} {Colors.GREEN}{text}{Colors.ENDC}") @staticmethod def error(text: str): """打印错误信息""" - print(f"{Colors.FAIL}✗ {text}{Colors.ENDC}") + print(f"{Colors.BRIGHT_RED}[×]{Colors.ENDC} {Colors.RED}{text}{Colors.ENDC}") @staticmethod def info(text: str): """打印提示信息""" - print(f"{Colors.CYAN}ℹ {text}{Colors.ENDC}") + print(f"{Colors.BRIGHT_CYAN}[i]{Colors.ENDC} {Colors.CYAN}{text}{Colors.ENDC}") @staticmethod def warning(text: str): """打印警告信息""" - print(f"{Colors.WARNING}⚠ {text}{Colors.ENDC}") + print(f"{Colors.BRIGHT_YELLOW}[!]{Colors.ENDC} {Colors.YELLOW}{text}{Colors.ENDC}") + + @staticmethod + def step(num: int, text: str): + """打印步骤信息""" + print(f"{Colors.BRIGHT_BLUE}[{num}]{Colors.ENDC} {Colors.WHITE}{text}{Colors.ENDC}") + + @staticmethod + def menu_item(num: int, text: str, icon: str = ">>"): + """打印菜单项""" + print(f"{Colors.BRIGHT_CYAN}[{num}]{Colors.ENDC} {Colors.WHITE}{text}{Colors.ENDC}") + + @staticmethod + def key_value(key: str, value: str, key_color=None, value_color=None): + """打印键值对""" + if key_color is None: + key_color = Colors.BRIGHT_CYAN + if value_color is None: + value_color = Colors.WHITE + print(f"{key_color}{key}:{Colors.ENDC} {value_color}{value}{Colors.ENDC}") + + @staticmethod + def status(status_type: str, text: str): + """打印状态信息""" + icons = { + 'pending': f"{Colors.YELLOW}[·]{Colors.ENDC}", + 'running': f"{Colors.BRIGHT_CYAN}[>]{Colors.ENDC}", + 'success': f"{Colors.BRIGHT_GREEN}[√]{Colors.ENDC}", + 'error': f"{Colors.BRIGHT_RED}[×]{Colors.ENDC}", + } + icon = icons.get(status_type, "[·]") + print(f"{icon} {text}") class InputValidator: diff --git a/run.bat b/run.bat new file mode 100644 index 0000000..a48943b --- /dev/null +++ b/run.bat @@ -0,0 +1,4 @@ +@echo off +chcp 65001 >nul +python quickgit.py +pause