1. 需求分析
我为什么会有如本文标题所述这样的需求呢?这是因为在写的一个项目,新建里多个分支分,但有部份dll是每个分支共的代码,如果的A分支修改dll1 ,并而当前不想同步到其他的分支,后期其他的分支或者工程需要用到。此时,如果把整个dll代码复制过去,当然可以实现功能,但是,会缺失部份dll修改时的log,而且这样操作也麻烦,也不能保证代码统一性。
所以后面考虑使用此方法,思路如下:
1. 将现有 git 仓库中的子目录分离为独立仓库并保留其提交历史
2. 在现在库新建子模块,子模块内容为新的dll仓库
现有项目布局:
big-repo
├── .git
├── avatar-api
├── config-generator
├── register-email-validation
│ ├── bootstrap.php
│ ├── package.json
│ └── src
├── report-texture
└── name-of-folder
<---【我想把这个独立为一个新 repo】
├── bootstrap.php
├── package.json
├── routes.php
└── src
2. 文章描述约定
为了方便描述后续操作,这里稍微约定一下文章中各占位符的含义。
- 原来的仓库:
<big-repo>
- 想要分离出来的子文件夹名称:
<name-of-folder>
- 该子文件夹形成的新仓库:
<new-repo>
3. 方法一
# 进入父目录
cd big-repo
#为模块name-of-folder的目录创建一个新的分支名为 name-of-folder-branch
git subtree split -P name-of-folder name-of-folder-branch
#退到和父目录同级的目录
cd ..
#为name-of-folder新建一个和父目录同级的目录name-of-folder-dir
mkdir name-of-folder-dir
#进入新建的目录
cd name-of-folder-dir
#初始化git
git init
# 将分离出来的分支pull到新建的文件目录下
git pull ../big-repo big-repo -branch
git remote add origin XXXXXXXXX.git
git push -u origin master
4. 方法二
4.1. 实现子目录分离为独立仓库,使用 git filter-branch
除了使用新添加的 subtree
命令,你也可以使用 git 传统的所谓核弹级大杀器命令 —— filter-branch
解决上述问题。
首先,clone 一份原仓库并删掉原来的 remote:
git clone big-repo <new-repo>
cd <new-repo>
git remote rm origin
然后运行如下命令(这是重点):
git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter <name-of-folder> -- --all
这条命令同样会过滤所有历史提交,只保留所有对指定子目录有影响的提交,并将该子目录设为该仓库的根目录。这里说明各下个参数的作用:
--tag-name-filter
该参数控制我们要如何处理旧的 tag,cat 即表示原样输出;--prune-empty
删除空的(对子目录没有影响的)提交;--subdirectory-filter
指定子目录路径;-- --all
该参数必须跟在--
后面,表示对所有分支进行操作。如果你只想保存当前分支,也可以不添加此参数。
该命令执行完毕后就可以看到新仓库中已经变成子目录的内容了,且保留了关于该子目录所有的提交历史。不过只是这样的话新仓库中的 .git
目录里还是保存有不少无用的 object,我们需要将其清除掉以减小新仓库的体积(如果你用上面 subtree
的方法的话是不需要执行这一步的)。
以下git 指令一条一条执行就行,不需要修改
git reset --hard
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
git reflog expire --expire=now --all
git gc --aggressive --prune=now
4.2. 关联新仓库和上传
git commit -m "first commit"
git remote add origin XXXX.git
git push -u origin master