Git哲学问题:“祖先”和“历史”(a.k.a.年代学)是不同的东西?...
看哪,我有以下git存储库:
A---B---C develop
/
D---E---F---G master
我希望主人能够准确地包含发展的主要内容:
A---B---C develop
/ \
D---E---F---G---C master
除 外,我不想合并。我希望它像 this :
A---B---C develop
/ \
D---E---F---G===C--- master
主人的头部不会有多个祖先,它将成为开发者的头脑。我的图表中的===
表示按时间顺序连接,但不是祖先。
为了澄清,我不想使用git merge -X theirs
或git merge -r ours
因为那些仍然是真正的合并......我只是不想合并:我想要一个“粘贴”。但是,我还需要能够查看master上的git log并查看commit C,然后提交G,然后提交F等,即使C和G之间没有父系关系。另外,我的约束禁止我从擦除大师或发展,或搞乱他们的历史。有一种简单的方法可以做到这一点吗?
答案 0 :(得分:3)
你可以实现这样的目标,但不是Git如何运作,你不应该这样做。
您可以使用git reset
移动master
并拖动当前状态develop
。您需要将master
分支从其当前提交G
移动到所需的提交C
,然后将软重置master
恢复为其原始提交,{{1用它拖动G
的当前状态。然后,您可以提交这些更改,使用C
上存在的更改创建新提交(不是C
)。
develop
此时,git checkout master
git reset --hard develop
git reset <commit id of C> # or git reset HEAD@{1}
# the working directory now contains the difference between C and G
git commit -m 'a message for C'
和master
将包含相同的内容,但具有不同的提交历史记录。提交图将如下所示:
develop
A---B---C develop
/
D---E---F---G---C' master
和C'
包含相同的内容但有不同的历史记录。
您可以先进行合并,然后完全按上述步骤执行,并创建一个新的提交,其中包含C
和master
作为祖先,但内容为develop
:
git checkout master git merge develop git reset - hard develop git reset HEAD @ {1} git commit -m&#39; C&#39;的提交消息。
这将为您提供以下内容,其中C
是合并提交:
M
但同样,这并不是Git的真正意义,如果您打算放弃一个分支或另一个分支的所有更改,则没有理由这样做。
答案 1 :(得分:2)
编辑:让我先从哲学问题开始(我之前错过了)。
Git哲学问题:是&#34;祖先&#34;和&#34;历史&#34; (a.k.a.年代学)不同的东西?...
不是真的。好吧,也许:&#34;祖先&#34;和&#34;历史&#34;是相同的,或者至少是深刻的联系。年表... 经常无关紧要。 Git存储提交,提交是或至少形成历史。
每个提交都有一些父级:通常只有一个父级,但是合并提交有两个或更多,并且存储库中至少有一个提交 - 名为 root commit-has no 父母。通常,您通过选择现有提交,选择或创建树(快照)以进入新提交并使用以下命令写出新提交来进行新提交:
一旦完成,提交将永久冻结:它实际上不能被更改,因为它的哈希ID - 其真正的名称&#34;就Git如何在数据库中找到它而言,它是其内容的加密校验和。以任何方式更改内容,甚至只更改一位,并获得不同的校验和,这意味着新的和不同的提交(同时旧的提交保留在存储库中)。
当您在不启动当前提交的情况下进行提交时,会发生 root 提交的特殊情况。这显然必须是新的空存储库中第一次提交的情况:没有先前的提交,因此第一次提交不能拥有父级。之后,所有正常提交都有一些现有提交作为其父提交,尽管可以创建新的提交。
结果是将字符串提交到一起,在时间和#34;中向后指向,而不管提交中存储的任何时间戳。我们可以像这样绘制一个小的三个提交存储库,其中单个大写字母代表实际的提交哈希值:
A <-B <-C
提交C
是最新的(即使它的时间戳是在20世纪70年代制作的);提交B
是C
的父级;并且root commit A
是B
的父级。这些事实嵌入在每个提交中,它是只读的和不可破坏的。
Git 找到 C
的方式是通过分支名称,例如master
。 C
有一些哈希ID - C
内容的加密校验和 - 而Git将该哈希ID放在键值存储中,键名为master
。因此,给定master
,Git找到C
,Git用它来查找B
,依此类推。要将新提交放到master
分支上,我们现在将编写一些新内容,运行git add
和git commit
,并使用新快照获取新提交D
,其父级是提交C
。无论D
的哈希ID是什么,Git都会将其写入master
,现在master
将指向新提交D
。
历史和祖先是如此深深地交织在一起的。我们可以在任何时候给出所有起始点名称(分支,标签和任何其他名称),通过这些链接查找存储库中的所有提交。绘制链接会生成提交图。
git log
命令不一定总是按照它们在此图中出现的顺序显示提交。例如,特别是如果git log
有,则在其#34;承诺中显示&#34; queue,两个或更多提交,它必须先选择一个才能显示。它选择的是基于您在git log
命令行上指定的排序条件。但是,如果只有一个提交要显示,则显示一个提交。显示一个提交,git log
通常会将提交的父母(所有这些)添加到队列中,除非它们已经显示。
git log
的默认设置是通过将(单个)提交放入队列来向您显示HEAD
提交。现在只有一个提交在队列中,所以Git删除它并显示它。如果这是普通的单父提交,您将看到它,然后git log
将其(单个)父级放入现在为空的队列中。然后Git会向您显示父级:无论是否提交任何提交时间戳,您都会按照图形顺序查看提交。
由于合并提交根据定义至少有两个父项,因此显示合并提交的行为通常会将两个尚未见过的提交丢弃到要显示的提交队列中。此时,时间戳可能(默认情况下,确实如此)进入图片。如果您没有指定任何特定的排序标准,默认情况下Git使用的是:首先显示提交时间戳更高的提交。所以现在年表 - 具有存储在提交中的时间戳的含义,这可能与实际提交时没有任何关系 - 具有一定的效果。
(请注意,您可以通过更改计算机的时钟来调整任何新提交中的一个或两个时间戳,或通过设置GIT_AUTHOR_DATE
和/或更轻松,更可重复运行GIT_COMMITTER_DATE
时的git commit
个环境变量。某些命令(包括git commit
本身)也会带有--author-date
个标记等。)
有几个人告诉过你,你无法在Git中得到你想要的东西。
此外,这张图是荒谬的:
A---B---C develop / \ D---E---F---G---C master
因为它包含两个标记为C
的不同提交。您无法更改现有提交,根据定义,您所做的任何新提交都将具有新的不同哈希ID。
这张图也是如此,出于同样的原因:
A---B---C develop / \ D---E---F---G===C--- master
但是,如果我们用额外的C
替换一个完全不同的提交 - 具有不同的哈希ID - 那么我们可以得到前一个图,现在看起来像: / p>
A---B---C <-- develop
/ \
D---E---F---G---H <-- master
即名称master
选择提交H
,其历史记录包括所有提交,而名称develop
选择现有提交C
,其历史记录可追溯到B
至A
,然后返回E
,依此类推。或者,如果您愿意,我们可以获得图表:
A---B---C <-- develop
/
D---E---F---G---H <-- master
与提交H
相关联的快照可以是您想要的任何内容。 (要完全手动创建此类快照,您可以在工作树中工作,运行git add
以通过当前索引中的副本复制更新的工作树文件,然后编写git write-tree
以写出索引以形成H
快照的树 - 但您似乎不需要这样。)
如果您希望提交H
与提交C
具有相同的快照,那么git diff develop master
根本不产生任何输出,这非常简单:我们可以使用Git& #39;所谓的 plumbing 命令,用于从树中为提交H
创建提交C
。最后有三个步骤:
vim /tmp/msg
# write appropriate log message to file /tmp/msg
接下来,创建新的提交H
并将其哈希ID保存在某处。 (我在这里使用一个变量。例如,如果你愿意的话,你可以用git log --graph $h
检查它,然后再实际提交在任何地方使用它。)
h=$(git commit-tree -p master -p develop -F /tmp/msg develop^{tree})
这将按顺序创建两个父项master
和develop
(即G
和C
)的提交。如果您只想要一个父母,请留出一个-p
和参数。
最后,如果一切看起来都不错,并且您已经master
并且希望H
成为提示提交,请重置或快进合并到H
:< / p>
git checkout master # if necessary
git merge --ff-only $h
这会更新当前分支名称 - 即master-to指向commit $h
(即新提交H
),并相应地更新索引和工作树,以便commit H
现在占用了通常的分段和工作空间。
(顺便说一句,如果您确实使用两个父项进行最终提交H
,并使第一个父项提交G
,第二个提交C
但使用提交C
的内容,相当于git merge -s theirs
,如果它存在的话。它不会,但你可以通过多种方式合成它,包括上面的那些。另见VonC&#39 ;回答here和一些相关链接。)
答案 2 :(得分:0)
我不想要合并:我想要一个“粘贴”。但是,我还需要能够查看master上的git log并查看commit C,然后提交G,然后提交F等,即使C和G之间没有父系关系。
这是自相矛盾的。你已经切断了祖先的链接。你完全抛弃了历史。
Git checkout -B master develop
是您放弃现有master
历史记录的方式,现在master
会引用当前的develop
历史记录。
如果你想在master
的新历史中看到旧的遗弃历史,那么你可以
git merge -s ours master@{1}
记录祖先而不采取任何内容,但你要么记录了祖先,要么你没有记录。你不能切断你的祖先链接并拥有它们。