如何对其他遥控器隐藏一个遥控器的提交历史记录?



假设我有多个用于单个存储库的远程。大多数情况下,我使用一个 git 帐户进行开发,完成后,我将最终版本推送到另一个远程。现在,如何从第二个远程隐藏第一个远程的提交历史记录?

我会告诉你如何做你要求的事情,然后告诉你为什么这是一个坏主意。

在任何 Git 存储库中,历史记录只是该存储库中的提交集,由该存储库中的名称集找到。 此查找过程向后工作,因为 Git 始终向后工作。 我们稍后将看到更多关于此的内容。

背景

请记住,每个提交都有一个唯一的哈希 ID。 这实际上是提交的真实名称。 要查看带有git log的提交,您必须以某种方式将 Git 告知提交的哈希 ID。 然后,Git 可以从存储库数据库中检索该提交,前提是它首先在数据库中。

每次提交都有所有文件的完整快照(这是提交中的主要数据)以及一些元数据:有关提交本身的信息,例如谁进行了提交,何时(日期和时间戳)以及原因(日志消息)。 这些元数据的大部分只是 Git 向您展示的内容git log. 但是 Git 本身需要的元数据中的一个关键信息也在这里。 每个提交都有一个其提交的原始哈希 ID 列表。 大多数提交在此列表中只有一个条目,因为它们具有单个父项。

这个哈希 ID 意味着如果我们以某种方式找到一些起始提交哈希H,我们可以获取提交本身并显示它,并使用它来查找其父(早期)提交。 我们称之为提交G。 我们说提交H点来提交G

... G <-H

但 G 也指出了较早的提交——我们称之为F——如下所示:

... F <-G <-H

当然,F也指向倒退:

... <-F <-G <-H

所以我们真正需要的只是一种告诉 Git 的方法:最后一个提交的哈希 ID 是 _____(用哈希 ID填空)。

这就是分支名称的含义:它提供最后一次提交的哈希 ID。 分支名称指向一个提交,就像每个提交指向一个较早的提交一样。 这样,我们就不必记住人类无法处理的大丑陋哈希ID。 我们只需要记住分支名称。这些名字记住了丑陋的大哈希ID:

... <-F <-G <-H   <-- master

。当我完成[进行新提交]时...

让我们看一下进行新提交的过程。 让我们创建一个新的分支名称,例如feature,现在。 分支名称必须指向某个现有提交- 这是 Git 中的规则:分支名称指向某个提交。 在...--F--G--H系列中,显而易见的是...最后一个:

...--F--G--H   <-- feature (HEAD), master

我们需要一种方法来记住我们使用的分支名称,因此我将特殊名称HEAD附加到新名称feature。 如果我们这样做,这就是我们得到的:

git checkout -b feature master

我们仍在使用提交H,但现在我们on branch feature,正如git status所说。 特殊名称HEAD现在附加到feature,而不是master

当我们进行新的提交时,它会得到一个新的、从未在其他地方使用过、从未在其他地方使用过的、从未在其他地方使用过的提交哈希I。 新提交I指向现有提交H

...--F--G--H   <-- master

I   <-- feature (HEAD)

重复几次,你就会得到这个:

...--F--G--H   <-- master

I--J--K   <-- feature (HEAD)

最终,您完成了提交。 您现在可以git push到某些远程,例如origin。 这种工作方式是,你的 Git 调用另一个 Git(存储在远程名称下的 URL 上的 Git,origin个),并通过哈希 ID 为他们提供一些提交

他们在存储库中查看他们是否具有该哈希 ID。 如果你向他们提供提交K,他们就不会有它。 这迫使你的 Git 也向他们提供提交J,因为JK的父级,这也是 Git 规则的一部分。 他们不会有,所以你的 Git 会提供I,他们不会有,所以你的 Git 会提供H。 在这里,他们很可能有H! 假设他们这样做。 这让你的 Git 停止提供哈希 ID。

现在,您的 Git 必须打包新提交的提交,I-J-K并发送它们。 你将在此处看到有关计数和压缩的消息,然后你的 Git 发送提交。

git push现在进入最后阶段:它向他们发送一个礼貌的请求:如果没问题,请将您的分支名称 ______ 设置为指向提交K只要这会将提交添加到其分支之一,而不从该分支中删除任何提交,他们就可能会遵守此请求。 如果这是一个全新的分支名称,他们更有可能遵守此请求。

最终结果是,现在它们的分支名称指向提交链中的最后一个提交K。 从K,他们会找到J,然后是I,然后是H,等等。这是他们现在在存储库中拥有的历史记录。

你想要什么

。如何从第二个遥控器隐藏第一个遥控器的提交历史记录?

你不能,或者,不完全是。 但是,您可以进行具有不同历史记录的新提交,并改为发送这些提交。

假设您在自己的存储库中,使用以下方法创建一个新的不同的分支名称:

git checkout -b other-guy master

这会在您的 Git 存储库中为您提供以下一系列名称和提交:

...--F--G--H   <-- master, other-guy (HEAD)

I--J--K   <-- feature

您当前的提交现在是提交H。 您当前的分支名称现在为other-guy

您现在可以进行新的提交 - 使用一个全新的、前所未见的哈希 ID,我们称之为L- 其中包含您喜欢的任何快照。 让我们不要担心你是如何做到这一点的,只是画出结果:

L   <-- other-guy (HEAD)
/
...--F--G--H   <-- master

I--J--K   <-- feature

您现在可以使用:

git push other-remote other-guy:feature

这会让您的 Git 调用存储在远程名称下的 Gitother-remote并为他们提供提交L。 他们不会拥有它,所以你的 Git 也会提供提交H。 他们可能有那个 - 它已经存在了一段时间 - 所以你的 Git 可能会停在那里,捆绑L,然后发送它。

现在,您的 Git 向他们的 Git 发送了一个礼貌的表单请求:如果没问题,请在指向提交Lfeature设置或创建您的名称。如果他们接受,他们仓库中的内容是:

...--H--L   <-- feature

(他们可能还有其他名字,比如他们的master,指向H,我们只是没有在这里画)。 因此,它们在存储库中的提交是通过从它们的名称feature开始找到的,该名称标识提交L。 他们将显示提交L。 然后它们将移回L的父H,并显示H,依此类推。

请注意他们如何从不显示I-J-K. 他们不能,因为他们没有。如果他们想要并有权访问,他们现在可以现在或将来从您和/或您发送给它们的任何其他 Git 中获取它们,或者与您发送给他们的 Git 有 Git 性关系的任何 Git,从而拾取它们,等等;但是现在,他们没有感染提交I-J-K

(一般来说,Gits 真的很喜欢接受新的提交。 一般来说,Git 不喜欢放弃提交。 像感染一样传播提交非常容易。

使提交L的简单方法

我答应教你如何做你想做的事。 在I-J-K之后,有一种简单的方法可以使提交L,那就是使用git merge --squash

鉴于此:

...--F--G--H   <-- master, other-guy (HEAD)

I--J--K   <-- feature

您可以运行git merge --squash feature,然后git commit.git merge --squash告诉 Git:为真正的合并做所有你想做的事情,但随后停止而不提交。 当我进行提交时,将其作为常规的日常单父提交,而不是与其两个父级的合并提交。

Git 现在结合了从提交H到提交H的差异(完全没有变化)以及从HK的差异,并将所有这些更改应用于H中的快照,从而在K中生成快照。 此快照尚未提交,但您运行git commit,根据需要填写提交消息,现在是:

L   <-- other-guy (HEAD)
/
...--F--G--H   <-- master

I--J--K   <-- feature

并且您已准备好git push提交L并让其他人将其称为feature

为什么你可能不应该这样做

一旦你完成了一次,你在你自己的仓库中的下一个起始位置是这样的:

L   <-- other-guy (HEAD)
/
...--F--G--H   <-- master

I--J--K   <-- feature

您的两个遥控器之一具有相同的设置,只是它完全缺少提交L。 如果你想在那里发送提交L,这次你需要使用feature以外的名称:他们的名字feature记住提交K。 你可以告诉他们强行放弃I-J-K以支持L,但如果你这样做,你已经放弃了你要求做的事情:现在其他两个遥控器只能找到提交L(至少,通过他们的名字feature)。

如果你想开发更多的东西,你现在有一个问题:你是从提交K开始,还是从提交L开始? 如果你从L开始,你自己做的新工作的历史,没有I-J-K的历史。 毕竟,历史记录是由某个名称发现并向后工作的一组提交

因此,您最终要做的是以下两件事之一:

  • 创建许多您忽略的历史记录(您自己的feature分支,在这种情况下,您可以通过从提交L开始下一个分支来放弃这些历史记录),或者
  • 当你做git merge --squash时,开始不得不做git merge --squash

让我们看看后者是如何工作的:

git checkout feature

现在的结果是:

L   <-- other-guy
/
...--F--G--H   <-- master

I--J--K   <-- feature (HEAD)

我们做出更多承诺:

L   <-- other-guy
/
...--F--G--H   <-- master

I--J--K--M--N   <-- feature (HEAD)

现在我们去挤压合并:

git checkout other-guy
git merge --squash feature

这会合并工作 — 例如,比较HL以查找"我们的"更改,HN以查找"他们的"更改,并合并更改。 这通常工作正常...但是,如果我们在M-N中所做的任何事情撤消了I-J-K中的某些操作,或者与我们在I-J-K中所做的内容相同,则会出现合并冲突

有很多方法可以解决这个问题,最后我们得到:

L--O   <-- other-guy
/
...--F--G--H   <-- master

I--J--K--M--N   <-- feature (HEAD)

其中O具有将MN相结合的挤压结果. 您现在可以将两个不同的历史git push到两个不同的地方。

这真的可以工作。 随着时间的推移,它只会变得痛苦。 还有另一个"类似感染"的问题:你已经将提交I-J-K-M-N发送到其他一些 Git。 这些提交很有可能会传播到更多的 Git 克隆中,然后从那里到达您试图保持这些秘密的克隆。 即使这没有发生,通过做git push other-guy feature,你自己也很容易搞砸(尽管幸运的是,在第一轮之后,这通常会被拒绝,因为"不是快进"错误)。

简而言之,秘密 - 隐藏的提交 - 通常不会持续一次共享。 通常有一个更简单的选择。 不过,我不知道你想要这一切的动机,所以很难确定。

相关内容

  • 没有找到相关文章

最新更新