微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

{git 44}中的“他们”和“我们”是谁? 合并案例因此,合并或合并为动词会在Git索引中起作用樱桃选择和还原使用合并机制 Rebase反复进行摘樱桃

如何解决{git 44}中的“他们”和“我们”是谁? 合并案例因此,合并或合并为动词会在Git索引中起作用樱桃选择和还原使用合并机制 Rebase反复进行摘樱桃

us期间,我无法理解themgit revert是谁在这些冲突中,所以我真的不知道这里发生了什么:

git revert some_commit_hash

然后git status显示以下冲突:

deleted by them: path/to/file1.h
both modified:   path/to/file2.h
deleted by them: path/to/file1.cpp
deleted by them: path/to/test_file1.cpp
added by us:     path/to/file3.h
deleted by them: path/to/file4.h
added by us:     path/to/file5.h

谁是“我们”?谁是“他们”?

更新:请注意,我要还原的提交是一个非常大的合并提交。


不重复:

  1. 因为它无法阐明谁是usthemGIT: How dangerous is "deleted by us" conflict?
  2. 因为它涵盖了mergerebase但没有涵盖revert,而git经常使用相同的术语来表示相反的内容,具体取决于操作:{{ 3}}
  3. 因为它没有提及“我们”和“他们”-Who is "us" and who is "them" according to Git?

解决方法

发生冲突时,适用于所有情况的规则是:

  • ours / us是当前HEAD(活动提交)的状态
  • theirs / them是另一端的状态(正在合并的提交,被挑选/重新设置的提交,或者在您的情况下是您想要的提交的“反向”还原)

对于rebase(回答@GabrielStaples的评论)有一些额外的说明:

如果您在my/branch上,并且运行git rebase other/branch,则git将检出other/branch的头提交,并开始在顶部重播一些提交。

如果发生冲突,由于检出的提交来自other/branch,因此ours将大致代表other/branch,而theirs将是my/branch。 / p>

这部分与直觉“ ours应该是我的更改”相反,但它符合上面的描述:在发生冲突时,签出的提交为ours,另一端(正在重播的提交)为theirs

,

TLDR;

跳到最底端以获取结果和结论。

详细信息:

关于:

然后git status显示以下冲突:

deleted by them: path/to/file1.h
both modified:   path/to/file2.h
deleted by them: path/to/file1.cpp
deleted by them: path/to/test_file1.cpp
added by us:     path/to/file3.h
deleted by them: path/to/file4.h
added by us:     path/to/file5.h

我做了一些实验,并观察了以下内容。

首先,我正常地手动解决了两个已修改的文件path/to/file2.h中的冲突,这对于任何变基或合并冲突都是正常的。然后,我添加了所有文件并完成了还原:

git add -A
git revert --continue

接下来,我观察到所有标有删除的文件以及所有标有由我们添加的文件在我的文件系统上存在/存在。因此,还原没有删除任何文件。接下来,我想知道:哪些提交创建了这些文件?要查看此内容,请运行以下(source):

git log --diff-filter=A -- path/to/file

这仅显示创建该文件的单个git log commit_hash的{​​{1}}。我一次一次 执行每个被他们删除由我们添加的文件:

commit_hash

我发现,如上所述,其中4个文件是由我还原的提交添加的。 注意,这意味着它们是由提交git log --diff-filter=A -- path/to/file1.h # added by the commit I reverted git log --diff-filter=A -- path/to/file1.cpp # added by the commit I reverted git log --diff-filter=A -- path/to/test_file1.cpp # added by the commit I reverted git log --diff-filter=A -- path/to/file3.h # added by a later commit git log --diff-filter=A -- path/to/file4.h # added by the commit I reverted git log --diff-filter=A -- path/to/file5.h # added by a later commit 本身添加的,而不是由我运行some_commit_hash时创建的还原提交添加的。那么,如果我仍然将它们存在,为什么恢复该提交?好吧,事实证明,后来发生的提交(我们称为git revert some_commit_hash,发生在later_commit_hash之后,触及了所有6个文件,修改了其中4个并创建了2个。

将上述文件按删除的文件与我们添加的 的分组:

some_commit_hash

现在指示哪个文件是通过哪个提交添加的:

# deleted by them:
path/to/file1.h
path/to/file1.cpp
path/to/test_file1.cpp
path/to/file4.h

# added by us:
path/to/file3.h
path/to/file5.h

因此,您可以看到由他们删除的文件是我还原的提交中添加的,这意味着还原该提交将删除这些文件!因此,# deleted by them / added by the commit I reverted (`some_commit_hash`) path/to/file1.h path/to/file1.cpp path/to/test_file1.cpp path/to/file4.h # added by us / added by a later commit (`later_commit_hash`) path/to/file3.h path/to/file5.h 指的是要还原的提交them,而some_commit_hash指的是us处的其余提交。

冲突是HEAD触摸了这4个“被他们删除”的文件,因此不允许later_commit_hash删除它们。并且,这2个“由我们添加”文件在git revert some_commit_hash之前不存在,因此冲突在于它们在还原后不应该存在,但它们确实存在,因为它们是由some_commit_hash创建的

我解决的方法是手动删除所有这6个文件:

later_commit_hash

然后我将此更改作为新的提交提交>

rm path/to/file1.h
rm path/to/file1.cpp
rm path/to/test_file1.cpp
rm path/to/file3.h
rm path/to/file4.h
rm path/to/file5.h

但是,我可以将其重置回还原提交之前的位置,然后先还原git add -A git commit ,然后再还原later_commit_hash,然后有效地将这些更改回滚像这样:

some_commit_hash

结果和结论:

无论哪种情况,都要回答我自己的问题:

git reset --hard HEAD~ # WARNING! DESTRUCTIVE COMMAND! BE CAREFUL. git revert later_commit_hash git revert some_commit_hash # should result in no conflicts during both of those reverts now 期间:

  1. “我们” =键入并运行git revert some_commit_hash时当前已签出的提交(即:HEAD),并且:
  2. “他们” =您要还原的提交(相反或相反?);即:假设您运行命令git revert some_commit_hash,这是与some_commit_hash相反的临时提交,以撤消some_commit_hash的更改。

我仍在为这个概念而苦苦挣扎,但这就是要点。我认为,还原工作原理的确切机制将在这里澄清一些事情。 This answer可能会提供更多的见解,但我不理解。

我也刚刚在此处添加了一个答案,以澄清我想到的所有4个git操作的“我们”和“他们” git revert some_commit_hash,{ {1}},git mergegit cherry-pickWho is "us" and who is "them" according to Git?


(自我注释):

需要看看:http://ezconflict.com/en/conflictsse12.html#x53-890001.7

,

嗯... revert是一种非常特殊的情况。那么,考虑一下普通的合并,与共同的祖先和所有东西,整个包,对吧?现在,整个工作就像合并 except 一样(而且很大),合并引擎强制 the common ancestor是您要尝试的修订进行还原,并且the other branch是该修订版的父级

,

尽管已经很好地回答了这个问题,但是还有另一种方法可以查看全部内容。 Git本身就是这样看待它的。樱桃提取,合并,变基和还原这四个操作都使用相同的机器,--ours--theirs标志分别为git checkout-X ours和{ {1}}扩展选项,使用相同的内部代码引用相同的内容。我喜欢将这种机制称为作为动词进行合并,因为当合并必须进行真正的合并时,我们首先通过-X theirs对其进行了介绍。

合并案例

进行真正的合并时,这些术语才有意义。我们从可以用这种方式说明的内容开始:

git merge

在这里,名称 I--J <-- ourbranch (HEAD) / ...--G--H \ K--L <-- theirbranch 选择提交ourbranch,这是我们在分支上的提交(在这种情况下,这是两个这样的提交之一,尽管专门在我们自己的分支上的提交数量需要至少为 1才能强制进行真正的合并)。名称J选择提交theirbranch,这就是他们在其分支上的提交(还是两个之一,这里至少需要一个提交)。

Git为了进行合并(将其作为动词进行合并一些文件集)所做的工作是,对于所有三个提交文件LH中的每个文件,和J,将L中的文件与H中的文件进行比较,以查看我们发生了什么变化,并将J中的文件与在H中查看他们发生了什么变化。然后,Git将这两组更改组合在一起,并将组合的更改应用于L中的所有内容。

提交H合并基础提交,提交H是“我们的”提交,提交J是“他们的”提交。任何差异,无论是“由我们添加”的新文件,还是“由他们删除”的文件,或其他有关提交L的东西。

为了通过合并机制运行合并,Git进行了以下内容的略微优化:

  1. 设置:
  • 将合并基本提交(H)读入插槽1的索引中
  • H提交(ours = HEAD)读入插槽2的索引中
  • J提交(theirs)读入插槽3的索引中
  1. 识别“相同文件”。请注意,每个文件都重复执行步骤2和3。
  • 如果所有三个插槽中都有一个名为 F 的文件,则该文件是同一文件
  • 否则,如果插槽1中有任何内容,请尝试猜测重命名,该重命名会将插槽1中的合并基础文件与我们或他们的具有不同名称​​ 的文件绑定,该文件位于插槽2中, /插槽3;如果找不到可用来重命名的文件,则我们和/或他们一方删除了该文件;这些情况也可能导致高层冲突,例如重命名/修改或重命名/删除,我们声明冲突并继续执行而不执行步骤3
  • 否则(插槽1中什么都没有,但是插槽2和3中什么都没有)我们有一个添加/添加冲突:声明此特定文件有冲突,然后继续执行而不执行步骤3
  1. 短路简单情况,并通过低级别合并来进行困难情况:
  • 如果插槽1、2和3中的Blob哈希ID都匹配,则所有三个副本都相同;使用其中任何一个
  • 如果插槽1中的blob哈希ID与2或3中的blob哈希ID相匹配,则有人没有更改文件,有人更改了文件;使用更改后的文件,即采用不同
  • 的文件
  • 否则,所有三个插槽都不同:通过更改块,低级合并来执行更改的行块
    • 如果在低级别合并期间存在合并冲突,则L-X ours的意思是“使用我们的解决冲突”,其中我们的冲突在插槽2中,而他们的冲突在插槽2中3
    • 请注意,这意味着只要没有冲突,例如,只有一个“边”更改了第42行,-X theirs扩展选项根本就不适用,因此我们进行了修改,无论是我们的还是他们的

在此过程结束时,所有完全解析的文件都将移回到其正常的零插槽位置,同时删除插槽1、2和3条目。任何未解析的文件都将保留所有三个索引槽(在删除冲突和添加/添加冲突中,一些槽为空,但正在使用 some 非零阶段编号槽,这会将文件标记为已冲突) )。

因此,合并合并为动词会在Git索引中起作用

以上所有操作均发生在Git的索引中,其副作用是将更新的文件保留在工作树中。如果存在低级冲突,则会在工作树文件上标记冲突标记,并在与索引插槽1(合并基准),2(我们的)或3(他们)。

最终,它总是归结为相同的等式:1 =合并基准,2 =我们的基准,3 =他们的基准。即使加载索引的命令不是-X,也是如此。

樱桃选择和还原使用合并机制

运行git merge时,我们将看到一个提交图,如下所示:

git cherry-pick

此处的字母...--P--C--... \ ...--H <-- somebranch (HEAD) P代表任何一对父子提交。只要我们使用C选项指定要使用的父对象C甚至可以是合并提交。 (对这三个提交在图中的位置没有真正的限制:我用-m绘制了它,它是H之前的某个提交的子对象,但也可以在{{1}之后}对,例如P,或者,如果您有多个不相交的子图,则P-C...-E-P-C-F-G-H提交之间可能根本没有关系。)

当我们跑步时:

P-C

Git将使用从H回到git cherry-pick <hash-of-C> 的父链接自行定位提交PC现在充当合并基础,并且被读入索引槽1。P充当P提交,并被读取到索引槽3。我们当前的提交{{1} }是C提交,并被读入索引槽2。合并机制现在运行,因此“我们的”提交是--theirs,“他们的”提交是提交H,其中合并基础-如果将--ours设置为HEAD,或者如果我们使用C运行合并工具,则会显示为提交merge.conflictStyle

当我们跑步时:

diff3

发生相同的事情,除了这次,commit git mergetool是插槽1中的合并基础,commit P是插槽3中的git revert <hash-of-C> 提交。{{1} }像往常一样来自C的插槽2中的提交。

请注意,如果您使用cherry-pick或还原一系列提交:

P

优先选择使用拓扑较旧的提交一次进行一次提交,而选择恢复的操作首先使用较新的拓扑提交一次进行操作。也就是说,给定:

--theirs

--ours首先复制HEAD,然后复制git cherry-pick stop..start ,但是...--C--D--E--... \ H <-- HEAD 首先复制git cherry-pick C..E,然后复制D。 (提交E不会起作用,因为两点语法会排除两点表达式左侧可到达的提交。有关更多信息,请参见the gitrevisions documentation。)

Rebase反复进行摘樱桃

rebase命令的工作方式是反复运行git revert C..E,然后在之后使用ED进入分离HEAD 模式。 (从技术上讲,它现在仅在内部执行;在过去,某些基于外壳脚本的C确实使用了git cherry-pick,尽管其哈希ID始终始终处于分离模式。)

当我们运行git checkout --detach时,我们从以下内容开始:

git switch --detach

我们运行:

git rebase

首先要做的是 second ,这是进入分离的HEAD模式,HEAD提交是我们用git checkout参数选择的提交。如果我们没有使用单独的git rebase标志和参数,则 C--D--E <-- ourbranch (HEAD) / ...--B--F--G--H <-- theirbranch 来自我们给出的一个参数,在这种情况下为git checkout ourbranch # if needed - the above says we already did that git rebase theirbranch # or,git rebase --onto <target> <upstream> 。如果我们不使用单独的 --onto 参数,那么我们给出的一个参数(在本例中为--onto)将同时用于两种目的。

Git也(首先,这就是上面第二个原因)列出了要复制的每个提交的原始哈希ID。这个列表比乍一看看起来要复杂得多,但是如果我们忽略了额外的复杂性,基本上是以下结果:

--onto

,在这种情况下,它们是提交theirbranchupstreamtheirbranch的哈希ID:git rev-list --topo-order --reverse <hash-of-upstream>..HEAD 可以访问的三个提交,而C不能访问的三个提交D

E生成了此列表并进入了HEAD分离模式,我们现在看起来像这样:

ourbranch

现在,Git运行一个theirbranch。它的参数是提交git rebase的哈希ID,这是要复制的第一个提交。如果我们在上面查看cherry-pick的工作方式,我们将看到这是一个谓词合并操作,合并基础是 C--D--E <-- ourbranch / ...--B--F--G--H <-- theirbranch,HEAD 的父级,即,提交git cherry-pick,当前或C提交是提交C,而待复制或B提交是提交--ours。因此,这就是我们和他们的似乎相反的原因。

一旦完成摘樱桃操作,我们现在就有:

H

Git现在继续使用--theirs复制提交C。现在,合并基础为提交 C--D--E <-- ourbranch / ...--B--F--G--H <-- theirbranch \ C' <-- HEAD D提交为提交git cherry-pick,而C提交为--ours。这意味着我们的和他们的提交都是我们的,但是这次“我们的”提交是我们几秒钟(或毫秒)之前构建的!

它基于他们现有的提交C',但它是我们自己的提交--theirs。如果我们遇到任何合并冲突,那么毫无疑问,它们是基于D的结果,也许包括我们为制作H手动执行的某种冲突解决方案。但是,从字面上看,所有三个输入提交都是我们的。索引插槽1来自提交C',索引插槽2来自提交H,索引插槽3来自提交C'

完成所有操作后,现在的图片如下:

C

Git现在在提交C'的哈希值上运行D。合并基础为提交 C--D--E <-- ourbranch / ...--B--F--G--H <-- theirbranch \ C'-D' <-- HEAD ,而我们的提交和他们的提交分别为git cherry-pickE。因此,再次说明,在重新设置期间,所有三个提交都是我们的—尽管合并冲突可能是基于D构建的结果。

完成最后一个选择后,Git通过将 name D'从旧提交E中移出并将其粘贴到新提交{{1 }}:

H

我们现在恢复到正常的附加工作方式,因为ourbranch从我们现在的位置开始(在提交E处),并且向后工作,因此从不访问原始提交{{1 }},似乎我们已经以某种方式修改了原始的三个提交。我们还没有:它们仍然在我们的存储库中,可以通过特殊的伪引用E'获得,也可以通过我们的引用日志获得。默认情况下,我们至少可以将它们退回30天,然后 C--D--E [abandoned] / ...--B--F--G--H <-- theirbranch \ C'-D'-E' <-- ourbranch (HEAD) 可以随时收割它们,然后然后真正消失了。 (好吧,只要我们不将它们git log保留在仍保留它们的某些 other Git存储库中即可。)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。