我的组织正准备使用github发布我们软件的开源版本,但是我不确定解决这个问题的最佳方法:
我们有两个分支主和发布,主包含我们决定不发布的一些专有组件,发布包含我们要分发的已清理版本。问题是,如果我们只是将发布分支推送到github,可以通过查看修订历史来检索专有组件。
我正在考虑创建一个单独的存储库,将 relase 的HEAD复制到其中,执行git init
,并将该存储库推送到github。但是,我们希望将来能够从主中挑选某些补丁到发布,并将这些更改推送到github。
有没有办法在不维护两个separte存储库的情况下执行此操作?
谢谢!
更新
更具体一点,这就是我们的提交历史目前的样子:
--- o - o - o - o - f - o - o - f - master
\
c - c - c - c - c - c - c - REL - f - f
其中'o'是 master 中的提交,专有分支,'c'是提交,删除不应发布的内容(通常不删除整个文件,但重新处理现有文件不依赖在专有组件上),'f'也是适用于发布的主中的修复,因此被挑选出来。 REL是我们认为可以安全发布的代码的标记版本,没有任何历史记录(甚至是发布分支的早期版本,因为在REL标记之前并未删除所有专有材料)。
答案 0 :(得分:17)
Ben Jackson的回答已经涵盖了一般性的想法,但我想在这里添加一些关于最终目标的注释(不仅仅是评论的价值)。
您可以很容易地拥有两个分支,一个具有完全干净(无私有文件)历史记录,一个完整(具有私有文件),并且可以适当地共享内容。关键是你要如何合并。过于简化的历史记录可能如下所示:
o - o - o - o - o - o - o (public)
\ \ \ \
x ----- x ----x---- x - x (private)
o
提交是“干净”提交,x
是包含一些私人信息的提交。只要您从公共合并到私有,它们都可以拥有所有所需的共享内容,而不会泄漏任何内容。正如Ben所说,你需要小心这一点 - 你不可能以其他方式合并。尽管如此,它仍然可以避免 - 而且你不必限制自己采摘樱桃。您可以使用正常的所需合并工作流程。
实际上,当然,您的工作流程最终会变得复杂一些。您可以在自己的分支上开发主题(feature / bugfix),然后将其合并到公共版本和私有版本中。你甚至可以偶尔选择樱桃。真的,任何事情都有,除了将私人合并到公众之外的关键例外。
因此,您现在的问题只是让您的存储库进入此状态。不幸的是,这可能非常棘手。假设存在一些涉及私有和公共文件的提交,我认为最简单的方法是使用filter-branch
来创建公共(干净)版本:
git branch public master # create the public branch from current master
git filter-branch --tree-filter ... -- public # filter it (remove private files with a tree filter)
然后创建一个临时的仅限私有分支,仅包含私有内容:
git branch private-temp master
git filter-branch --tree-filter ... -- private-temp # remove public files
最后,创建私有分支。如果你只有一个完整版本就可以了,你可以简单地合并一次:
git branch private private-temp
git merge public
这只会让你获得一个只有一次合并的历史记录:
o - o - o - o - o - o - o - o - o - o (public)
\
x -- x -- x -- x -- x -- x -- x --- x (private)
注意:这里有两个单独的root提交。这有点奇怪;如果你想避免它,可以使用git rebase --root --onto <SHA1>
将整个私有临时分支移植到公共分支的某个祖先。
如果你想拥有一些中级完整版本,你可以做同样的事情,只需停下来合并和改组:
git checkout -b private <private-SHA1> # use the SHA1 of the first ancestor of private-temp
# you want to merge something from public into
git merge <public-SHA1> # merge a corresponding commit of the public branch
git rebase private private-temp # rebase private-temp to include the merge
git checkout private
git merge <private-SHA1> # use the next SHA1 on private-temp you want to merge into
# this is a fast-forward merge
git merge <public-SHA1> # merge something from public
git rebase private private-temp # and so on and so on...
这将为您提供如下历史记录:
o - o - o - o - o - o - o - o - o - o (public)
\ \ \
x -- x -- x -- x -- x -- x -- x --- x (private)
同样,如果你希望他们有一个共同的祖先,你可以做一个初始的git rebase --root --onto ...
来开始。
注意:如果您已在历史记录中进行合并,则需要在任何rebase上使用-p
选项来保留合并。
编辑:如果重新编写历史记录真的是难以处理的话,你总是可以完全捏造它:将整个历史记录压缩到一个提交,在你已经拥有的相同根提交之上。像这样:
git checkout public
git reset --soft <root SHA1>
git commit
所以你最终会得到这个:
o - A' (public)
\
o - x - o - x - X - A (public@{1}, the previous position of public)
\
x - x (private)
其中A
和A'
包含完全相同的内容,X
是您从公共分支中删除所有私有内容的提交。
此时,您可以将public合并为private,从那时起,按照我在答案顶部描述的工作流程进行:
git checkout private
git merge -s ours public
-s ours
告诉git使用“我们的”合并策略。这意味着它将所有内容保留在私有分支中,并简单地记录合并提交,显示您将公共分支合并到其中。这可以防止git将这些“删除私有”更改从提交X
应用到私有分支。
如果根提交中包含私有信息,那么您可能希望创建一个新的根提交,而不是在当前提交之前提交一次。
答案 1 :(得分:5)
提交的SHA基于提交blob,其中包括父SHA,提交文本和文件树的SHA。树包含树中每个blob的SHA。因此,任何给定的提交都依赖于该修订中的所有内容以及每个父修订版返回到空的存储库。如果您从包含您不想发布的文件的版本(无论多么间接)派生提交,那么您不希望发布该分支。
git filter-branch
的第一个例子讨论了从存储库中删除机密文件的问题。它通过创建备用历史记录(重写所有树和提交)来完成此操作。如果你理解我答案的第一部分,你就会明白为什么这一定是真的。
您应该能够运行filter-branch命令以从“干净”提交创建新提交。历史将有些奇怪(旧版本可能无法构建,因为它们现在已不完整或以其他方式破坏)。这不会破坏存储库中的任何现有分支或blob。它将创建所有共享文件blob但不共享树或提交的新(并行)文件。您应该能够安全地推送该分支而不暴露任何它不引用的对象(当您推送分支时,只有该分支命名的SHA及其依赖项被推送)。但是,这有点冒险,因为一个git merge
进入“干净”分支,你最终可能会拖入“私有”分支和对象。您可能希望使用钩子(提交或推送触发器)来仔细检查私有文件是否未转义。