模块化重构代码

This commit is contained in:
2026-02-14 01:08:38 +08:00
parent f07619c5b4
commit ca8ff7db82
9 changed files with 896 additions and 563 deletions

6
quickgit/__init__.py Normal file
View File

@@ -0,0 +1,6 @@
"""
QuickGit - 萌芽一键Git管理工具
"""
__version__ = "1.0.0"
__author__ = "shumengya"

84
quickgit/config.py Normal file
View File

@@ -0,0 +1,84 @@
"""
配置模块 - 存储项目配置信息
"""
class Config:
"""配置类"""
# Gitea服务器配置
GITEA_HOST = "repo.shumengya.top"
GITEA_PORT = "8022"
# GitHub配置
GITHUB_USER = "shumengya"
# Git配置
DEFAULT_BRANCH = "main"
# .gitignore模板
GITIGNORE_TEMPLATE = """# Node/React
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
build/
dist/
coverage/
.env.local
.env.development.local
.env.test.local
.env.production.local
# Go
*.exe
*.exe~
*.test
*.out
*.dll
*.so
*.dylib
vendor/
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
env/
venv/
ENV/
*.egg-info/
dist/
build/
# 数据文件
data/data.json
*.db
*.sqlite
# 日志文件
*.log
logs/
# 操作系统
.DS_Store
Thumbs.db
desktop.ini
# IDE
.idea/
.vscode/
*.swp
*.swo
*~
.project
.classpath
.settings/
# 其他
*.bak
*.tmp
*.temp
"""

207
quickgit/git_operations.py Normal file
View File

@@ -0,0 +1,207 @@
"""
Git操作模块 - 提供Git基本操作功能
"""
import os
from datetime import datetime
from .utils import CommandExecutor, OutputFormatter
from .config import Config
class GitOperations:
"""Git操作类"""
def __init__(self):
self.executor = CommandExecutor()
self.current_dir = os.getcwd()
def is_git_repo(self) -> bool:
"""检查当前目录是否是Git仓库"""
return os.path.isdir('.git')
def init_repo(self) -> bool:
"""
初始化Git仓库
Returns:
是否成功
"""
if self.is_git_repo():
OutputFormatter.warning("当前目录已经是Git仓库")
return True
# 初始化Git仓库
OutputFormatter.info("正在初始化Git仓库...")
success, _ = self.executor.run("git init", show_output=False)
if not success:
OutputFormatter.error("Git初始化失败")
return False
OutputFormatter.success("Git仓库初始化成功")
# 创建main分支
OutputFormatter.info("正在创建main分支...")
success, _ = self.executor.run(f"git checkout -b {Config.DEFAULT_BRANCH}", show_output=False)
if success:
OutputFormatter.success("main分支创建成功")
else:
OutputFormatter.warning("main分支创建失败将使用默认分支")
# 创建.gitignore文件
self._create_gitignore()
# 首次提交
OutputFormatter.info("正在进行首次提交...")
self.executor.run("git add .", show_output=False)
success, _ = self.executor.run('git commit -m "first commit"', show_output=False)
if success:
OutputFormatter.success("首次提交完成")
else:
OutputFormatter.warning("首次提交失败(可能没有文件可提交)")
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)
OutputFormatter.success(".gitignore文件创建成功")
except Exception as e:
OutputFormatter.error(f".gitignore文件创建失败: {str(e)}")
def get_status(self) -> tuple[bool, str]:
"""
获取Git状态
Returns:
(是否成功, 状态输出)
"""
return self.executor.run("git status --short", show_output=False)
def has_changes(self) -> bool:
"""
检查是否有更改
Returns:
是否有更改
"""
success, output = self.get_status()
return success and bool(output.strip())
def add_all(self) -> bool:
"""
添加所有更改
Returns:
是否成功
"""
OutputFormatter.info("正在添加文件...")
success, _ = self.executor.run("git add .", show_output=False)
if success:
OutputFormatter.success("文件添加成功")
else:
OutputFormatter.error("添加文件失败")
return success
def commit(self, message: str = None) -> bool:
"""
提交更改
Args:
message: 提交信息
Returns:
是否成功
"""
if not message:
message = f"update: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
OutputFormatter.info("正在提交更改...")
success, _ = self.executor.run(f'git commit -m "{message}"', show_output=True)
if success:
OutputFormatter.success("提交成功")
else:
OutputFormatter.error("提交失败")
return success
def get_current_branch(self) -> str:
"""
获取当前分支
Returns:
分支名
"""
success, branch = self.executor.run("git branch --show-current", show_output=False)
if success and branch.strip():
return branch.strip()
return Config.DEFAULT_BRANCH
def push(self, remote: str, branch: str = None) -> bool:
"""
推送到远程仓库
Args:
remote: 远程仓库名
branch: 分支名(默认为当前分支)
Returns:
是否成功
"""
if not branch:
branch = self.get_current_branch()
OutputFormatter.info(f"正在推送到 {remote}...")
# 先尝试直接推送
success, _ = self.executor.run(f"git push {remote} {branch}", show_output=True)
if not success:
# 如果失败,尝试设置上游分支
OutputFormatter.info("尝试设置上游分支...")
success, _ = self.executor.run(f"git push -u {remote} {branch}", show_output=True)
if success:
OutputFormatter.success(f"成功推送到 {remote}")
else:
OutputFormatter.error(f"推送到 {remote} 失败")
return success
def pull(self, remote: str, branch: str = None) -> bool:
"""
从远程仓库拉取
Args:
remote: 远程仓库名
branch: 分支名(默认为当前分支)
Returns:
是否成功
"""
if not branch:
branch = self.get_current_branch()
OutputFormatter.info(f"正在从 {remote} 拉取 {branch} 分支...")
success, _ = self.executor.run(f"git pull {remote} {branch}", show_output=True)
if success:
OutputFormatter.success(f"成功从 {remote} 拉取更新")
else:
OutputFormatter.error(f"{remote} 拉取失败")
return success
def show_status(self):
"""显示仓库状态"""
from .utils import Colors
print(f"{Colors.CYAN}当前目录:{Colors.ENDC} {self.current_dir}\n")
# Git状态
OutputFormatter.info("Git状态")
self.executor.run("git status", show_output=True)
# 最近提交
print(f"\n{Colors.CYAN}最近3次提交{Colors.ENDC}")
self.executor.run("git log --oneline -3", show_output=True)

210
quickgit/remote_manager.py Normal file
View File

@@ -0,0 +1,210 @@
"""
远程仓库管理模块 - 处理GitHub和Gitea远程仓库的管理
"""
from .utils import CommandExecutor, OutputFormatter, InputValidator
from .config import Config
class RemoteManager:
"""远程仓库管理器"""
def __init__(self):
self.executor = CommandExecutor()
def get_remotes(self) -> list[str]:
"""
获取所有远程仓库
Returns:
远程仓库列表
"""
success, output = self.executor.run("git remote", show_output=False)
if success and output.strip():
return [r.strip() for r in output.strip().split('\n') if r.strip()]
return []
def show_remotes(self):
"""显示所有远程仓库"""
from .utils import Colors
print(f"\n{Colors.CYAN}远程仓库:{Colors.ENDC}")
self.executor.run("git remote -v", show_output=True)
def add_github_remote(self, repo_name: str = None) -> bool:
"""
添加GitHub远程仓库
Args:
repo_name: 仓库名如果为None则提示用户输入
Returns:
是否成功
"""
if not repo_name:
repo_name = InputValidator.get_input("\n请输入GitHub仓库名: ")
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:
"""
添加Gitea远程仓库
Args:
user: Gitea用户名如果为None则提示用户输入
repo_name: 仓库名如果为None则提示用户输入
Returns:
是否成功
"""
if not user:
user = InputValidator.get_input("\n请输入Gitea用户名: ")
if not repo_name:
repo_name = InputValidator.get_input("请输入Gitea仓库名: ")
remote_url = f"ssh://git@{Config.GITEA_HOST}:{Config.GITEA_PORT}/{user}/{repo_name}.git"
return self._add_remote("gitea", remote_url)
def _add_remote(self, name: str, url: str) -> bool:
"""
添加远程仓库
Args:
name: 远程仓库名称
url: 远程仓库URL
Returns:
是否成功
"""
# 检查remote是否已存在
remotes = self.get_remotes()
if name in remotes:
if InputValidator.confirm(f"远程仓库 '{name}' 已存在,是否覆盖?", default=False):
self.executor.run(f"git remote remove {name}", show_output=False)
else:
return False
success, _ = self.executor.run(f'git remote add {name} {url}', show_output=False)
if success:
OutputFormatter.success(f"{name.capitalize()}远程仓库已添加: {url}")
else:
OutputFormatter.error(f"{name.capitalize()}远程仓库添加失败")
return success
def remove_remote(self, name: str = None) -> bool:
"""
删除远程仓库
Args:
name: 远程仓库名如果为None则提示用户选择
Returns:
是否成功
"""
remotes = self.get_remotes()
if not remotes:
OutputFormatter.warning("没有配置远程仓库")
return False
if not name:
# 让用户选择要删除的远程仓库
print("\n当前远程仓库:")
for idx, remote in enumerate(remotes, 1):
print(f"{idx}. {remote}")
choice = InputValidator.get_choice(
f"\n请选择要删除的远程仓库 [1-{len(remotes)}]: ",
range(1, len(remotes) + 1)
)
name = remotes[choice - 1]
if not InputValidator.confirm(f"确认删除远程仓库 '{name}'?", default=False):
return False
success, _ = self.executor.run(f"git remote remove {name}", show_output=False)
if success:
OutputFormatter.success(f"远程仓库 '{name}' 已删除")
else:
OutputFormatter.error("删除失败")
return success
def configure_remotes_interactive(self):
"""交互式配置远程仓库"""
OutputFormatter.info("\n配置远程仓库...")
print("\n请选择要配置的远程仓库:")
print("1. GitHub")
print("2. Gitea")
print("3. 两者都配置")
print("4. 跳过")
choice = InputValidator.get_choice("\n请选择 [1-4]: ", range(1, 5))
if choice == 1:
self.add_github_remote()
elif choice == 2:
self.add_gitea_remote()
elif choice == 3:
self.add_github_remote()
self.add_gitea_remote()
else:
OutputFormatter.info("跳过远程仓库配置")
def select_remote_for_push(self) -> list[str]:
"""
选择要推送的远程仓库
Returns:
选中的远程仓库列表
"""
remotes = self.get_remotes()
if not remotes:
OutputFormatter.warning("没有配置远程仓库")
return []
print("\n可用的远程仓库:")
for idx, remote in enumerate(remotes, 1):
print(f"{idx}. {remote}")
print(f"{len(remotes) + 1}. 全部推送")
choice = InputValidator.get_choice(
f"\n请选择要推送的远程仓库 [1-{len(remotes) + 1}]: ",
range(1, len(remotes) + 2)
)
if choice == len(remotes) + 1:
return remotes
else:
return [remotes[choice - 1]]
def select_remote_for_pull(self) -> str:
"""
选择要拉取的远程仓库
Returns:
选中的远程仓库名
"""
remotes = self.get_remotes()
if not remotes:
OutputFormatter.warning("没有配置远程仓库")
return None
print("\n可用的远程仓库:")
for idx, remote in enumerate(remotes, 1):
print(f"{idx}. {remote}")
choice = InputValidator.get_choice(
f"\n请选择要拉取的远程仓库 [1-{len(remotes)}]: ",
range(1, len(remotes) + 1)
)
return remotes[choice - 1]

158
quickgit/ui.py Normal file
View File

@@ -0,0 +1,158 @@
"""
UI交互模块 - 处理用户界面和交互逻辑
"""
from .git_operations import GitOperations
from .remote_manager import RemoteManager
from .utils import OutputFormatter, InputValidator, Colors
class GitManagerUI:
"""Git管理器UI"""
def __init__(self):
self.git_ops = GitOperations()
self.remote_mgr = RemoteManager()
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 ''}")
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. 退出")
def handle_init_repo(self):
"""处理初始化仓库"""
OutputFormatter.header("初始化Git仓库")
if self.git_ops.init_repo():
# 配置远程仓库
self.remote_mgr.configure_remotes_interactive()
def handle_commit_and_push(self):
"""处理提交并推送"""
OutputFormatter.header("提交并推送更改")
if not self.git_ops.is_git_repo():
OutputFormatter.error("当前目录不是Git仓库请先初始化")
return
# 检查是否有更改
OutputFormatter.info("检查文件更改...")
if not self.git_ops.has_changes():
OutputFormatter.warning("没有文件更改,无需提交")
return
# 显示更改
print("\n当前更改的文件:")
_, status = self.git_ops.get_status()
print(status)
# 输入提交信息
commit_msg = InputValidator.get_input(
"\n请输入提交信息 (直接回车使用默认信息): ",
allow_empty=True
)
# 添加并提交
if not self.git_ops.add_all():
return
if not self.git_ops.commit(commit_msg if commit_msg else None):
return
# 推送到远程仓库
selected_remotes = self.remote_mgr.select_remote_for_push()
for remote in selected_remotes:
self.git_ops.push(remote)
def handle_pull(self):
"""处理拉取"""
OutputFormatter.header("从远程仓库拉取")
if not self.git_ops.is_git_repo():
OutputFormatter.error("当前目录不是Git仓库")
return
remote = self.remote_mgr.select_remote_for_pull()
if remote:
self.git_ops.pull(remote)
def handle_show_status(self):
"""处理显示状态"""
OutputFormatter.header("仓库状态")
if not self.git_ops.is_git_repo():
OutputFormatter.error("当前目录不是Git仓库")
return
self.git_ops.show_status()
# 显示远程仓库
self.remote_mgr.show_remotes()
def handle_manage_remotes(self):
"""处理远程仓库管理"""
OutputFormatter.header("管理远程仓库")
if not self.git_ops.is_git_repo():
OutputFormatter.error("当前目录不是Git仓库")
return
while True:
print("\n远程仓库管理:")
print("1. 查看远程仓库")
print("2. 添加GitHub远程仓库")
print("3. 添加Gitea远程仓库")
print("4. 删除远程仓库")
print("5. 返回主菜单")
choice = InputValidator.get_choice("\n请选择 [1-5]: ", range(1, 6))
if choice == 1:
self.remote_mgr.show_remotes()
elif choice == 2:
self.remote_mgr.add_github_remote()
elif choice == 3:
self.remote_mgr.add_gitea_remote()
elif choice == 4:
self.remote_mgr.remove_remote()
elif choice == 5:
break
def run(self):
"""运行主程序"""
self.show_welcome()
while True:
self.show_main_menu()
choice = InputValidator.get_choice(
f"\n{Colors.BOLD}请输入选项 [1-6]: {Colors.ENDC}",
range(1, 7)
)
if choice == 1:
self.handle_init_repo()
elif choice == 2:
self.handle_commit_and_push()
elif choice == 3:
self.handle_pull()
elif choice == 4:
self.handle_show_status()
elif choice == 5:
self.handle_manage_remotes()
elif choice == 6:
OutputFormatter.success("感谢使用萌芽Git管理工具")
break

150
quickgit/utils.py Normal file
View File

@@ -0,0 +1,150 @@
"""
工具类模块 - 提供命令执行、输出格式化等工具函数
"""
import subprocess
class Colors:
"""控制台颜色"""
HEADER = '\033[95m'
BLUE = '\033[94m'
CYAN = '\033[96m'
GREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
class CommandExecutor:
"""命令执行器"""
@staticmethod
def run(command: str, show_output: bool = True) -> tuple[bool, str]:
"""
执行命令
Args:
command: 要执行的命令
show_output: 是否显示输出
Returns:
(是否成功, 输出内容)
"""
try:
result = subprocess.run(
command,
shell=True,
capture_output=True,
text=True,
encoding='utf-8',
errors='ignore'
)
output = result.stdout + result.stderr
if show_output and output.strip():
print(output)
return result.returncode == 0, output
except Exception as e:
OutputFormatter.error(f"命令执行失败: {str(e)}")
return False, str(e)
class OutputFormatter:
"""输出格式化器"""
@staticmethod
def header(text: str):
"""打印标题"""
print(f"\n{Colors.HEADER}{Colors.BOLD}{'='*50}")
print(f" {text}")
print(f"{'='*50}{Colors.ENDC}\n")
@staticmethod
def success(text: str):
"""打印成功信息"""
print(f"{Colors.GREEN}{text}{Colors.ENDC}")
@staticmethod
def error(text: str):
"""打印错误信息"""
print(f"{Colors.FAIL}{text}{Colors.ENDC}")
@staticmethod
def info(text: str):
"""打印提示信息"""
print(f"{Colors.CYAN} {text}{Colors.ENDC}")
@staticmethod
def warning(text: str):
"""打印警告信息"""
print(f"{Colors.WARNING}{text}{Colors.ENDC}")
class InputValidator:
"""输入验证器"""
@staticmethod
def get_choice(prompt: str, valid_range: range) -> int:
"""
获取用户选择(数字)
Args:
prompt: 提示信息
valid_range: 有效范围
Returns:
用户选择的数字
"""
while True:
try:
choice = input(prompt).strip()
choice_num = int(choice)
if choice_num in valid_range:
return choice_num
else:
OutputFormatter.error(f"请输入 {valid_range.start}-{valid_range.stop-1} 之间的数字")
except ValueError:
OutputFormatter.error("请输入有效的数字")
@staticmethod
def get_input(prompt: str, allow_empty: bool = False) -> str:
"""
获取用户输入
Args:
prompt: 提示信息
allow_empty: 是否允许空输入
Returns:
用户输入的字符串
"""
while True:
user_input = input(prompt).strip()
if user_input or allow_empty:
return user_input
else:
OutputFormatter.error("输入不能为空")
@staticmethod
def confirm(prompt: str, default: bool = False) -> bool:
"""
获取用户确认
Args:
prompt: 提示信息
default: 默认值
Returns:
用户确认结果
"""
suffix = " [Y/n]: " if default else " [y/N]: "
user_input = input(prompt + suffix).strip().lower()
if not user_input:
return default
return user_input in ['y', 'yes']