LnxGit f0fb316bd6 修正上传说明:git.xnhw.cc 已不再是 Cloudflare 反代
现在 DNS 解析到 64.49.47.22、响应头是 `via: 1.1 Caddy`,前置代理已经不是
Cloudflare,之前写的「免费版请求体上限 100 MB → 413」已经过时。

重新实测了两条路径:

* 公网(经 Caddy):小文件上传 201 正常;150 MB 的 APK 会在约 305 秒后
  返回 **502** —— 瓶颈在 Caddy 背后的 Gitea 处理大附件那一层,不是请求体限制。
* 内网直连 192.168.100.103:8080:151 MB 约 15 秒,稳定。

结论不变(APK 走内网直传),但原因和文案改了;README 与
scripts/wsl_upload_release.sh 的注释都同步更新。

另外记录一个踩坑:之前用 `curl --resolve` 把域名钉在旧的 Cloudflare 边缘 IP
上,在 DNS 已经切换之后就会表现为「接口返回 000 / 413」,让人误判成还在 CF。
现在直连即可,不需要 --resolve。
2026-09-23 07:55:35 +08:00

LnxGit · Android Git 客户端

PySide6 (Qt for Python) + QML 编写的移动端 Git 客户端,支持连接任意远程仓库 GitHub / Gitee / GitLab / 自建 Gitea 等)并完成完整的 Git 操作。 Git 能力由纯 Python 实现 dulwich 提供, 不依赖设备上的 git 可执行文件,因此在 Android 上开箱即用。

界面预览

安装包APK

Release 页:https://git.xnhw.cc/l/lnxgit/releasestag v1.0.0

文件 大小 适用
LnxGit-0.1-arm64-v8a-debug.apk 150.9 MB arm64 真机(绝大多数手机)
LnxGit-0.1-x86_64-debug.apk 153.4 MB x86_64 模拟器
adb install -r LnxGit-0.1-arm64-v8a-debug.apk    # 真机
adb install -r LnxGit-0.1-x86_64-debug.apk       # 模拟器

SHA256

07b2e7352a865553d9347eda463dbecca83b4cbbf81f075a24c9f518fec9d0fa  LnxGit-0.1-arm64-v8a-debug.apk
e75ed77159df52c46fbbb8267607d1c5649f4157e8e8810b908149c65d058415  LnxGit-0.1-x86_64-debug.apk

包名 org.lnxgit.lnxgit,最低 Android 7.0API 24两个版本不能混装x86_64 模拟器上装 arm64 版会走 ARM 转译层 libhoudiniQt 的 JNI_OnLoad 会直接段错误 —— 见 android/BUILD.md 4.1。

上传附件时的注意事项

公网入口传不了大文件。 git.xnhw.cc 目前是 Caddy 反代2026-09 起已不再走 Cloudflare小文件上传正常但 150 MB 的 APK 走公网会在约 5 分钟后返回 502 —— 瓶颈在 Caddy 背后的 Gitea 处理大附件那一层,不是前置代理的请求体限制。

所以 APK 一律走内网直连(同一个 Gitea 实例,没有这层问题):

bash scripts/wsl_upload_release.sh dist/LnxGit-0.1-arm64-v8a-debug.apk

实测 151 MB 约 15 秒。清理旧附件用 tools/clean_release.py

两个注意点:

  • 令牌要有管理员权限。仓库所有者 l 的令牌可以;kimigit 那个只有 pull 权限,上传或删除附件都会 403。
  • 不要用切片绕过。曾用过一次,结果 Release 里堆了几十个 xxx.apk.part001 同名附件,页面很乱、服务端也白扛一遍合并。

功能一览

仓库连接

能力 说明
克隆远程仓库 HTTPS / SSH 地址,支持浅克隆(指定深度)与指定分支
新建空仓库 直接在应用私有目录中初始化
多仓库管理 统一列表、搜索、概览、按需删除、重命名目录
连接测试 克隆前可先 ls-remote 验证地址与凭据

完整的 Git 操作

  • 本地:暂存 / 取消暂存(单文件或全部)、提交(含作者信息与修正提交)、工作区差异、暂存区差异、提交间差异
  • 分支:创建、切换、重命名、删除(含删除当前分支自动回退)、合并、从远端分支建立跟踪分支
  • 标签:创建(轻量 / 附注)、列示、删除、推送到远端
  • 远端:添加 / 删除 / 修改地址、fetch含 prune、pull快进 / 仅快进、push含强推与设置上游、删除远端分支
  • 历史:提交日志分页加载、搜索(说明 / 作者 / SHA、提交详情、文件级变更、逐行追溯 blame
  • 分支地图:独立页面是二维流程图(节点带标题、时间自下而上、分支横向铺开、 可拖动缩放),历史页则在每行左侧附带紧凑泳道图(详见下文)
  • 文件:任意版本的文件树浏览与内容查看(含二进制识别与大文件截断)、工作区文件查看
  • 其他储藏stash push / pop / drop / 列表、resetsoft / mixed / hard、丢弃改动、清理未跟踪文件、拣选cherry-pick、仓库配置读写、对象统计

分支地图

两个入口,共用同一套提交与引用数据,但呈现形态不同:

位置 形式 特点
「图谱」页 二维流程图 每个提交一个带标题的胶囊节点,时间自下而上、分支横向铺开,可拖动与缩放
「历史」页 列表左侧的紧凑泳道 一行一个提交图谱旁边保留作者、时间、SHA 与引用标签

流程图形态(独立页面):

  • 顶部有分支选择框:「全部引用」+ 各本地分支(当前分支标注「(当前)」), 末项是「+ 新建分支…」;选中某个分支就只看它的历史
  • 纵向按拓扑深度(到根的最长路径)分层 —— 从同一点分叉出的兄弟分支落在 同一水平线上并排,而不是斜着排成一条对角线
  • 横向按轨道铺开,每条分支一条轨道;轨道数决定图的宽度
  • 颜色按分支分配(不是按轨道):同一分支的提交与连线同色;顶部有一行 颜色标识列出「分支名 → 颜色」,可横向滚动
  • 父 → 子用带箭头的曲线连接,箭头指向时间更近的一侧
  • 整张图可以双向拖动,右下角是 / / ↔(适应宽度)
  • 图比屏幕宽时首次进入会自动缩放,但只缩到 60% —— 21 个分支铺开有 4000 多 像素,按比例缩会到 30%,标题糊成一片什么也读不出来。地图本来就是拿来拖动 浏览的,宁可让用户横向滚动
  • 节点只渲染视口内的那些:一次建两百个节点会明显卡顿,而屏幕上同时能 看到的也就十几个

紧凑形态(历史页):

  • 同父的兄弟分支挤在同一列以省空间(与 git log --graph 一致)
  • 最多 6 列,超出按可用宽度压缩列宽(下限 11px仍超才裁剪

范围都是 git log --all:当前分支 + 全部本地分支 + 远端引用 + 标签; 注解标签会解引用到对应提交,未合并的分支同样会出现在图上。

分支地图(流程图) 历史页(含图谱) 多分支21 个分支)
分支地图 历史 多分支

移动端体验

  • 深色 / 浅色主题Material 风格,触控友好的 44dp 点击区域
  • 全部耗时操作在后台线程执行,界面实时显示进度与日志
  • 认证失败自动弹出登录框(支持个人访问令牌),可记住凭据
  • 内置运行日志页,便于排查网络与认证问题

界面

仓库 更改 历史 分支 设置
仓库 更改 历史 分支 设置

架构

main.py                    # 入口:注册 QML 单例、加载界面
lnxgit/
├── apppaths.py            # 应用数据目录(桌面 / Android 自适应)
├── settings.py            # 设置与凭据持久化QML 单例 Settings
├── gitcore.py             # ★ 核心:基于 dulwich 的全部 Git 操作 + 图谱泳道布局
├── worker.py              # 后台工作线程(串行任务队列 + 凭据回调)
├── service.py             # 异步服务门面QML 单例 GitService
└── gitea.py               # Gitea REST API 客户端(建仓 / Release
qml/
├── Main.qml               # 主界面StackView + 底部导航
└── LnxGit/                # QML 模块qmldir 声明 Theme / Git 单例与各页面)
    ├── Theme.qml          # 主题、尺寸与图谱配色
    ├── Git.qml            # 全局状态与操作桥接
    ├── components/        # 通用组件(卡片、按钮、差异视图、图谱绘制…)
    └── pages/             # 12 个页面
tools/                     # 测试与辅助脚本
scripts/                   # Android 构建脚本

新增 QML 文件后记得在 qml/LnxGit/qmldir 里登记,否则 import LnxGit 会找不到类型 —— 这个疏漏会导致应用启动即崩(QThread: Destroyed while thread ... is still running)。

线程模型

QML 只与 GitService 交互,后者把任务投递到独立 QThread 中的 Worker Worker 串行执行 gitcore 的操作,并通过信号把结果、进度、日志回传主线程。 网络操作需要凭据时,工作线程阻塞等待主线程弹窗结果(authNeededprovideCredentials 因此认证流程不会破坏「界面永不阻塞」的约束。

在桌面运行(开发调试)

python -m pip install PySide6 dulwich certifi
python main.py

可选:生成演示数据与截图

python tools/seed_demo.py     # 在应用数据目录创建带真实改动的示例仓库
python tools/screenshot.py    # 输出界面截图到 docs/screenshots/

测试

# 1) 纯核心逻辑(无需 Qt完整 Git 流程
python tools/smoke_test.py

# 2) 端到端:真实 GitService + 后台线程 + 凭据回调
python tools/e2e_test.py

# 3) QML 加载检查(离屏,逐文件定位报错)
python tools/qml_check.py

# 4) 图谱泳道布局:拓扑序、连线连续性、注解标签解引用(需系统有 git
python tools/graph_test.py

# 5) QML 布局陷阱Column/Row 子项误用锚点会让整列布局失效
python tools/check_qml_layout.py qml

# 6) 图标字形Android 上会渲染成豆腐块的字符(需先拉取设备字体)
bash scripts/wsl_pull_fonts.sh emulator-5554 /tmp/lnxgit-fonts
python tools/check_glyphs.py qml /tmp/lnxgit-fonts

当前状态:smoke_teste2e_test 全部通过(共 70+ 断言), graph_test 覆盖分叉 / 合并 / 三父提交 / 未合并分支 / 空仓库等场景。

构建 Android APK

官方 pyside6-android-deploy 只支持 Linux / macOS 主机。 在 Windows 上推荐使用同一台机器的 WSL2 (Ubuntu):源码位于 /mnt/c/... 构建在 Linux 侧完成,产物 APK 直接写回项目的 dist/ 目录。

完整的踩坑记录buildozer 拒绝 root、SDK 路径假设、GitHub/Gradle 下载受限、 源文件 BOM 导致打包工具解析失败等)见 android/BUILD.md

1) 准备构建环境WSL 内,一次性)

sudo bash scripts/wsl_bootstrap.sh          # Python 3.11 / JDK 17 / 工具链
bash scripts/wsl_setup_android.sh           # Android SDK + NDK + Android wheel约 2GB
bash scripts/wsl_install_host_pyside.sh     # 宿主 PySide6提供打包命令
sudo bash scripts/wsl_create_builder_user.sh
sudo bash scripts/wsl_move_workdir.sh       # 构建目录移到 /opt普通用户可写

# 网络受限时的预热GitHub / Gradle 走镜像)
sudo -u builder bash scripts/wsl_prefetch_p4a.sh
bash scripts/wsl_place_p4a.sh
bash scripts/wsl_prefetch_gradle.sh
bash scripts/wsl_fix_gradle_cache.sh
bash scripts/wsl_fix_sdk_layout.sh

环境与版本对应关系Qt 6.11.2

项目 版本
Qt for Python 6.11.2Android wheelcp311 / abi3aarch64x86_64
打包用 CPython 3.11.15 —— 必须与 wheel 的 cp311 一致,构建脚本会自动固定
Android NDK 27.2.12479018
Android SDK Platform android-35
Build Tools 35.0.0
Gradle 8.14.3
JDK 17

2) 打包

bash scripts/wsl_build.sh debug     # debug APK可直接安装
bash scripts/wsl_build.sh release   # release体积更小

# 可选:裁剪未被使用的 Qt 模块WebEngine / Designer / 3D / Charts 等)并重新签名
bash scripts/wsl_trim_apk.sh 输入.apk 输出.apk

产物自动回写到 dist/

3) 安装到手机

adb install -r dist/LnxGit-*-debug.apk

凭据与安全说明

  • HTTPS 认证使用 用户名 + 个人访问令牌PAT,令牌即密码; GitHub / Gitea / GitLab 均支持,避免直接使用账号密码。
  • 勾选「记住凭据」后,凭据以明文 JSON 保存在应用私有目录 Android 上为应用沙箱内路径,其他应用无法读取)。 如需更严格的保护,可在「设置」中关闭保存,或随时删除已保存的条目。
  • 默认校验 HTTPS 证书(内置 certifi 根证书); 仅在自签名证书的内网环境才建议关闭校验。
  • 本应用不会向任何第三方上报数据,所有网络请求只发往你填写的仓库地址。

已知限制

  • dulwich 不支持三方合并的冲突编辑流程:合并出现冲突时需手动处理后提交。
  • blame 对超大文件较慢;差异与文件预览均限制在 400 KB / 1 MB 以内以避免卡顿。
  • 应用内暂未实现交互式 rebase 与 submodule 操作。
  • 图谱最多画 12 条泳道(超出部分裁掉),单次加载默认 200 条提交; 两者都会随窗口宽度与 limit 调整。
  • 仓库同时提供 arm64-v8a真机与 x86_64模拟器两种产物构建时用 ARCH_ABI / ARCH_P4A 选择;注意 x86_64 模拟器必须装 x86_64 版 装 arm64 版会走 ARM 转译层并在启动时段错误。

许可证

MIT

Description
LnxGit —— 用 PySide6 + QML 编写的 Android Git 客户端,支持连接远程仓库与完整 Git 操作
Readme MIT 2 MiB
2026-09-22 13:35:20 +08:00
Languages
Python 43.7%
QML 37.2%
Shell 19%