子git存储库作为主存储库的子集

时间:2014-11-18 16:13:23

标签: git github

我正在寻找一种方法来设置git存储库,其中包含来自更大存储库的文件子集,并从该主存储库继承历史记录。我的主要动机是能够通过GitHub共享代码的子集。

我目前通过单个git存储库管理我的研究相关(主要是Matlab)代码。代码本身被松散地组织成少数文件夹,其代码依赖性经常跨越文件夹。我不想上传整个存储库的远程副本,因为它包含了许多其他人不想要的混合项目。

我的精神图片涉及每个项目的单独存储库,该存储库仅跟踪该项目的相关文件,但继承了主存储库中的所有提交。理想情况下,我希望能够将这些子存储库中的版本与主存储库中的版本分开,但这不是必需的。我查看了git子模块,子树和gitslave,但所有这些似乎都假设子项目是文件的隔离集合,而在我的情况下,许多子项目与其他子项目共享文件。我还尝试创建一个特定于项目的分支,git rm - 不相关的文件,但是一旦我需要将更改从主分支合并到项目分支(由于更改导致的一系列冲突),我就会崩溃项目删除的文件)。

统计数据:

  • 主存储库中的8096个文件
  • 我要分享的14个子项目
  • 这些子项目中的394个文件
  • 276个文件仅属于1个项目,57个到2个,60个到3个,1到6个。

我目前只是通过将每个项目的相关文件定期复制到新文件夹来共享代码。但这意味着新副本没有附加提交历史记录。是否有更强大的方法来共享这些不同的代码子集,并使它与我所做的更改保持同步?

3 个答案:

答案 0 :(得分:1)

据我所知,您的问题

  • 您有一个包含多个子项目的大型仓库
  • 您要提取并共享每个子项目作为其自己的存储库,仍然包含(仅)该子项目的历史记录/提交
  • 这些子项目共享一些文件=>这意味着一个子项目使用的文件没有严格包含在单个子目录中,因为一个文件可以在多个子项目中使用,这就是为什么您不能简单地使用{{1} }或git subtree

将文件的一部分历史记录提取到专用分支(然后可以将其推送到专用存储库)的一种方法是使用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
  • 所有未触及子项目文件之一的提交都将从该分支(git ls-files --cached | grep -v -E '$file_list_regex')中删除。
  • 此操作不会签出每个修订,而仅对索引(--prune-empty / --index-filter)起作用。

这是一次一次性操作,但是据我了解您的问题,您想用新的提交连续更新提取的子项目存储库/分支。 好消息是,您可以重复执行此命令,因为--cached始终会为子项目分支产生相同的提交/历史记录-假设您不手动更改它们或重写主分支。

这样做的缺点是,每次{em} {em} {em} 完整的历史记录,每个子项目一次又一次。 假设您只想将git filter-branch分支的最后5次提交添加到现有filter-branch分支的尖端,则可以修改以下命令:

master

说明:

  • 这只会重写直到subproject1为止的最近5次提交(# 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 ),我们认为这是对git filter-branch [...] ${common_base_commit}..HEAD当前提示的等同提交。
  • 对于(第一次)提交,它将从master~6subproject1master~6)重写其父级,从而有效地将5个重写的提交重新置于subproject1之上。
  • 最后,我们只需要更新--parent-filter 'sed s/${common_base_commit}/${subproject_tip}/g'即可在其顶部包括新提交。

进一步的优化/自动化:

  • 实施更好的逻辑以列出要包含(subproject1)或实际上要从给定子项目中排除(subproject1)的文件
  • 使要包含的文件列表取决于当前提交($file_list_regex)或将列表签入存储库本身,以防每个子项目要包含的文件随时间变化
  • 找到一种自动方法来查找当前主项目中子项目分支提示的“等效”提交
  • 使用漂亮的git别名组合所有内容,因此您只需使用git ls-files --cached | grep -v -E '$file_list_regex'

答案 1 :(得分:0)

首先让我总结一下您的问题:

  • 您的仓库很大
  • 您想将其拆分为子存储库
  • 您想保持历史的完整性

从您的统计信息中,我可以看到您有14个子项目存储在一个主资源库中。这通常是一个非常糟糕的解决方案,因为请记住,每当有人克隆存储库时,它也会获得所有子项目的完整历史记录。例如,如果我想为您的一个子项目做贡献,那么我不想携带您拥有的所有8096文件。

如果项目彼此无关,则将其拆分为子存储库。使用GitHub,您可以创建organizations。不要犹豫创建自己的组织,并将所有子项目放入其中。主要优点是每个子项目将具有:

  • 它自己的Wiki
  • 它自己的问题跟踪器
  • 它自己的首页

如果您有相关项目,则每个项目都需要从特定提交中获取。我建议您使用git submodules。例如,如果您查看ext/文件夹中的TortoiseGit project,则会注意到指向其他存储库的链接。

另一种解决方案是使用git subtree,这似乎并不是解决您问题的最佳方法。

如果您的主存储库属于以下任何类别,则应查看使用Git的方式:

  • Git存储库超过100 MB
  • Git存储库存储工件(.exe.tmp,二进制文件,生成的文件,.pdf ...)

您的存储库在GitHub上公开吗?

答案 2 :(得分:0)

您正在寻找git submodules

  

在一个项目上工作时,经常需要在其中使用另一个项目。也许这是第三方开发的图书馆,或者您是在单独开发并在多个父项目中使用的图书馆。在这些情况下会出现一个常见问题:您希望能够将这两个项目视为独立的项目,但仍然能够在另一个项目中使用它们。

子模块上的TL; DR是它们是包含在其他存储库中的存储库。

父级回购协议唯一了解子级的是子级告知的最后一次提交的SHA,因此每个回购协议都是相互独立管理的,但是它们相互引用,因此您可以编写它们在一起。

这是该主题上写得好的blog post from GitHub