# 极递 GitGo **Repository Path**: deepin20/GitGo ## Basic Information - **Project Name**: 极递 GitGo - **Description**: 图形化 Git 推送工具 - **Primary Language**: Python - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-06-21 - **Last Updated**: 2026-10-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 极递 GitGo v2.1.6 ## 简介 极递 GitGo 是一款基于 Python + Tkinter 的图形化 Git 工具,支持多仓库管理、自动双向同步、私人令牌认证、分支管理等高级功能,让开发者轻松管理代码版本。 --- ## 核心功能 - **多仓库管理**:添加/移除/切换仓库,每个仓库独立配置;添加时**本地目录手动选、 远程仓库从 Gitee 列表里选**(自动获取账号可见的仓库,含协作与组织仓库) - **新建仓库**:一次把本地目录(不存在则自动创建并 `git init`)、 Gitee 远程仓库(可新建、可跳过)与首次同步做完,并自动登记进 GitGo - **自动同步(方向可选)**: - 同步方式三选一:**双向同步** / **仅本地 → 远程** / **仅远程 → 本地** - 本地 → 云端:文件变更自动 `add → commit → pull --rebase → push` - 云端 → 本地:定时自动 `pull --rebase` - **手动 Git 操作**:提交并推送、仅提交、初始化仓库、选择远程仓库 - **凭证只输入一次**:用户名与私人令牌保存在 `~/.config/gitgo/credentials`(权限 600), 所有仓库共用;新添加的仓库不再重复索要令牌,旧版本散在各仓库 `.git` 里的令牌会自动合并 - **安全认证**:支持 HTTPS(私人令牌)和 SSH - **高级功能**:分支管理(主界面分支选择器,保证本地与远程属于同一分支)、标签管理、Stash 暂存、历史提交查看、统计贡献 - **发行版发布**:自动查找最新 deb 包,一键在 Gitee 创建发行版(版本号、标题、描述、deb 附件上传) - **实时状态栏**:显示远程连接、分支、领先/落后提交数(后台线程查询,不卡界面) --- ## 安装与使用 ### 1. 安装依赖 ```bash pip install -r requirements.txt ``` ### 2. 运行 ```bash python run.py ``` ### 3. 首次启动配置 - 程序首次启动时会自动检测 Git 全局配置(`user.name` 和 `user.email`)。 - 若未设置,会弹出对话框引导您输入姓名和邮箱,并自动执行 `git config --global` 设置。 - **如果跳过输入**,GitGo 不会替你填入 `GitGo User` 之类的占位身份——错误的作者信息 一旦写进提交历史就无法批量修正。此时程序会给出明确警告,并跳过自动同步; 在补好身份之前,请手动执行: ```bash git config --global user.name "你的姓名" git config --global user.email "你的邮箱" ``` --- ## 配置路径 配置文件保存在 `~/.config/gitgo/config.json`,每个仓库独立存储: ```json { "repositories": { "/path/to/repo": { "alias": "myproject", "remote_url": "https://gitee.com/user/repo.git", "last_commit_msg": "更新代码", "last_branch": "main", "auto_sync": true, "auto_pull": true, "sync_mode": "both", "pull_interval": 5, "debounce_delay": 5, "ignore_temp_files": true } }, "current_repo": "/path/to/repo" } ``` | 字段 | 含义 | | --- | --- | | `auto_sync` | 文件变更后自动提交并推送。**只有勾选时才动作**:控制的是对远程有副作用的提交推送,未勾选时改文件不会被自动提交 | | `auto_pull` | 定时自动拉取远程更新 | | `sync_mode` | 同步方式:`both`(双向)/ `push`(仅本地 → 远程)/ `pull`(仅远程 → 本地)。认不出来的值一律当 `both` | | `pull_interval` | 自动拉取间隔(分钟) | | `debounce_delay` | 文件变更防抖延迟(秒),界面可调 | | `ignore_temp_files` | 是否忽略 `.tmp/.log/.swp` 等临时文件 | 两个开关互相独立,可以只开一个: - 只开 `auto_sync`:文件一变就「提交 → 先拉后推」,不会定时拉取; - 只开 `auto_pull`:只做定时拉取,改文件不会被自动提交(文件监控也不会启动); - 都开:两条链路共用一把操作锁,不会有两个 git 进程同时改索引。 `sync_mode` 是凌驾于两个开关之上的**方向约束**,实际动作取两者的交集: `push` 模式下定时拉取不生效,`pull` 模式下自动提交推送不生效。 界面会把被压制的那一项禁用并取消勾选,不会留下「勾着却不会执行」的复选框。 写入采用「临时文件 + 原子替换」,进程中断不会留下半个 JSON。 ### 凭证(只输入一次) | 文件 | 内容 | | --- | --- | | `~/.config/gitgo/credentials` | 用户名 + 私人令牌,**所有仓库共用一份**(格式与 git `store` 后端一致,权限 `600`) | | `<仓库>/.git/config` | 每个仓库一条 `credential.helper = store --file=<上面的路径>`(`--local`,不改写全局 `~/.gitconfig`) | | `<仓库>/.git/gitgo-credentials` | **旧版**位置。首次接管该仓库时会被合并进共享文件并删除,不需要手动处理 | 入口:主界面工具栏「🔑 凭证」,或菜单「工具 → Gitee 账号凭证(只输一次)」。 保存时会调用 Gitee 接口验证令牌(能区分「令牌无效」与「网络不通」)。 --- ## 打包与安装 ### 统信 UOS / deepin 应用商店规范(推荐) ```bash sudo apt install rsync dpkg-dev # 打包工具 bash build_deb.sh # 产物:build-deb/com.gitee.deepin20.gitgo_<版本>_<架构>.deb sudo dpkg -i build-deb/com.gitee.deepin20.gitgo_*.deb sudo apt install -f # 依赖缺失时补齐 ``` 包布局遵循官方《商店打包规范》:应用全部文件落在 `/opt/apps//`,根下只有 `entries/`(desktop 与图标,安装时由系统软链到 `/usr/share/applications` 等目录)、 `files/`(应用本体)与 `info`(JSON 元数据)三样。 `build_deb.sh` 的要点: - **版本号与 appid 的唯一来源**都是 `src/utils/constants.py`(`APP_VERSION` / `APP_ID`), 脚本用 sed 从该文件解析,不要在脚本里写死。 - **没有任何维护脚本**:商店明确规定含「改系统」钩子的包无法上架; `entries/` → 系统目录的软链由 `deepin-home-appstore-daemon` 的 dpkg 触发器 (`interest-noawait /opt/apps`)完成,不是包自己的事。 - **不建虚拟环境**:UOS 默认不带 `python3-venv`,靠它反而会把「装不上」变成 「跑不起来」。`watchdog` 降为 `Recommends`,缺了只是「自动同步」不可用, 程序照常启动(联网后 `sudo apt install python3-watchdog` 补上)。 - `info` 的 version 是四段纯数字(`2.1.6` → `2.1.6.0`),与 deb 的 Version 是两回事; desktop 的 `Icon` 必须等于 appid,不允许绝对路径。 - 与旧的 `gitgo` 包(装到 `/opt/GitGo`)互斥:control 里声明了 `Conflicts/Replaces/Breaks/Provides: gitgo`,安装新包会自动卸掉旧包。 - 架构不写死(`dpkg --print-architecture`),UOS 的 arm64 / loongarch64 只差这一行。 - 纯函数式的规范判定(版本号规范化、info/desktop/control 的生成、md5sums) 在 `src/utils/packaging.py`,由打包脚本调用,并有 `tests/test_packaging.py` 兜底。 ### 直接运行(不打包) ```bash pip install -r requirements.txt bash run.sh # 自动查找可用的 Python 解释器后启动 ``` ### PyInstaller(可选,非推荐路径) ```bash pip install pyinstaller pyinstaller --onefile --windowed --add-data "src/resources:resources" run.py -n GitGo ``` ### 为什么不能 `pip install .` GitGo **不作为 Python 库分发**,`setup.py` 只保留元数据(版本、描述、作者), 并在安装命令上显式拒绝。原因是源码里的导入写的是 `from core...` / `from gui...` / `from utils...` 这种**顶层**形式(`run.py` 把 `src` 加进 `sys.path`),真要按库安装就会把 `core` / `gui` / `utils` / `app` 四个**通用 名字**打进 `site-packages`——`core` 与 `utils` 是别的发行版也在用的名字, 轻则互相覆盖文件,重则把别的工具的导入劫持到 GitGo 的模块上。 请用 deb 包或「直接运行(不打包)」两种方式,它们都只读 `src/`, 不对系统 Python 环境做任何写入。 --- ## 技术架构 ``` GitGo/ ├── run.py # 开发入口(python run.py) ├── run.sh # 启动脚本(开发用;商店包的入口在 files/bin/) ├── gitgo # 源码目录下的启动壳,内容与 run.sh 一致 ├── build_deb.sh # 按 UOS 应用商店规范构建 .deb ├── setup.py # 仅元数据(版本号从 constants 解析);**不支持 pip 安装**,见下 ├── requirements.txt ├── src/ │ ├── app.py # 应用主类,负责装配与线程派发 │ ├── core/ # 核心模块(不依赖 GUI) │ │ ├── git_utils.py # Git 命令封装(subprocess) │ │ ├── config.py # 配置读写(原子写入 + 加锁 + 原子字段更新) │ │ ├── credentials.py # 共享凭证:一次输入、所有仓库共用 + 旧凭证迁移 │ │ ├── gitee.py # Gitee API:令牌校验、仓库列表、新建仓库、地址拼装 │ │ ├── remote_setup.py # 远程对接:配 origin + 首次同步规划/执行 │ │ ├── repo_create.py # 新建仓库:路径判定 + 本地初始化 + 接远程 │ │ ├── auto_sync.py # 自动同步(watchdog + 定时拉取 + 同步方式门控) │ │ ├── repo_manager.py # 仓库状态查询(带缓存) │ │ ├── stats.py # 贡献统计(shortlog / numstat 的解析与聚合) │ │ └── release.py # Gitee 发行版发布 │ ├── gui/ # GUI 组件 │ │ ├── main_window.py │ │ ├── repo_selector.py │ │ ├── sync_controls.py │ │ ├── commit_panel.py │ │ ├── log_viewer.py │ │ ├── status_bar.py │ │ └── dialogs/ # 10 个对话框│ │ ├── branch_dialog.py │ │ ├── tag_dialog.py │ │ ├── stash_dialog.py │ │ ├── history_dialog.py │ │ ├── stats_dialog.py │ │ ├── remote_picker_dialog.py # 本地手选 + 远程从 Gitee 列表选 │ │ ├── create_repo_dialog.py # 新建仓库(本地 + 远程 + 首次同步) │ │ ├── credential_dialog.py # 凭证只输一次 │ │ ├── release_dialog.py │ │ └── repo_manager_dialog.py │ └── utils/ # 工具 │ ├── constants.py # 常量 + 版本号/appid 唯一来源 │ ├── helpers.py # 纯函数(时间格式化、名称规整、字体解析等) │ ├── theme.py # 主题层:跟随系统的深浅色/字体/缩放(DTK 配色) │ ├── packaging.py # UOS 商店打包规范的纯函数(info/desktop/control) │ └── threading_utils.py # 把工作线程调用派发回 Tk 主线程 └── tests/ # 单元测试(pytest / unittest 均可运行) ``` ### 界面适配统信原生应用 `utils/theme.py` 在启动时读系统外观并统一应用,让 GitGo 与 UOS / deepin 原生应用观感一致: - **字体**:候选以「思源黑体」开头(= 系统的 Noto Sans CJK)。Tk **不做字体回退**, 候选一旦缺字就是方框,所以含 CJK 的字体必须排在 `Noto Sans` 这类纯西文字体前面; 等宽同理(日志里有中文)。 - **缩放**:按 `Xft.dpi` 算 `tk scaling`(dpi / 72)。缺了这一步,在设置了 125% 缩放的机器上界面会比原生应用小一圈。 - **深浅色**:读 `com.deepin.dde.appearance gtk-theme`(`deepin-dark` 即深色), 两套调色板都取自 DTK6;经典 Tk 控件的默认外观靠 `option_add` 生效,因此 主题必须在构建界面**之前**应用。 - **强调色**:DTK 的 `#0081FF`。工具栏按钮一律中性色(原生应用的工具按钮本来 就不着色),强调色只给真正的「主操作」:提交并推送、对话框里的创建/应用/验证保存。 - **任务栏集成**:`Tk(className=APP_ID)` 决定 WM_CLASS,与桌面项的 `StartupWMClass` 对齐(都取 `constants.APP_ID`);少了这对齐,任务栏会把窗口 当成另一个程序、图标显示成默认齿轮。 ### 线程模型 Tkinter 只能在主线程操作。文件监控(watchdog 线程)、定时拉取(`threading.Timer`) 和后台状态查询都在工作线程里跑 git 命令,结果一律经 `utils.threading_utils. MainThreadDispatcher`(队列 + 主线程轮询)派发回主线程,避免跨线程操作控件导致的 偶发崩溃。 --- ## 常见问题 ### Q: 自动同步时提示“作者身份未知” **A**: 首次启动时请按要求设置 Git 全局用户名和邮箱。若未弹窗,可手动执行: ```bash git config --global user.name "Your Name" git config --global user.email "you@example.com" ``` ### Q: 自动拉取时发生冲突怎么办? **A**: 程序会立刻 `git rebase --abort`,把仓库还原到冲突之前——**工作区是干净的, 既不在 rebase 中间态也没有待提交内容**,本地提交和未提交改动都还在。 随后暂停对应开关(自动同步撞冲突就暂停自动同步,定时拉取撞冲突就暂停自动拉取), 并同步取消界面上的勾选,避免「看着是开着的其实没在跑」。 恢复办法是在终端手动完成这次变基: ```bash git pull --rebase # 按提示编辑冲突文件后: git add <冲突文件> && git rebase --continue ``` 处理完再重新勾选「启用自动同步」/「启用自动拉取」。 > 注意:由于 rebase 已被中止,此时执行 `git add . && git commit` 不会有任何效果—— > 那两步只在「变基进行中」才有意义。 ### Q: 日志说「工作区有未提交的改动,本轮自动拉取已跳过」? **A**: 这不是错误,也不是冲突。`git rebase` 要求已跟踪文件是干净的,而自动同步的 防抖窗口内本来就常常处于「刚改完还没提交」的状态。此时跳过本轮即可,提交完成后 下一轮会正常拉取:程序**不会**因此误报冲突,也**不会**把自动拉取关掉。 未跟踪的新文件不影响变基,不会导致跳过。 ### Q: 如何切换到其他仓库? **A**: 在主窗口左上角的“当前仓库”下拉框中选择即可,程序会自动加载该仓库的配置和同步状态。 ### Q: 状态栏的「领先 / 落后」数字准吗? **A**: 数字来自真实的 `git fetch`。若网络不通、认证失败或超过 20 秒,会在状态栏与日志中 明确提示「无法连接远程,数字可能滞后」——此时显示的仍是在现有远程引用上算出的值,仅供参考。 ### Q: 私人令牌存在哪里? **A**: 只有一份,在 `~/.config/gitgo/credentials`(权限 `600`,明文),所有仓库共用; 每个仓库只保留一条指向它的 `credential.helper` 配置。不会写入 `~/.git-credentials`, 也不会改写全局 `~/.gitconfig`。 旧版本把令牌存在各仓库的 `.git/gitgo-credentials` 里(于是每加一个仓库都要重输一次)。 首次接管这些仓库时,旧文件会被合并进共享文件并删除;共享文件里已有的主机不会被旧文件 覆盖,避免过期令牌顶掉刚输入的那份。 注意 `store` 后端是**明文存储**:文件不会被提交进版本库,但任何能读到你主目录的进程 都能看到它。对安全要求高的场景请改用 SSH 或系统的 keyring 凭证助手。 ### Q: 添加仓库时为什么先选本地目录、再选远程? **A**: 本地路径只有你知道,远程地址却可以问出来。点「➕添加」后用文件对话框选目录 (不是 Git 仓库时会问你要不要 `git init`),接着弹出的窗口会用已保存的令牌调用 `/user/repos` 拉回账号可见的仓库列表(含协作与组织仓库,按最近更新排序), 支持关键字筛选、双击选中。拿不到网络或要连别的平台时,切到「手动输入地址」即可, HTTPS / SSH 都收。 远程选定后会抓一次远程,并按规则决定要不要顺手完成第一次同步: | 本地 | 远程 | 动作 | | --- | --- | --- | | 空 | 空 | 什么都不做 | | 有提交 | 空 | 把本地分支推上去(自动建立 upstream) | | 空 | 有分支 | 拉取远程默认分支到本地(**分支名与远程一致**) | | 有提交 | 有分支 | 只提示,不自动合并——两边都有提交时「自动」等于替用户决定代码走向 | 拉取那一行有个安全前提:本地目录里若已经躺着**与远程同名的未跟踪文件**, 程序会**中止**并如实报错,而不是把那些文件覆盖掉。详见下一条。 ### Q: 首次同步提示「切换到分支 X 失败(本地若存在与远程同名的未跟踪文件,请先移走或改名)」? **A**: 这是**保护**,不是故障。你给的那个目录里已经有和远程同名的文件(例如 远程有 `README.md`,本地那份你还没 `git add` 过),直接拉下来会把本地那份 原样覆盖、且事后无法恢复。GitGo 把这一步交给 `git checkout -b` 而不是 `git reset --hard`,于是 git 自带的保护会在覆盖前中止,失败原因如实告诉你。 处理办法二选一: - 把本地那份先备份/改名(例如 `mv README.md README.md.local`)后重试; - 或者确认远程那份才是你要的,删掉本地同名文件后重试。 本地独有的、路径不与远程冲突的未跟踪文件不受影响,会原样保留。 ### Q: 「新建仓库」和「添加仓库」有什么不同? **A**: 分界在起点。**添加仓库**要求本地目录已经存在(甚至已经是 Git 仓库), 再给它接一个远程;**新建仓库**则相反——本地目录可以还不存在(会自动创建并 `git init`),远程也可以还不存在(可直接在 Gitee 上新建),创建完顺手完成 首次同步并登记进 GitGo。目标目录如果已经是 Git 仓库,新建流程会拦住你并 引导改用「添加仓库」:重新 `git init` 不会丢东西,但会让人误以为自己新建了一个仓库。 ### Q: 三种同步方式有什么区别? **A**: 同步方式决定本地与远程之间**允许发生哪个方向**的动作,仓库之间互相独立: | 同步方式 | 文件变更自动提交推送 | 定时拉取 | | --- | --- | --- | | 双向同步(默认) | 按开关,且推送前先 `pull --rebase` | 按开关 | | 仅本地 → 远程 | 按开关,**提交后不再拉取** | 禁用 | | 仅远程 → 本地 | 禁用 | 按开关 | 两点需要注意: - 被模式压制的那一项会在界面上**禁用并取消勾选**,不会留下「勾着却不会执行」的复选框 (切回双向同步后需要重新勾选); - 「仅本地 → 远程」下若远程有新提交,推送会因非快进(non-fast-forward)被拒绝—— 这正是该模式的定义(不把远程改动合到本地),日志会如实说明,不会悄悄改成拉取。 需要在一条线上的话,请改用「双向同步」。 同步方式只约束**自动**行为;手动点「提交并推送」始终按你的意图执行。 ### Q: 首次推送为什么以前会失败? **A**: 旧版本用的是裸 `git push`,新建仓库没有 upstream 关联时必然报错。现在会在没有 upstream 时自动改用 `git push --set-upstream origin <当前分支>`。 --- ## 开发与测试 ```bash pip install -r requirements.txt pip install pytest pyflakes # pyflakes 可选,装了会多跑一道静态检查 python -m pytest tests -q ``` 测试不需要图形环境(Tk 只被 import,不会被实例化),覆盖:配置读写(含并发 「原子字段更新」不丢更新)、Git 命令封装、推送 upstream 行为、首次同步的规划真值表 与**同名未跟踪文件的保护**、凭证作用范围与共享凭证(含用真实 `git credential fill` 验证落盘格式、旧凭证迁移、权限收紧)、Gitee 仓库列表的分页与错误分支、新建仓库的 请求形状、贡献统计的解析与作者名匹配口径、Tk 线程派发器与对话框任务、仓库状态与缓存、 自动同步的开关语义与拉取健壮性(`auto_sync` 门控、脏工作区跳过、重命名事件、 冲突后的暂停与提示文案)、同步方式的合成规则与真实仓库上的方向差异、新建仓库的 路径判定与初始提交边界、别名/分支名等外部文本的界面渲染口径, 以及「模块导入 + 无未定义名字 + 界面文案无非 BMP 字符」的整体冒烟检查。 其中只有真正需要 `Observer` 的监控生命周期用例会因缺 watchdog 而跳过; 开关、事件处理与冲突处理等最容易出错的部分都不依赖 watchdog,照常执行。 --- ## 贡献指南 欢迎提交 Issue 和 Pull Request。请确保代码风格符合 PEP 8,并添加必要的单元测试。 --- ## 许可证 MIT License Copyright (c) 2026 温暖断章 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.