将git仓库的一个分支推送到一个新的远程仓库(github),隐藏它的历史记录 [英] Push a branch of a git repo to a new remote (github), hiding its history

查看:145
本文介绍了将git仓库的一个分支推送到一个新的远程仓库(github),隐藏它的历史记录的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的组织正在准备使用github发布我们的软件的开源版本,但我不确定最好的方法来处理这个问题:

我们有两个分支 master 发布 master 包含我们决定不发布的一些专有组件,发布包含我们想要分发的清理版本。问题是,如果我们只是将 release 分支推送到github,那么可以通过查看修订历史记录来检索专有组件。



I正在考虑创建一个单独的存储库,将 relase 的HEAD复制到它中,执行 git init ,并将该存储库推送到github。但是,我们希望将来能够从 master 挑选某些修补程序到 release 中,并将这些修改推送到github。



有没有办法做到这一点,而无需维护两个separte存储库?



谢谢!



更新:

更具体一点,这就是我们现在的提交历史记录的样子:

  --- o  -  o  -  o  -  o  -  f  -  o  -  o  -  f  -  master 
\
c - c - c - c - c - c - c - REL - f - f

master 中,专有分支'c'是提交内容,用于删除不应该发布的内容(通常不会删除整个文件,但是修改现有内容不依赖于专有组件)和'f'在主控中修正了发布的问题,因此已被樱桃采摘。 REL是我们认为可以安全发布的代码的标签版本,没有任何历史记录(甚至是发布分支的以前版本,因为并非所有专有材料都在REL标签之前被删除)。
本杰克逊的答案已经涵盖了总体思路,但是我想在这里添加一些关于最终目标的笔记(不仅仅是评论的价值)。

解决方案

/ p>

您可以非常容易地拥有两个分支,一个完全清理(无私密文件)历史记录,一个完整(使用私有文件),并适当地共享内容。关键是要小心你如何合并。一个简单的历史可能看起来像这样:

  o  -  o  -  o  -  o  -  o  -  o  -  o(public)
\\\\\
x ----- x ---- x ---- x - x(private)

o 提交是干净的提交,而 x 是包含一些私人信息的那些。只要你从公共到私人合并,他们都可以拥有所有需要的共享内容,而不会泄漏任何东西。正如本说的那样,你确实需要小心这一点 - 你永远无法以另一种方式合并。尽管如此,这是完全可以避免的 - 而且你不必限制自己去挑选樱桃。您可以使用正常的合并工作流程。



实际上,当然您的工作流程可能会更复杂一些。您可以在自己的分支上开发一个主题(功能/错误修复),然后将其合并到公共和私有版本中。你甚至可以随时挑选。真的,任何事情都会发生,将私有合并为公共的关键例外。


$ b

filter-branch



你现在的问题只是让你的仓库进入这个状态。不幸的是,这可能非常棘手。假设存在一些触及私有和公共文件的提交,我相信最简单的方法是使用 filter-branch 来创建公共(干净)版本:

  git branch public master#从当前主文件创建公共分支
git filter-branch --tree-filter ... - - public#过滤它(使用树型过滤器移除私人文件)

然后创建一个临时的仅限于private分支,只包含私有内容:

  git分支private-temp master 
git filter-branch --tree-过滤器... - private-temp#删除公共文件

最后,创建私有分支。如果你只有一个完整版本,你可以简单地合并一次:

  git branch private private-temp 
$ git merge public

这会给你一个只有一次合并的历史记录:


$ b $

  o  -  o  -  o  -  o  -  o  -  o  -  o  -  o  -  o  -  o(public)
\
x - x - x - x - x - x - x - x(private)

注意:这里有两个单独的提交。这有点奇怪,如果你想避免它,你可以使用 git rebase --root --onto< SHA1> 将整个私人临时分支移植到公共分支的某个祖先。



如果你想有一些中间完整版本,你可以做同样的事情,只是在这里和那里停止合并和重新分配:

  git checkout -b private< private-SHA1> #使用private-temp 
#的第一个祖先的SHA1,您想要将公共内容合并到
中git merge< public-SHA1> #合并相应的公共分支提交
git rebase private private-temp#rebase private-temp包含合并
git checkout private
git merge< private-SHA1> #使用private-temp上的下一个SHA1来合并到
#这是一个快进合并
git merge< public-SHA1> #从公共
合并一些东西git rebase private private-temp#等等等等...

这会为你带来一段历史:

  o  -  o  -  o  -  o  -  o  -  o  - o  -  o  -  o  -  o(public)
\\\\
x - x - x - x - x - x - x - x(private)

再一次,如果你想让它们有一个共同的祖先,你可以做一个初始的 git rebase --root --onto ... 开始使用。



注意:如果您已经在历史中进行了合并,我们希望在任何资产上使用 -p 选项来保留合并。



假它



编辑:如果重新编写历史记录真的变得棘手,那么您总是可以完全捏造它:将整个历史压缩到一个提交,在同一个根目录之上提交有。就像这样:

  git checkout public 
git reset --soft< root SHA1>
git commit

所以你最终得到这个结果:

  o  -  A'(public)
\
o - x - o - x - X - A(public @ {1 },以前的位置)
\
x - x(私人)



<其中 A A'包含完全相同的内容,并且 X 是您从公共分支中删除所有私人内容的提交。



此时,您可以将公共私有内容合并为私有内容,并且从那以后,请按照我在答案顶部描述的工作流程进行操作:

  git checkout private 
git merge -s我们的公共

-s我们的告诉git使用我们的合并策略。这意味着它保持所有内容与私有分支完全相同,并简单地记录一个合并提交,表明您已将公共分支合并到其中。这可以防止git永远将从commit X 中的私有更改应用到私人分支。



如果root提交中包含私有信息,那么您可能需要创建一个新的根提交,而不是在当前提交之前提交一次。


My organisation is preparing to release an open-source version of our software using github, however I'm not sure the best way to approach this:

We have two branches master and release, master contains some proprietary components that we have decided not to release, and release contains the cleaned-up version that we want to distribute. The problem is, if we just push the release branch to github, the proprietary components can be retrieved by looking through the revision history.

I was considering creating a separate repository, copying the HEAD of relase into it, doing a git init, and pushing that repository to github. However, we want to retain the ability to cherry-pick certain patches from master into release in the future, and push those changes up to github.

Is there a way to do this without maintaining two separte repositories?

Thanks!

Update:

To be a little more specific, this is sort-of what our commit history looks like at the moment:

--- o - o - o - o - f - o - o - f - master
             \
              c - c - c - c - c - c - c - REL - f - f

Where 'o' are commits in the master, proprietary branch, 'c' are commits that remove things that should not be published (often not removing entire files, but reworking existing ones not to rely on proprietary components), and 'f' are fixes in master that apply to release as well, and so have been cherry-picked. REL is a tagged version of the code we deem safe to publish, with no history whatsoever (even previous versions of the release branch, since not all the proprietary material had been removed before the REL tag).

解决方案

Ben Jackson's answer already covers the general idea, but I'd like to add a few notes (more than a comment's worth) about the ultimate goal here.

You can quite easily have two branches, one with an entirely clean (no private files) history, and one complete (with the private files), and share content appropriately. The key is to be careful about how you merge. An oversimplified history might look something like this:

o - o - o - o - o - o - o (public)
 \       \           \   \
  x ----- x ----x---- x - x (private)

The o commits are the "clean" ones, and the x are the ones containing some private information. As long as you merge from public to private, they can both have all the desired shared content, without ever leaking anything. As Ben said, you do need to be careful about this - you can't ever merge the other way. Still, it's quite possible to avoid - and you don't have to limit yourself to cherry-picking. You can use your normal desired merge workflow.

In reality, your workflow could end up a little more complex, of course. You could develop a topic (feature/bugfix) on its own branch, then merge it into both the public and the private versions. You could even cherry-pick now and then. Really, anything goes, with the key exception of merging private into public.

filter-branch

So, your problem right now is simply getting your repository into this state. Unfortunately, this can be pretty tricky. Assuming that some commits exist which touch both private and public files, I believe that the simplest method is to use filter-branch to create the public (clean) version:

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)

then create a temporary private-only branch, containing only the private content:

git branch private-temp master
git filter-branch --tree-filter ... -- private-temp    # remove public files

And finally, create the private branch. If you're okay with only having one complete version, you can simply merge once:

git branch private private-temp
git merge public

That'll get you a history with only one merge:

o - o - o - o - o - o - o - o - o - o (public)
                                     \
  x -- x -- x -- x -- x -- x -- x --- x (private)

Note: there are two separate root commits here. That's a little weird; if you want to avoid it, you can use git rebase --root --onto <SHA1> to transplant the entire private-temp branch onto some ancestor of the public branch.

If you'd like to have some intermediate complete versions, you can do the exact same thing, just stopping here and there to merge and rebase:

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...

This will get you a history something like this:

o - o - o - o - o - o - o - o - o - o (public)
      \              \               \
  x -- x -- x -- x -- x -- x -- x --- x (private)

Again, if you want them to have a common ancestor, you can do an initial git rebase --root --onto ... to get started.

Note: if you have merges in your history already, you'll want to use the -p option on any rebases to preserve the merges.

fake it

Edit: If reworking the history really turns out to be intractable, you can always totally fudge it: squash the entire history down to one commit, on top of the same root commit you already have. Something like this:

git checkout public
git reset --soft <root SHA1>
git commit

So you'll end up with this:

o - A' (public)
 \
  o - x - o - x - X - A (public@{1}, the previous position of public)
               \
                x - x (private)

where A and A' contain exactly the same content, and X is the commit in which you removed all private content from the public branch.

At this point, you can do a single merge of public into private, and from then on, follow the workflow that I described at the top of the answer:

git checkout private
git merge -s ours public

The -s ours tells git to use the "ours" merge strategy. This means it keeps all content exactly as it is in the private branch, and simply records a merge commit showing that you merged the public branch into it. This prevents git from ever applying those "remove private" changes from commit X to the private branch.

If the root commit has private information in it, then you'll probably want to create a new root commit, instead of committing once on top of the current one.

这篇关于将git仓库的一个分支推送到一个新的远程仓库(github),隐藏它的历史记录的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆