在项目git库中引入依赖库git子库进行开发
最新版:
在项目git库中引入依赖库git子库进行开发 – 知乎有时候,你的论文,项目之类的代码,可能共同访问一个你自己的依赖库。
比如,我的深度学习代码A,以及代码B,是两个项目的工程文件和代码,此时,A和B称作项目库,他们同时需要依赖一个文件夹下的代码,记为C。这个C可能是一个类似于依赖库的角色,例如对于复数神经网络的基本模型的搭建。A和B都需要同时依赖C,那么,多个项目文件夹下,就得手动同步C文件夹的更新。
实际上git的子库模式是可以自动同步的。
git子库的原理是:
- 将依赖库和项目库中除了依赖库文件夹下以外的代码,分离为两个git仓库。
- 项目库将依赖库作为子库管理。相当于建立了一个指针,不直接参与子库的分支流程中。
- 子库可以不止一个。
(git子库:submodule,其实应该叫“子模块”,但是我觉得这东西应该叫“子库”)
换而言之,
- 这相当于让你的项目库中复用的代码成为了一个独立的git库。你写代码的时候,当作整体去写,但是提交更改的时候,签出,拉取推送,分支,则是项目库和子库各干各的。
- 子库和项目库都可以有远端仓库,而且可以不在一起。
- 子库内的branch,提交和拉取和项目库的,是独立的,互相不影响
你在PyTorch的官方代码,以及最新最热门的flashMLA代码内都能看见这种操作。
如果你使用vscode开发,那么更方便。
注意:本文不适合git新手,你应该至少比较熟悉git的分支,拉取,推送之后,再阅读本文。这里不管是擅长用命令行还是gitGUI,还是vscode内的git支持,只要熟悉,并且清楚git的基本用法即可。
注意:下文中,子库一般是依赖库,项目库就是父级库。各种操作,首先应该需要注意,是在哪个库下操作。
如何将现有项目库中的依赖库分离为子库?
假设你的工程目录结构如下:存在两个子库lib1和lib2。
my_project/ # 工程根目录
├── lib1/ # 子库2
│ ├── ....
│ └── ....
├── net/
│ ├── ..... # 其他工程文件
│ ├──lib2/ # 子库2
│ └── .....
│ └── ....
├── .....
└── .... # 其他工程文件
首先,备份隐藏的.git文件夹,复制出去一个,这样折腾坏了, 删除坏掉的.git,把备份的扔进来还能重新开始。
然后,将lib1和lib2单独建立为依赖库仓库。你可以将他们两个复制出去,然后建立本地仓库或者远端仓库。例如地址为user@server:/media/data/prj1/lib1/.git。
对于lib1
然后,删除项目库下的lib1文件夹。
然后,在项目库执行
git submodule add user@server:/media/data/prj1/lib1/.git
# 注意,远端并非一定需要是服务器,也可以是本机,git本来就是分布式的。
git submodule add user@mypc:~/prj1/lib1/.git
然后,依次执行:
git submodule init # 初始化子库
git submodule update # 同步子库,这样子库的文件才会从远端下载回来
即可。
之后的项目开发:
- 现在你的项目中有两个git仓库,一个是项目库,这个库里面有一个类似于指针一样的子库。你每次更改项目库的代码直接在项目库的git仓库提交即可。如果更改了依赖库的代码,那么需要在依赖库的git仓库提交。也就是项目代码是整体,但是git仓库各干各的。
- 注意,如果有多个开发的话,项目库和依赖库都需要各自的分支,处理分支冲突的方法也一样。
- 多个项目公用一个依赖库的时候,建议不同项目中的子库使用名称不同的分支防止合并冲突。
git clone user@server:/media/data/prj1/.git在新的计算机上搞下来项目库的时候, 子库默认是不会自动同步的。你需要执行
git submodule init
git submodule update
之后,才是完整的。同理,远端仓库可能也需要更新。(我的远端仓库就是GPU服务器,需要执行代码的。如果你的远端仓库只是用来当云盘,那无所谓)
对于lib2
lib2在一个子文件夹下。你需要将一个命令改一下就好:
git submodule add user@server:/media/data/prj1/lib2/.git ./net/lib2/ # 指定目录,注意路径包括库本身的名字
关于分支
分支很重要。虽然上面没有写出来任何指令,但是切记,协同开发的时候,一定要给每个子库和项目库都签出到合适的分支上。

关于拉取代码
存在子库的代码拉取后,还要通过那两个命令让子库的代码也拉下来。因为它两是独立的,使用这个代码拉取所有子库代码:
git submodule init
git submodule update
移除子库
2. git bash输入:git submodule deinit -f — 01_SRC/ASW/HL_test
git submodule deinit -f -- 01_SRC/ASW/HL_test
- 操作:反初始化子模块。
- 目的/原因: 这是官方推荐的、用于解除子模块关联的第一步。它会从
.git/config文件中移除该子模块的配置信息,并清空工作区中该子模块的文件夹内容。-f(force) 参数用于确保即使有本地修改也能强制执行。
3. git bash输入:rm -rf .git/modules/01_SRC/ASW/HL_test
- 操作: 手动删除
.git/modules目录下对应的子模块文件夹。 - 目的/原因:彻底清除。
deinit命令只清除了配置和工作区,但子模块本身的 Git 数据库仍存储在父仓库的.git/modules目录中。这一步是手动将这些底层的 Git 数据彻底删除,防止留下任何孤立的数据。
4. git bash输入:git rm -f 01_SRC/ASW/HL_test
git rm -f 01_SRC/ASW/HL_test
- 操作: 从 Git 索引中删除子模块。
- 目的/原因: 这一步是正式告诉主仓库:“我要移除这个子模块的记录”。它会执行两个关键动作:
- 从
.gitmodules文件中删除关于 HL_test 的定义。 - 将“删除 HL_test 文件夹”这个操作暂存(stage)起来,准备提交。
5. update current change as a commit (git bash输入:1.git add . 2. git commit)
- 操作: 提交一次变更,内容是“删除了 HL_test 子模块”。
- 目的/原因: 在这个流程中,这是一个临时步骤。它创建了一个 commit,记录了刚刚完成的删除操作。这个 commit 稍后会被“修正”。
6. add the backup folder as source code into the repo 01_SRC/ASW/HL_test
- 操作: 将第 1 步备份的源代码文件夹,复制回原来的路径
01_SRC/CSWC_ASW/HL_test。 - 目的/原因: 现在,HL_test 不再是一个子模块链接,而是一个包含实际代码的普通文件夹。
7. update current change as a amend commit (git bash输入:1.git add . 2. git commit –amend)
- 操作: 将新添加的源代码暂存,然后使用
commit --amend命令来修正上一步的提交。 - 目的/原因:这是整个流程中最巧妙的一步。
git commit --amend不会创建新的 commit,而是将当前暂存的更改(新加入的源代码)与上一个 commit(第 5 步的“删除”操作)合并成一个全新的 commit。- 最终效果:Git 历史中不会有“先删除后添加”的过程。从历史记录来看,只有一个 commit,其效果就是“将子模块 HL_test 替换为其实际源代码”。这使得版本历史非常干净、清晰且具有原子性。
8. push the change to the remote repo
【eos】
