我正在开发一个由单独开发者运行的项目,他习惯于在准备发布新版本时将整个工作目录放大。我相信他有多个仓库,他正在工作并分支到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
答案 0 :(得分:0)
这里没有真正的正确答案,但是有两个或三个选项。大多数以git replace
开头:使用git replace --graft
将历史记录嫁接在一起。但这有一些缺点,那就是选项的所在。
根本问题是这些提交已经存在并且不能更改。那永远不会消失,但是您可以使用不同提交。棘手的部分是决定如何做到这一点,以及是否让所有人切换。
有一种最好的入门方法(无论如何,我认为),就是使用git replace
。这意味着您需要知道git replace
的实际功能。从这个概念开始: Git中的每个对象都由一个哈希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
C
和D
都指向B
。
提交A
是 root提交:,它没有父项。在典型的存储库中,您只有一个root提交,但是要有人创建多个root提交并不太困难-正如您所发现的!
当Git遍历历史记录时,您告诉它从某个提交开始(通常是分支提示),然后Git沿着所有链条向后工作。 merge 提交将两个分支合并在一起,因此,当向后向后前进时,它将导致Git分支其分支!也就是说,如果我们将develop
合并回master
:
A--B--C---F <-- master (HEAD)
\ /
D--E <-- develop
,然后让Git从提交F
开始并向后工作,Git依次为:F
,(C
,E
和D
),B
和A
。到达没有父母的A
时,Git停止了。
(更确切地说,git log
具有要显示的提交的优先级队列。Git首先将您命名的任何提交或提交放在命令行上,或者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 log
从C
扫描到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
的两个父级分别为C
和F
,但是G
中的快照与C
中的快照完全相同。这只是添加到现有存储库中,因此没有替换和/或分支过滤的缺点。问题是提交A-B-C
与D-E-F
仍然没有适当的关联。如果没什么大不了的,那可能是您最好的方法。