回购有多个未关联的提交,没有共享历史记录

时间:2018-08-16 17:14:20

标签: git

我正在开发一个由单独开发者运行的项目,他习惯于在准备发布新版本时将整个工作目录放大。我相信他有多个仓库,他正在工作并分支到master(github,bitbucket等),因此提交历史记录显示了很多提交链,而它们之间没有链接。

我正在尝试调试,并且如果不手动指定提交哈希值就无法在这些独立链之间进行直接区别,这非常麻烦。我想将这些分支/标签链接在一起,但是我不确定最好的方法。其中一些拆分是在几周后进行的,我想将合并保留在它们所属的提交历史记录中。

*     v2 release
|
*     commit
|
*     fresh/rebased

*     v1 merge/release
| \
| *   commit
| |
| *   commit
| 
*     fresh

我只希望能够签出当前的master并能够查看提交历史记录并查看在何处进行了某些更改,所以我想我需要将我们的策略从v2分支合并到v1并手动设置时间戳?

此处类似的问题,没有答案:Connecting git branches with no shared history on Github

1 个答案:

答案 0 :(得分:0)

这里没有真正的正确答案,但是有两个或三个选项。大多数以git replace开头:使用git replace --graft将历史记录嫁接在一起。但这有一些缺点,那就是选项的所在。

根本问题是这些提交已经存在并且不能更改。那永远不会消失,但是您可以使用不同提交。棘手的部分是决定如何做到这一点,以及是否让所有人切换。

有一种最好的入门方法(无论如何,我认为),就是使用git replace。这意味着您需要知道git replace的实际功能。从这个概念开始: Git中的每个对象都由一个哈希ID表示。

哈希ID和提交图

您已经对这些哈希ID有所了解,因为您现在正在使用它们来完成git diff。我们还拥有一个事实,即分支和标记名称仅存储一个单一的哈希ID。对于分支名称,该哈希ID是Git应该将其视为分支的 tip 的提交。

每个提交本身(通过某个哈希ID找到)都包含另一个哈希ID-嗯,通常是一个,但有时更多,很少一个都没有。例如:

$ git cat-file -p HEAD | sed 's/@/ /'
tree 1fd4a47af4942cbdee0bdcb4375612ab521a4a51
parent 5571d085b3c9c2aa9470a10bcf2b8518d3e4ec99
author Junio C Hamano <gitster pobox.com> 1531941857 -0700
committer Junio C Hamano <gitster pobox.com> 1531941857 -0700

Third batch for 2.19 cycle

Signed-off-by: Junio C Hamano <gitster pobox.com>

此提交-b7bd9486b055c3f967a870311e704e3bb0654e4f-作为其父提交5571d085b3c9c2aa9470a10bcf2b8518d3e4ec99。我们说分支名称​​指向提示提交,而提示提交指向其父级:

... <-grandparent <-parent <-tip   <--branch

,只要连接是线性的,我们就会得到一个很好的简单提交历史。如果历史记录中有分支,我们有两个提示,两个名字:

A--B--C   <-- master (HEAD)
    \
     D--E   <-- develop

CD都指向B

提交A root提交:,它没有父项。在典型的存储库中,您只有一个root提交,但是要有人创建多个root提交并不太困难-正如您所发现的!

当Git遍历历史记录时,您告诉它从某个提交开始(通常是分支提示),然后Git沿着所有链条向后工作。 merge 提交将两个分支合并在一起,因此,当向后向后前进时,它将导致Git分支其分支!也就是说,如果我们将develop合并回master

A--B--C---F   <-- master (HEAD)
    \    /
     D--E   <-- develop

,然后让Git从提交F开始并向后工作,Git依次为:F,(CED ),BA。到达没有父母的A时,Git停止了。

(更确切地说,git log具有要显示的提交的优先级队列。Gi​​t首先将您命名的任何提交或提交放在命令行上,或者HEAD如果您没有命名,则进入队列,然后git log从队列中取出一个提交,如果应该显示,则显示它,然后Git将那个提交的父母放入队列中。队列是空的。提交的访问顺序取决于日志选项,还有一个选项可以禁止除父级插入之外的所有插入。)

替换

以上所有内容(实际上也是其他所有内容)也都通过哈希ID起作用:Git首先以master表示提交F的哈希ID或其他任何含义的概念开始,该哈希ID进入要查看的事物列表。然后,Git从数据库中获取该对象,这就是替换的地方。

对象本身无法更改,因此,提交A只是提交A,但是可以说,我们可以让Git“避开眼睛”。如果由于某种原因而不喜欢提交A(例如,它应该有一个父提交),我们可以为其进行替换并告诉Git :

  

每当您要查看提交A时,都应查看A'

我们使用git replace进行此操作。 --graft选项告诉git replace复制原始A但放入一些不同的父项中,因此如果有:

A--B--C   <-- new

D--E--F   <-- old

A确实应该加入F,我们可以创建一个新的A',否则就像 A,但是链接到F

        A--B--C   <--new

        A'
       /
D--E--F   <-- old

,现在git logC扫描到B,进行替换 A',进行扫描F。现在运行git log可以使看起来像历史D-E-F-A'-B-C

隐藏替代物

有时您可能想查看原始的,未替换的链条。使用git --no-replace-objects log,您将看到图表中实际的内容而无需替换。

替代品的主要缺陷

使用git replace的主要问题是,默认情况下未克隆这些替换对象。带有替换A'的存储库的新克隆没有A'。要克隆替换项,您可以在fetch配置中添加指令。

可以使用git filter-branch通过重新复制历史记录来使替换永久化。这一步有点激烈,因为这意味着每个人都必须从旧存储库切换到新存储库。

或者,您可以合并不相关的历史记录

如果您不介意奇数图构造和棘手的差异,则无需拥有来重写或替换历史记录。您可以使用git merge -s ours简单地添加忽略其他输入的合并。例如,给定:

A--B--C   <-- new

 D--E--F   <-- old

您可以运行git checkout new; git merge -s ours old来获取:

A--B--C--G   <-- new
        /
 D--E--F   <-- old

G的两个父级分别为CF,但是G中的快照与C中的快照完全相同。这只是添加到现有存储库中,因此没有替换和/或分支过滤的缺点。问题是提交A-B-CD-E-F仍然没有适当的关联。如果没什么大不了的,那可能是您最好的方法。

相关问题