我正在寻找一种方法来设置 git 存储库,其中包括来自较大存储库的文件子集,并从该主存储库继承历史记录。我的主要动机是能够通过 GitHub 共享代码子集。
我目前通过单个 git 存储库管理我的研究相关(主要是 Matlab)代码。代码本身松散地组织成几个文件夹,代码依赖关系通常跨文件夹。我不想上传整个存储库的远程副本,因为它包含许多其他人不想要的完整项目。
我对此的想象是每个项目都有一个单独的存储库,该存储库仅跟踪该项目的相关文件,但继承了主存储库的所有提交。理想情况下,我希望能够在这些子存储库中标记版本,与主存储库分开,但这不是必需的。我研究了 git 子模块、子树和 gitslave,但所有这些似乎都假设子项目是独立的文件集合,而在我的情况下,许多子项目与其他子项目共享文件。我还尝试创建一个特定于项目的分支,git rm
-ing 不相关的文件,但是当我需要将主分支的更改合并到项目分支中时,它就崩溃了(由于项目删除的文件的更改而导致混乱)。
统计数据:
- 主存储库中有 8096 个文件
- 我想分享的 14 个子项目
- 这些子项目中共有 394 个文件
- 276个文件只属于1个项目,57个属于2个,60个属于3个,1个到6个。
目前,我通过简单地将每个项目的相关文件定期复制到新文件夹来共享代码。但这意味着新副本没有附加提交历史记录。是否有更可靠的方法来共享这些不同的代码子集,并使它们与我所做的更改保持同步?
据我了解你的问题
- 你有一个包含多个子项目的大仓库
- 您想要提取并共享每个子项目作为其自己的存储库,仍然包含(仅)该子项目的历史记录/提交
- 子项目共享一些文件=>这意味着一个子项目使用的文件并不严格包含在单个子目录中,因为一个文件可能在多个子项目中使用,这就是为什么你不能简单地使用
git subtree
or git submodules
将文件子集的历史记录提取到专用分支(然后您可以将其推送到专用存储库)的一种方法是使用git filter-branch
:
# regex to match the files included in this subproject, used below
file_list_regex='^subproject1/|^shared_file1$|^lib/shared_lib2$'
git checkout -b subproject1 # create new branch from current HEAD
git filter-branch --prune-empty \
--index-filter "git ls-files --cached | grep -v -E '$file_list_regex' | xargs -r git rm --cached" \
HEAD
这会
- 首先创建一个新分支
subproject1
基于当前的HEAD
(git checkout -b subproject1
)
- 遍历它的整个历史(
git filter-branch [...] HEAD
)
- 删除所有文件(
xargs -r git rm --cached
)那是not子项目的一部分(git ls-files --cached | grep -v -E '$file_list_regex'
)
- 所有未触及子项目文件之一的提交都将从该分支中删除(
--prune-empty
).
- 此操作不会签出每个修订版本,而是仅对索引进行操作(
--index-filter
/--cached
).
虽然这是一项一次性操作,但据我了解您的问题,您希望使用新的提交不断更新提取的子项目存储库/分支。
好消息是您可以简单地重复此命令,因为git filter-branch
将始终为您的子项目分支生成相同的提交/历史记录 - 假设您不手动更改它们或重写您的主分支。
这样做的缺点是这会filter-branch
the complete历史每一次并为每个子项目一次又一次。
鉴于您只想添加最后 5 次提交master
分支到现有的尖端subproject1
分支你可以像这样调整命令:
# get the full commit ids for the commits we consider
# to be equivalent in master and subproject1 branch
common_base_commit="$(git rev-parse master~6)"
subproject_tip="$(git rev-parse subproject1)"
# checkout a detached HEAD so we don't change the master branch
git checkout --detach master
git filter-branch --prune-empty \
--index-filter "git ls-files --cached | grep -v -E '$file_list_regex' | xargs -r git rm --cached" \
--parent-filter "sed s/${common_base_commit}/${subproject_tip}/g" \
${common_base_commit}..HEAD
# force reset subproject1 branch to current HEAD
git branch -f subproject1
解释:
- 这只会重写最后 5 次提交(
git filter-branch [...] ${common_base_commit}..HEAD
) up to master~6
我们认为这相当于承诺subproject1
当前提示。
- 对于这些提交(第一个),它将重写其父级
master~6
to subproject1
(--parent-filter 'sed s/${common_base_commit}/${subproject_tip}/g'
)有效地重新调整 5 个重写的提交subproject1
.
- 最后我们只需要更新
subproject1
将新的提交包含在其之上。
进一步优化/自动化:
- 实现更好的逻辑来列出您想要包含的文件(
$file_list_regex
) 或实际上排除 (git ls-files --cached | grep -v -E '$file_list_regex'
) 来自给定的子项目
- 使要包含的文件列表取决于当前提交(
$GIT_COMMIT
)或将列表签入存储库本身,以防每个子项目包含的文件可能随着时间的推移而改变
- 找到一种自动方法来查找当前主项目中子项目分支提示的“等效”提交
- 将所有内容组合在一个漂亮的 git 别名中,这样你就可以简单地使用
git update-project subproject1
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)