背景
正在开发的一个项目中引用了第三方库的源码,由于历史原因,源码的引用并不是很规范(直接下载下来后作为自己项目的部分源码使用,还进行了一些修改),具体如下:
- 我有一个本地git项目project_local,其基本目录结构如下:
其中,vpf/PyNvCodec、vpf/PytorchNvCodec均为目录,下面还有子目录和各种文件,正是这两个目录来源于开源项目project_local |-- CHANGELOG.md |-- CMakeLists.txt |-- README.md |-- jsoncpp | |-- CMakeLists.txt | |-- json | `-- jsoncpp.cpp |-- thread_pool.h `-- vpf|-- CMakeLists.txt|-- FindVPF.cmake|-- PyNvCodec|-- PytorchNvCodec|-- VideoCapture.cpp|-- VideoCapture.h|-- VideoWriter.cpp`-- VideoWriter.h
- 线上github源码工程为project_online,其基本目录结构为:
project_online |-- CMakeLists.txt |-- LICENSE |-- PyNvCodec | |-- CMakeLists.txt | |-- TC | |-- inc | |-- pybind11-2.1.1 | `-- src |-- PytorchNvCodec | |-- CMakeLists.txt | `-- src |-- README.md |-- SampleDecode.py |-- SampleDecodeMultiThread.py |-- SampleDecodeSw.py |-- SampleDemuxDecode.py |-- SampleEncode.py |-- SampleEncodeMultiThread.py |-- SamplePyTorch.py |-- SampleTensorRTResnet.py |-- SampleTorchResnet.py `-- Tests.py
- project_local项目中的vpf目录使用了project_online工程,即vpf/PyNvCodec、vpf/PytorchNvCodec两个目录均来源于project_online项目下的PyNvCodec和PytorchNvCodec
- 具体需要求是:project_online项目有更新了(新版本v1.1),我想把这些更新应用在vpf/PyNvCodec、vpf/PytorchNvCodec这两个目录上,其他文件和目录均保持不变
实现方法
推荐方法:推荐方法:使用子树合并策略 (Subtree Merge)
# 进入本地项目目录
cd /path/to/project_local# 1. 添加原始仓库为远程源
git remote add online https://github.com/original-owner/project_online.git
git fetch online# 2. 选择要合并的版本(标签或分支)
TARGET_VERSION="v1.1" # 替换为实际版本# 3. 更新 PyNvCodec
git subtree pull --prefix=vpf/PyNvCodec online $TARGET_VERSION -m "Update PyNvCodec to $TARGET_VERSION"# 4. 更新 PytorchNvCodec
git subtree pull --prefix=vpf/PytorchNvCodec online $TARGET_VERSION -m "Update PytorchNvCodec to $TARGET_VERSION"# 5. 保留本地修改的文件(根据情况看是否执行)
git checkout HEAD -- vpf/PyNvCodec/local_settings.py
git checkout HEAD -- vpf/PytorchNvCodec/custom_config.h
这种方法确保:
- 精确更新指定目录
- 保留本地其他文件的修改
- 维护清晰的提交历史
- 支持后续持续更新
- 提供冲突解决机制
其他
关于git subtree pull 命令详解:
git subtree pull --prefix=vpf/PyNvCodec online $TARGET_VERSION -m "Update PyNvCodec to $TARGET_VERSION"
这个命令是 Git 子树策略的核心操作之一,下面我将详细解释其工作原理和每个参数的作用。
命令结构解析
命令部分 | 说明 |
---|---|
git subtree pull | 子树拉取操作 |
--prefix=vpf/PyNvCodec | 指定本地目标目录 |
online | 远程仓库别名 |
$TARGET_VERSION | 要拉取的远程版本(分支/标签) |
-m "Update PyNvCodec to $TARGET_VERSION" | 合并提交信息 |
工作原理详解
1. 子树合并的本质
git subtree
是一种将外部仓库作为子目录集成到当前仓库的策略。与子模块不同,子树的内容直接属于主仓库,不需要额外的引用文件。
2. 命令执行流程
当执行 git subtree pull
时,Git 会执行以下操作:
- 创建临时分支:在后台创建一个临时分支,包含远程仓库指定版本的内容
- 提取子树历史:从临时分支中提取与指定目录相关的提交历史
- 重写路径:将远程路径重写为本地
--prefix
指定的路径- 远程:
PyNvCodec/file.txt
→ 本地:vpf/PyNvCodec/file.txt
- 远程:
- 合并到当前分支:将重写后的提交合并到当前分支
- 创建合并提交:使用
-m
指定的消息创建合并提交 - 清理临时对象:删除临时分支和相关对象
3. 路径映射机制
原始远程结构: 本地映射后结构:
PyNvCodec/ vpf/PyNvCodec/
├── src/ ├── src/
├── inc/ ├── inc/
└── CMakeLists.txt └── CMakeLists.txt
Git 会自动处理路径转换,确保远程文件放入正确的本地目录。
关键特性与优势
1. 历史保留
- 保留完整的提交历史(包括作者、日期等信息)
- 可以通过
git log vpf/PyNvCodec
查看子树历史 - 保持原始提交哈希不变(通过重写实现)
2. 双向同步
- 拉取更新:
git subtree pull
- 推送修改:
git subtree push --prefix=... online branch
- 允许将本地修改推送回原始仓库
3. 冲突处理
当子树目录有本地修改时:
- Git 会自动尝试合并
- 如果自动合并失败,会暂停并提示解决冲突
- 解决冲突后使用
git commit
完成合并
4. 与普通合并的区别
特性 | git subtree pull | 普通 git merge |
---|---|---|
影响范围 | 仅指定目录 | 整个仓库 |
历史处理 | 重写路径后保留原始历史 | 保留原始路径 |
冲突概率 | 更低(仅限子树目录) | 更高(整个仓库) |
工作目录 | 不需要干净状态 | 需要干净状态 |
使用示例场景
初始添加子树
# 首次添加子树(创建初始提交)
git subtree add --prefix=vpf/PyNvCodec online v1.0.0 -m "添加PyNvCodec v1.0.0"
更新子树
# 更新到新版本
git subtree pull --prefix=vpf/PyNvCodec online v1.2.0 -m "更新PyNvCodec到v1.2.0"
处理冲突
# 如果发生冲突
git mergetool -t vscode # 使用VS Code解决冲突
git commit -m "解决PyNvCodec合并冲突"
查看子树历史
git log --graph --oneline --decorate -- vpf/PyNvCodec
高级选项
1. 压缩历史 (--squash
)
git subtree pull --prefix=... --squash -m "更新"
- 优点:创建一个合并提交,不引入外部历史,保持仓库整洁
- 缺点:丢失详细提交历史,无法追溯子树内部变更
2. 指定合并策略
git subtree pull --prefix=... -X theirs # 优先使用远程版本
git subtree pull --prefix=... -X ours # 优先使用本地版本
3. 拆分子树历史
# 查看子树历史
git subtree split --prefix=vpf/PyNvCodec --rejoin
常见问题解决方案
-
目录不存在错误
mkdir -p vpf/PyNvCodec # 确保目标目录存在 touch vpf/PyNvCodec/.gitkeep # 添加占位文件 git add vpf/PyNvCodec/.gitkeep git commit -m "创建PyNvCodec目录"
-
权限拒绝错误
# 确保没有未提交的修改 git status# 暂存当前修改 git stash# 执行子树拉取 git subtree pull ...# 恢复暂存修改 git stash pop
-
大型仓库优化
# 浅层克隆历史 git subtree pull --prefix=... --squash# 限制历史深度 git config subtree.online.shallow true
与替代方案的比较
方案 | 优点 | 缺点 |
---|---|---|
子树策略 | 无需外部依赖,完整历史 | 学习曲线较陡峭 |
子模块 | 明确分离,易于更新 | 需要额外命令,协作复杂 |
手动复制 | 简单直观 | 丢失历史,难以更新 |
仓库合并 | 保留完整历史 | 污染主仓库历史 |
git subtree pull
是管理项目内嵌外部代码库的专业解决方案,特别适合需要保留历史同时精确控制更新范围的情况。