鸿蒙PC_Git-Bash-ohos适配全记录
鸿蒙PC开源移植:Git Bash原生适配全记录
欢迎加入开源鸿蒙PC社区: https://harmonypc.csdn.net/
欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper
摘要: 把 Git Bash 的操作习惯带到鸿蒙 PC,需要同时处理终端输入、Shell 解析和真实 Git 仓库读写。本文从一次 add → commit → log 操作出发,拆解 ArkTS、N-API、C++17 和 NetworkKit 的分工,说明索引与对象兼容、HTTPS 传输、桌面交互以及构建验证的实现过程,并结合已有模拟器运行记录说明当前成果与后续工作。
关键词: 鸿蒙PC、开源软件移植、Git Bash、ArkTS、Harmony NDK、原生Git
一、先确定要带到鸿蒙 PC 的是什么
使用 Git Bash 时,开发者通常不会刻意区分终端窗口、Shell 和 Git:打开窗口,进入项目目录,查看修改,提交代码,这些动作连在一起就构成了日常工作。但开始适配时,这三个部分必须拆开。窗口能显示字符,并不意味着命令已经被正确解释;命令能返回一段结果,也不意味着磁盘上的 Git 仓库发生了正确变化。
我希望保留的是这种连续的命令行工作方式:查看状态后只暂存需要的文件,提交之后立刻查询历史,必要时继续管理分支。应用因此采用 Git Bash 风格的深色终端和 MINGW64 提示符,仓库操作则接入鸿蒙侧的原生服务。MINGW64 在这里是界面风格的一部分,不能据此判断应用正在运行 Windows 的 MinGW 环境。
上游参考项目 Git for Windows 涉及 Git、MSYS2 和 mintty。当前工程的技术路线是原生适配终端交互和受支持的 Git 行为,没有直接搬运 Windows 可执行文件,也没有把完整 Bash、MSYS2 或 mintty 编译进 HAP。因此,准确的项目定位是“面向鸿蒙 PC 的 Git Bash 兼容终端与原生 Git 实现”。这个边界决定了后面的代码组织和验证方法。
工程托管在 AtomGit PC 社区:ohos_harmony-git-bash。本文代码分析对应社区仓库的现有实现;设备画面和实际运行结论来自仓库保存的 2026 年 9 月 5 日记录,文章修订日期不代表重新完成了一轮设备测试。
二、把终端输入接到真实仓库
2.1 为什么要把页面、Shell 和仓库服务分开
假设用户输入 git commit -m "first native commit"。页面需要接收键盘事件并保留回显,Shell 需要识别引号中的空格,Git 服务需要读取索引、生成提交并更新引用。如果把这些逻辑都塞进页面,终端样式改动就容易影响命令行为,测试一个提交还必须先启动整个应用。
项目把调用过程组织为:
Index.ets:输入、回显、键盘、滚动
↓
GitShell.ets:命令解析、参数展开、内置命令、Git 分发
↓
GitRepositoryService:仓库操作接口
↓
NativeGitRepositoryService.ets:鸿蒙原生服务适配
↓ N-API
libgit_native.so:对象、索引、引用、工作区、传输协议
↓
设备上的真实仓库文件
这份结构可以直接映射到源码。阅读工程时,先看 Index.ets 的 executeCommand(),再进入 GitShell.ets,最后跟到原生服务,就能知道一条输入在哪一层变成仓库操作。页面源码
2.2 在页面生命周期中接入原生后端
页面进入时会获取应用上下文,把原生仓库服务与应用文件目录交给 Shell。下面是 aboutToAppear() 中的实际接入片段,省略了后续欢迎信息和终端行初始化:
const context = this.getUIContext().getHostContext();
let nativeMessage: string = '';
if (context !== undefined) {
nativeMessage = this.shell.attachRepositoryService(
new NativeGitRepositoryService(),
context.filesDir
);
}
传入 context.filesDir,使应用可以先在自己的文件空间里建立本地工作流。用户打开其他仓库时,还需要处理文件选择器返回的 URI 和实际可访问范围。先让应用内仓库走通,再扩展授权目录,能够分别定位 Git 数据处理与系统文件访问的问题。
服务接口也为测试保留了入口:Shell 的参数解析可以使用可控的测试后端;真实索引和对象操作则在原生 fixture 中验证。项目保留的内存兼容行为用于 Shell 测试,不能拿它代替设备上的原生后端。
2.3 原生库具体包含什么
NativeGitRepositoryService.ets 从 libgit_native.so 导入 inspectRepository、stageRepository、commitRepository、readLog 等函数。N-API 层把 ArkTS 参数交给 C++,再把操作结果和仓库快照返回到应用。页面不需要知道二进制 index 的字段布局,C++ 也不负责终端行的颜色。
原生库的 CMake 配置明确列出了实现文件,以下为其中两个完整配置项:
add_library(
git_native
SHARED
git_repository.cpp
git_transport.cpp
napi_init.cpp
)
target_link_libraries(
git_native
PUBLIC
libace_napi.z.so
libnet_http.so
z
)
工程另外启用了 C++17。三个源文件分别承接仓库处理、传输协议和 N-API 导出,zlib 用于对象压缩等操作。这条链路没有通过启动系统 git 子进程完成本地命令;主机测试使用系统 Git,是为了建立和核对测试仓库。CMake 配置 · 服务实现
三、一次 commit 背后,究竟写了哪些内容
3.1 首先区分工作区、索引和 HEAD
实现 Git 时,一个容易低估的问题是:文件“现在的内容”和“准备提交的内容”可能不同。用户暂存文件后还可以继续修改,提交应该使用索引中的版本。若直接扫描工作区生成提交,就会把暂存后的修改一起提交,违背开发者对 git add 的预期。
因此,状态查询需要比较三份数据:工作区文件、索引记录和 HEAD 指向提交中的树。工作区与索引之间的差异对应尚未暂存的改动;索引与 HEAD 树之间的差异对应待提交的改动。未跟踪文件和忽略规则还需要单独判断,status 不能只靠文件是否存在来实现。
项目通过 ReadIndex() 读取 index v2/v3/v4,并在写入时规范化为 v2。这个选择让写入路径相对集中,但也意味着不能声称保留所有可选扩展:split-index、untracked-cache 等扩展数据目前不会完整保留。适配已有仓库时,读兼容范围和写回行为必须分别说明。索引与仓库实现
3.2 add 的结果要进入 Git 对象库
对于文件内容,Git 使用 blob 对象保存数据。对象标识并非直接对文件原始内容计算哈希,还包含对象类型、长度和 NUL 分隔符。项目 WriteLooseObject() 中实际构造待压缩数据的表达式是:
const std::string raw =
type + " " + std::to_string(payload.size()) + '\0' + payload;
该函数先通过 HashObjectId() 得到对象 ID,然后把压缩后的内容放进 objects/<前两位>/<剩余部分>。暂存操作还要把路径、对象 ID、文件模式等信息写入索引。终端上的 git add 通常没有大段成功提示,真正有意义的结果保存在对象和索引中。
这也解释了为什么“命令没有报错”还不足以完成测试:如果对象头、长度或者索引写入有误,后面的 commit 可能读到错误内容,其他 Git 工具也可能无法识别生成的数据。
3.3 commit 从索引生成树,再更新引用
CommitRepository() 先检查提交消息,加载仓库上下文,读取索引,计算暂存差异,然后调用 BuildTreeFromIndex() 创建 tree 对象。如果当前没有待提交变化,就返回对应错误,避免生成一次无意的空提交。
有了 tree 后,函数组织 commit 对象的文本。下面是源码中的连续片段:
const std::string timestamp = CurrentGitTimestamp();
std::string payload = "tree " + treeObjectId + "\n";
if (!context.headObjectId.empty()) {
payload += "parent " + context.headObjectId + "\n";
}
payload += "author " + author + " " + timestamp + "\n";
payload += "committer " + author + " " + timestamp + "\n\n";
payload += message;
if (payload.back() != '\n') {
payload.push_back('\n');
}
首次提交没有父提交;后续提交需要记录原来的 HEAD。生成 commit 对象之后,还要判断 HEAD 是指向本地分支的符号引用,还是 detached HEAD,再写入相应引用及 reflog。最后重新检查仓库,返回新的快照,让终端输出反映操作后的状态。
支持一个 commit 命令,实际依赖树构建、身份配置、时间格式、对象压缩、引用和日志等多个模块。项目中的专用 update-ref 事务路径另有预检与回滚处理;不能把那部分保证笼统套用到所有仓库写操作上。
3.4 为什么还要读取 packed 对象和 worktree
只在新建小仓库中测试,会遗漏真实使用场景。已有仓库可能经过对象打包,分支引用可能放在 packed-refs,关联 worktree 的 .git 可能是文件而非目录。若假定所有对象都以 loose 形式存在、所有 .git 都是目录,打开开发者原来的项目时就容易失败。
当前实现覆盖 loose/packed 对象、OFS_DELTA 和 REF_DELTA 解析,也处理 linked worktree 的 commondir。这些能力使“打开仓库”从演示目录扩展到更接近日常使用的仓库结构。大型 pack 目前仍会读入内存,流式读取和缓存属于后续优化方向,不能用小仓库测试推断大仓库性能。
四、终端兼容中,最容易被外观掩盖的细节
4.1 引号、展开和重定向都有先后关系
命令解析不能简单使用空格切分。git commit -m "first native commit" 中的消息是一个参数;echo '$HOME' 与 echo "$HOME" 对变量展开的要求不同;echo HarmonyOS-PC > pgc.txt 还包含输出重定向。如果先丢掉引号信息,再处理变量和路径,就很难恢复用户原本的输入含义。
GitShell.ets 承担引号感知的解析、参数展开、命令替换、通配符和重定向处理。当前管道是受支持内置命令之间的内存数据传递,文件描述符重定向限制在 0、1、2。它可以把 printf 输出传给已实现的 Git stdin 命令,但没有因此获得任意外部程序执行能力。Shell 实现
heredoc 又增加了一层交互状态:输入 << 后,后续行应进入正文收集,直到遇到结束标记,而不是每行都当作新命令执行。页面必须与 Shell 的输入状态配合,才能继续使用同一个终端输入区。
4.2 同一个输入框承担多种会话
普通命令、标签消息编辑、HTTPS 凭据和 heredoc 都可能经过同一个输入框,但它们对历史记录、回显和空白字符的处理不同。例如凭据输入不应进入普通命令历史;消息编辑中的空白也不应被普通命令的 trim() 逻辑抹掉。
页面 executeCommand() 先检查 editorActive() 和 credentialPromptActive(),再决定如何读取输入、记录历史和生成可复制的终端行。TerminalInputSession.ets 管理历史与补全,TerminalAnsi.ets 处理颜色片段和显示尺寸。这样的拆分让键盘行为可以单独测试,也便于排查“输入对了但显示不对”的问题。
Ctrl+C 同样结合上下文处理:网络操作进行时用于取消请求,heredoc 收集时用于取消当前输入。在没有 PTY 进程会话的前提下,不能把它描述为已经具备完整 Unix 进程信号行为。
4.3 一次实际发现的路径显示问题
9 月 5 日的运行记录中有一个具体问题:切换目录后,命令回显和 pwd 反映实际目录,但顶部路径和底部提示符可能仍停留在启动目录。记录提出,后续应把这些派生字段接入 ArkUI 状态更新。
这说明业务对象内部状态与页面响应式状态需要分别检查。定位时先用 pwd 确认工作目录,再观察提示符刷新,能够判断问题处于命令执行还是界面呈现。本文截图在启动目录执行,画面路径与操作一致;文章修订没有把这个已知问题描述成已经修复。原始运行记录
五、HTTPS 远程操作:网络请求只是其中一部分
本地工作流完成后,远程协作还需要处理 Git 协议。把仓库 URL 交给 HTTP 客户端,并不会自动得到一个可用的 Git 仓库。原生协议层要理解远程引用、能力协商和 pack 数据,系统网络层则负责实际请求、TLS 与连接控制。
项目在 NativeGitRepositoryService.ets 中使用 NetworkKit,二进制响应采用 ARRAY_BUFFER。git_transport.cpp 负责相关协议数据,仓库层安装 pack/index 并更新引用。以 fetch 为例,可以按以下顺序理解实现:
- 发现远程引用与服务端能力,确定需要获取的对象。
- 构造 upload-pack 请求,经 NetworkKit 收发二进制数据。
- 处理 side-band 进度、错误及 pack 内容,验证校验和并解析对象。
- 安装 pack 与索引,再更新远程跟踪引用及
FETCH_HEAD。
把数据校验放在引用更新之前,是因为引用一旦指向缺失或损坏的对象,仓库就会进入不一致状态。测试需要覆盖协议错误和损坏数据,不能只比较下载字节数。传输协议实现
push 走 receive-pack,并处理 report-status。本地分支检查通过不代表服务器一定接受,服务端报告也参与成功判定。pull 当前只执行 fast-forward 更新,遇到分叉历史会拒绝继续,尚未实现三路合并、rebase 和冲突处理。这个边界会直接影响开发者能否采用某个协作流程,必须在介绍“支持 pull”时一起说明。
HTTPS 请求使用系统 CA,支持 http.sslCAInfo,并拒绝 http.sslVerify=false。凭据通过专门的存储与脱敏模块处理,AssetStoreKit 可用时尝试持久化,否则保留在进程内存。系统证书、代理、凭据重启恢复和真实可写远程仓库仍需要目标设备验证;本文本地提交截图不用于证明这些网络能力已经通过设备验收。
六、从构建成功到行为正确,要验证不同的层
6.1 先准备能复现的工程环境
工程使用 DevEco Studio 与 HarmonyOS 6.1.1(API 24)SDK,目标设备包含 2in1。原生主机测试还需要 Git、C++17 编译器和 zlib 开发环境。下载工程的命令在开发电脑终端执行:
git clone https://atomgit.com/OpenHarmonyPCDeveloper/ohos_harmony-git-bash.git
cd ohos_harmony-git-bash
完整验证脚本以 macOS DevEco 安装路径为默认值。在配置好该环境,或按编译说明设置 DEVECO_SDK_HOME、JAVA_HOME、HVIGOR、OHPM 后执行:
bash ./scripts/verify.sh
Windows 开发者可以在 DevEco Studio 中打开工程、同步依赖,再选择设备运行;不能因为安装了 Git Bash,就认为 macOS 默认路径、rsync 和主机 C++ 依赖已经可用。完整环境配置见 README.OpenHarmony_CN.md。
此前构建曾遇到中文工程路径问题。现在验证脚本会检测路径,并在需要时复制到临时英文目录构建,再把 HAP 复制回来。这只解决脚本所覆盖的构建路径;直接在 IDE 中打开项目时,仍建议采用英文工作目录,把路径问题与业务代码问题分开处理。
6.2 主机 fixture 检查的是 Git 数据互通
scripts/verify.sh 先编译并运行原生 fixture,再执行 ArkTS 测试和 HAP 组装。原生测试中的一个关键做法,是调用系统 Git 创建仓库,再让本项目 C++ 服务读写,最后交回系统 Git 检查。
例如,现有测试调用 CommitRepository() 后,会用系统 Git 的 rev-parse HEAD 对比原生服务返回的提交 ID,并读取提交消息。这样可以发现“应用自己的写入和读取都用了同一种错误格式”而导致的假通过。索引、分支、工作区和打包对象也需要在真实 Git 数据上验证。原生测试源码
ArkTS 测试关注带引号参数、Shell 展开、历史补全、终端解析和凭据脱敏。HAP 组装与双 ABI 配置检查的是库能否进入目标工程。三类验证分别回答数据是否正确、应用逻辑是否正确、目标包能否构建,彼此不能替代。
七、把模拟器中的一次提交完整对应起来
7.1 环境和证据来源
仓库记录的运行环境是 OpenHarmony-6.1.1.125,API 24,设备类型 2in1,型号 emulator。9 月 5 日沿用了 9 月 4 日已通过完整验证的开发 HAP,运行记录保存了代码基线、HAP 哈希和截图信息。这是模拟器中的实际运行,不是实体鸿蒙 PC 的验收记录。
脚本生成的开发包位于:
entry/build/default/outputs/default/entry-default-unsigned.hap
在 DevEco Studio 中选择 entry/default、连接设备并点击 Run;目标设备要求签名时,完成对应调试签名。构建产物存在和设备允许安装是两个步骤,不应把未签名包描述成对所有设备都能直接安装。
7.2 在应用终端逐行执行
本次使用应用自带的 demo-app 仓库,新增一个文件,并且只暂存这个文件。作者配置通过命令级 -c 传入,不修改全局身份:
echo HarmonyOS-PC > pgc.txt
git add pgc.txt
git -c user.name=PGC -c user.email=pgc@example.invalid commit -m Verify-native-Git-on-HarmonyOS
git status --short
git log --oneline -1

图1:2026年9月5日的完整桌面运行截图,保留应用窗口、鸿蒙桌面和任务栏;设备侧 screenCap 采集,文章未改绘运行结果。
画面中的 commit 返回 [main 389cf92],随后的 log --oneline -1 也读到 389cf92。两者一致,说明后一次查询读取到了刚写入的提交引用。复现时提交哈希会受到时间和仓库内容影响,应比较同一次运行中的两处结果,而不是要求得到截图中的固定哈希。
status 仍显示 README.md 和 docs/porting-notes.md 为未跟踪文件,这并不表示刚才的提交失败。这两个文件原本就在演示仓库中,本次只执行了 git add pgc.txt,所以它们没有进入提交。这个结果对应了前文工作区与索引分离的设计。
7.3 进一步检查“暂存后再修改”的语义
下面是供读者在测试仓库中执行的补充实验,不是上述截图已经展示的结果。请选用尚不存在的测试文件名:
echo staged-version > pgc-index-check.txt
git add pgc-index-check.txt
echo working-version > pgc-index-check.txt
git diff --cached
git diff
核对重点是:暂存差异中应出现 staged-version,工作区差异应体现它到 working-version 的变化。这个实验直接检查索引是否保留了暂存时的内容。执行时还应留意测试仓库原有改动,避免把其他文件的差异误认为本次结果。
八、遇到问题时,沿着哪条链路排查
| 现象 | 优先检查的位置 | 判断依据 |
|---|---|---|
| 页面能显示,原生仓库打不开 | 页面服务接入、N-API 库和设备 ABI | 先确认原生服务初始化结果,再看路径;窗口出现不能证明 .so 已正确接入。 |
pwd 正确,顶部路径不更新 | ArkUI 页面状态与派生显示字段 | 这是已有运行记录中的问题,先区分实际目录和显示目录。 |
| 有文件改动却提示无内容可提交 | 工作区与索引 | 核对是否对目标文件执行 add,以及暂存后是否继续修改。 |
| 小仓库正常,已有仓库读失败 | index 版本、packed 对象、worktree 元数据 | 新建演示仓库未必覆盖这些存储形式。 |
| 工程在中文目录构建失败 | 工具链路径和临时目录逻辑 | 先在英文路径复现,避免误改业务代码。 |
| HTTPS 请求失败或 push 被拒绝 | URL、TLS、认证、服务端报告、分支关系 | 区分网络连接失败与 Git 服务端拒绝更新。 |
这些判断点把笼统的“鸿蒙上不能用”拆成可以定位的工程问题。页面、文件权限、Git 数据和网络协议有各自的证据,修复时应回到对应层验证。
九、T0 / T1 / T2 与下一步工作
本文用 T0 表示基础构建运行,T1 表示本地主要工作流,T2 表示远程与增强能力。详细参数范围保存在 工程能力说明 和 路线图,这里按使用场景归纳:
| 阶段 | 当前成果 | 还需要完成的验证或实现 |
|---|---|---|
| T0:构建和终端 | 已有开发构建与模拟器安装启动记录;ArkTS 终端连接原生服务,提供历史、补全和基础内置命令。 | 实体 PC 键盘、输入法、剪贴板和窗口体验;已知路径显示刷新问题。 |
| T1:本地 Git | 已实现对象、索引、分支、标签、引用、reflog 等操作;模拟器记录直接覆盖 add → commit → status → log。 | 更多真实项目与授权目录回归;大型 pack 内存占用、长路径等验证;submodule 检出仍有限制。 |
| T2:远程和 Shell 扩展 | 已实现 HTTPS 引用发现、fetch、clone、push 与仅快进 pull,以及受支持的管道、重定向和 heredoc。 | 真实远程认证、权限恢复与异常网络验证;SSH、完整 PTY、外部进程和三路合并尚未实现。 |
这次适配让我更明确地看到了命令行工具的验收重点:界面保留了使用习惯,真正决定工具价值的仍是输入能否落到正确的仓库数据。add 保存哪一版内容,commit 如何形成历史,log 能否重新读出它们,才是这一阶段最扎实的结果。
补充运行记录:2026 年 9 月 11 日在本机创建了独立的 MateBook Pro 2in1 模拟器实例(HarmonyOS 6.1.1 / API 24),安装了按当前源码构建的开发 HAP,并确认应用进入 PC 横向窗口。下面的截图只证明 PC 窗口启动和终端界面适配;由于该实例的系统输入法会在自动化输入时改写空格和重定向符号,本轮没有把未能可靠复现的差异命令截图作为证据,也没有据此新增 Git 行为结论。

图2:2026年9月11日,在 HarmonyOS 6.1.1 / API 24 MateBook Pro 2in1 模拟器中安装当前开发 HAP 后的启动画面。画面保留 PC 横向窗口和桌面,截图不代表实体鸿蒙 PC 真机。
接下来需要把主机测试覆盖继续延伸到实体鸿蒙 PC 上,优先核对文件访问、输入体验和远程异常恢复,再逐步补齐能力边界。这样每增加一项命令,读者都能在源码、测试或设备记录中找到对应依据。
参考资料与源码入口
- AtomGit:ohos_harmony-git-bash:本文介绍的鸿蒙 PC 适配工程。
- 编译运行说明:工具链、构建与安装步骤。
- 原生仓库实现:索引、对象、提交和引用处理。
- 原生 fixture:使用系统 Git 核对数据互通。
- 2026年9月5日运行记录:模拟器环境、操作结果与已知问题。
- 上游归属与同步说明:Git for Windows 等上游来源及维护方式。
更多推荐


所有评论(0)