当我的一个分支删除了我想保持删除的文件时,将 2 个分支合并到 master 中的最佳方法是什么



我是 git 的新手,想了解在以下情况下会发生什么。 我有以下分支:

  1. 主分支(当前部署到生产)
  2. 增强功能#1 - 清理未跟踪文件分支:这是从Master生成的。我对 gitignore 文件进行了更新,以在我的存储库中不包含特定的扩展名,并从存储库中删除了那些不必要的文件。
  3. 增强功能#2:这也是从Master创建的。代码更新支持客户请求。

在这一点上,我想将增强功能#1和#2都投入生产。在与 Master 合并之前将这两个分支合并在一起的最佳方法是吗? 如果是这种情况,Git 怎么知道我希望从增强 #1 中删除的文件保持删除状态?增强功能 2 到增强功能 1 的合并请求是否会再次引入这些旧文件,因为增强功能 #2 是从 Master 分支创建的?

提前感谢您的帮助。

我怀疑您的问题有多种解决方案。这是我的建议:

  1. 创建主副本

git checkout master && git checkout -b copyOfMaster

  1. 将您的分支合并到 copyOfMaster 中(我会根据您所说的做 E#1 然后是 E#2)。观察合并的输出以了解保留了哪些文件。

Git 合并策略(您可能需要递归 + 我们的组合):https://git-scm.com/docs/git-merge#_merge_strategies

这一步的美妙之处在于,如果出现问题,由于您正在处理 Master 的副本,因此您可以删除该分支并尝试其他操作。

git merge e1 -s recursive -X theirs && git status

git merge e2 && git status

  1. 测试你的主分支,如果一切看起来都不错,并且所有文件都在那里或删除了需要的文件,然后将copyOfMaster合并到master中。

git checkout master && git merge copyOfMaster -s recursive -X theirs

有两个原因导致思考 Git 做什么很困难:

  1. 其中一些本质上困难的,例如分布式版本控制系统的整个概念(某些存储库的多个独立副本)。 但这不是你在这里遇到的;为此,我们转到:

  2. 当人们开始使用 Git 时,他们认为这是关于分支文件或类似的东西。 其实不然。 Git 是关于提交的。 这包括git merge:它并没有真正合并分支——嗯,有时它会合并,这取决于你个人说"分支"这个词的意思——但它总是适用于提交

(第二点的补充是 Git 中的单词分支是模棱两可的。 看看我们所说的"分支"到底是什么意思?

为了正确思考 Git 在这里会做什么,我们需要从提交的正确定义开始。 由于您已经使用 Git 一段时间了,因此您现在可能已经熟悉了这个序列:

git checkout <something>
<edit some files>
git add <files>
git commit

和:

git log

但是,这一切究竟是做什么的呢?git log命令显示提交 - 带有大丑陋的哈希 ID -git commit命令进行新的提交,但确切地说,什么是提交? 提交包括哪些部分? 我们如何命名一个特定的提交? 一个人为我们做了什么?

提交

我们只需要记住以下一堆事实:

  • 每个提交都有编号。 这些数字不是简单的计数数字——它们不会变成 #1、#2、#3 等——但它们是一种数字,git log显示了它们。 它们实际上是每个提交内容的加密校验和,并且由于每个提交都保证是唯一的,因此每个校验和也是唯一的1。阿拉伯数字

  • 这意味着任何提交的任何部分都无法更改。 如果你取出一些现有的提交并以某种方式修改它,然后将新的提交塞回 Git,你会得到一个新的(和唯一的)新提交编号。 现有提交仍然存在。 您尚未更改它,只是添加了一个新的提交。

  • 每个提交由两部分组成:Git 知道的所有文件的快照,在您(或任何人)进行提交时,以及一些元数据,例如您的姓名和电子邮件地址以及一些日期和时间戳。

  • 由于每个提交都是完整快照,因此 Git 通过将提交中的文件以特殊的只读、压缩、Git 化格式共享重复文件来节省空间。 也就是说,如果您所做的某个新提交有一千个文件,但其中 999 个与之前某些提交中的文件相同,则该新提交实际上只有一个新文件。 它重复使用其他 999。 这是非常安全的,因为任何现有提交的任何部分都无法更改。

  • Git 在元数据部分包含上一个提交的提交号。


1为此,Git 包含日期和时间戳,因此即使您提交相同的内容两次,两次提交也是不同的。 只有同时提交相同的东西,两个提交才能相同,在这种情况下,你真的提交了两次,还是只是似曾相识?

2鸽子洞原则告诉我们,这最终一定会失败。 Git 在这里的诀窍是希望它不会失败,直到宇宙首先结束。 SHA-1 不再完全削减它,Git 正在转向 SHA-256。


提交是链接的,分支名称可以找到结尾

现在,每当我们有提交或其他内部 Git 对象的提交号(哈希 ID)3 时,Git 就可以在其大数据库中查找该对象。 所以我们说这个哈希 ID,或任何持有这个哈希 ID 的东西,都指向提交(或其他对象)。 由于每个提交都包含其早期前提交提交的哈希 ID,这意味着每个提交都指向较早的提交。 Git 将此较早的提交称为较晚(子)提交的父级。 我们可以画出这个:

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

在这里H代表最后一次提交的哈希 ID。 一旦我们真的有了提交H,我们就可以让 Git 查找之前提交G的哈希 ID,这让 Git 找到G。 这提供了早期提交F的哈希 ID,依此类推。

那么,我们需要的是链中最后一个提交的哈希 ID。 我们只需要将其保存在某个地方。 我们可以把它记在纸条上,或者在办公室白板上。 我们可以记住它。 或者——嘿,我们有一台电脑!计算机擅长记忆东西。 让我们让计算机记住链中最后一次提交的哈希 ID。

这就是分支名称为我们所做的:它记住上次提交的哈希 ID。 从那里开始,Git 向后工作。

让我们这样画它:

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

因为提交实际上无法更改,所以我们真的不需要箭头从H指向G,然后指向GF,等等。H总是永远向后指向G。 我们只需要记住,Git 很容易从H变成G,但不是相反:孩子指向他们的父母,但父母不认识他们的孩子,因为父母在他们生孩子之前就被冻结了。 但是另一方面,从名称中出来的箭头 - 好吧,让我们添加另一个名称,例如developfeaturetopic

...--F--G--H   <-- master, topic

请注意,这两个名称都指向现有的提交H。 这在 Git 中是正常的:所有提交现在都在两个分支上。 但是我们现在确实需要一种方法来判断我们正在使用哪个名称,为此,我们将让 Git 将特殊名称HEAD附加到这些其他名称之一:

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

这意味着我们on branch master,正如git status所说。 如果我们现在使用git checkout topicgit switch topic4我们得到:

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

现在我们on branch topic. 我们没有更改提交- 我们仍在使用提交H- 但我们确实更改了名称

您可以使用git checkoutgit switch按其哈希 ID 选择提交。 当你这样做时,Git 会使用它所谓的分离 HEAD模式。 在这里,名称HEAD没有附加到某个分支名称。 我们不会在这里详细介绍,但稍后了解它很有用,尤其是git rebase,它在内部使用此模式。


3实际上有四种类型的对象,Git 称之为blob提交标记。 每个都获得一个哈希 ID,该 ID 对于该特定对象的类型和内容是唯一的。 文件内容存储为 Blob 对象,唯一 Blob 内容获取唯一哈希 ID 这一事实是 Git 对文件进行重复数据删除的方式。 它只是从存储系统中掉出来:如果文件内容相同,则 blob 哈希 ID 也相同,因此 blob 已在对象数据库中。 您不需要知道大部分内容:您只需要了解提交哈希 ID。

4git switch命令是 Git 2.23 中的新增命令。 这是为了让git checkout更安全、更简单:git checkout命令执行太多不同的工作,因此作业被拆分(大约减半),新命令git restoregit switch各做一半。 现有的git checkout继续存在,并且可能会持续数年或数十年。 新命令已经在增长新的能力,这些能力没有被向后移植到git checkout,所以最好切换,但你有多年的时间来做到这一点。


查看提交

当我们要求 Git向我们展示其中一个提交时,我们看不到完整的快照。 相反,我们看到了变化。 Git 这样做的方式非常简单。 如果我们在查看父级为G的提交H,则git showgit log -p命令只是提取两个快照并进行比较。 当两个文件相同时,它什么也没说。 当两个文件不同时,它会找出不同之处,并显示出来。

简而言之,即使提交保留快照,我们通常也会看到差异。 Git 通过比较两个快照来实现这一点。 当 Git 将父级与子级进行比较时,这向我们展示了该提交中发生了什么;但是我们可以让 Git 比较我们喜欢的任何两个提交。

进行新提交:Git 的索引和你的工作树

我们已经注意到,每次提交中的文件都是 Git 化和去重复的冻结格式。 我喜欢简称为 Git 的冻干格式。 这种格式的文件实际上对完成任何新工作都没有,所以当你使用git checkoutgit switch来选择一些提交时,例如,通过选择一个分支名称来获取该分支,Git 会"解除冻结"文件以制作有用的副本。

有用的文件是您可以查看和使用的文件。 它们存在于 Git 称为工作树或工作树的区域中。 这里的文件具有普通的日常形式,并且计算机的所有常规文件操作命令都对它们起作用。 事实上,这些文件根本不在 Git 的控制之下,从某种意义上说,它们根本不在 Git。 它们是您的文件,可以随心所欲地处理。 您只需告诉 Git,只要您愿意并使用git checkout,将这些文件替换为某些现有提交的文件。

所有版本控制系统 (VCS) 都必须具有类似的东西,因为无论它们如何存储文件,5它们都有冻结的及时提交版本和可用版本。 但是 Git 比其他 VCS 更进一步:Git 将每个文件的第三个副本保存在 Git 调用索引或暂存区域缓存(现在很少)的东西中。 这是同一件事的三个名称。

Git 的索引可能最好被认为是提议的下一次提交。 它实际存储的是每个文件的名称,作为一个带有嵌入斜杠的长字符串——例如,path/to/file.ext只是一个长文件名;索引中没有文件夹或目录,以及该文件的冷冻干燥版本的内部哈希 ID。 因此,索引中的"副本"已经删除了重复数据,并准备进行新的提交。

当你使用git checkoutgit switch来提取一些特定的提交时,Git 会从提交中填充它的索引你的工作树。 索引保存冻干的文件,您的工作树保存普通文件,您的操作系统坚持使用目录或文件夹名称和文件名命名这些文件。 名为path/to/file.ext的文件变成了名为path的目录/文件夹,其中包含名为to的目录/文件夹,其中包含名为file.ext的文件。 Git 在这里处理操作系统的特性,包括在执行索引到工作树转换时可能需要的任何从/的转换。 这也是它解除冻结文件时,以及在需要时执行任何 CR-LF 转换时。

这意味着最初,Git 的索引与当前提交匹配。 这些也匹配您的工作树中的文件,这些文件现在是您的。 当您修改工作树中的文件时,它们将停止与冻干索引副本匹配。这就是 Git 让您始终运行git add的原因。git add命令告诉 Git:让你的索引副本与我的工作树副本匹配。 Git 此时将对文件进行压缩和重复数据删除,并更新其索引副本。


5其他系统可能会将文件存储为增量,和/或给它们编号(类Unix索引节点或等效文件),以帮助VCS识别"相同"的文件。 Git 不做任何这些,尽管它确实有一个低于Git 对象级别的级别,在该级别中,Git 对象可以被"打包"和增量压缩。


进行新提交:git commit

现在我们已经准备好看看git commit是如何工作的。 它:

  • 收集新提交的适当元数据:您的姓名和电子邮件地址、要放入提交的日志消息、时间戳的当前日期和时间以及当前提交的哈希 ID;
  • 写出 Git 索引中的任何内容,成为新的快照;
  • 添加元数据,并将其写出为新提交,为提交提供哈希 ID;和
  • (这是棘手的部分)将新提交的哈希 ID 写入当前分支名称,即HEAD附加到的名称。

最后一步是更新分支名称。 如果我们有:

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

就在刚才,好吧,现在我们有:

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

I   <-- topic (HEAD)

相反。 当前名称topicHEAD仍附在那里。 新的提交I-I像往常一样代表一些丑陋的大哈希ID - 指向现有的提交Htopic的名称现在指向新的提交I

假设我们现在使用git checkout master返回到现有的提交H。 我将topic移到master行上方,并假装我们也添加了另一个提交,并将其重命名为topic1

I--J   <-- topic1
/
...--F--G--H   <-- master (HEAD)

现在 Git 索引中的内容以及我们的工作树中的内容与现有的提交H匹配。 让我们现在创建一个新的分支名称topic2,并切换到它:

I--J   <-- topic1
/
...--F--G--H   <-- master, topic2 (HEAD)

Git 索引和我们的工作树中的内容根本没有改变。 没有现有的提交已经改变(没有一个可以),我们仍在使用提交H,但现在我们所做的任何新提交都将更改名称topic2指向的位置。 因此,如果我们现在进行两次提交,我们将得到:

I--J   <-- topic1
/
...--F--G--H   <-- master

K--L   <-- topic2 (HEAD)

合并

现在我们有了这个相当复杂的设置,我们可以看看git merge是如何完成它的工作的。 假设,为简单起见,我们将选择将topic2合并到topic1中,并在一段时间内完全忽略master。 因此,我们将从做一个git checkout topic1git switch topic1开始,并且根本不绘制master,以获得:

I--J   <-- topic1 (HEAD)
/
...--F--G--H

K--L   <-- topic2

现在我们将运行:

git merge topic2

重要的是,这种操作需要真正的合并。 我一会儿会展示一个没有的。 真正的合并不是有两个,而是三个输入,所有这些输入都是提交:

  • Git 使用名称HEAD找到其中之一的简单方法:这是我们提交或提交J。 这是稍后各种操作的--ours提交,尽管在内部这是提交#2(此内部编号可能会在几个地方泄漏,但--ours让我们不必记住它)。

  • Git 根据我们给它的命令找到一个。 既然我们说git merge topic2,Git 使用分支名称topic2来查找提交L。 这是稍后各种操作的--theirs提交,尽管在内部这是提交 #3。

  • Git 自行查找第三个提交。 Git 称之为合并基础提交,在内部,它是 #1:如果我们发现自己想要查看此提交中的文件,我们需要使用此内部编号。

合并基础是在有向图上使用最低共同祖先算法找到的,但我们可以将其视为两个分支上的最佳共享(共同)祖先。 在这里,这意味着我们从提交J开始,然后向后工作到I,然后H。 我们也从提交L开始,然后向后工作,到K,然后HH和更早的提交都在两个分支上,但H显然是一个G更好(或至少更新)的祖先,或任何更早的祖先。

Git 现在所做的是将合并基中的快照与两个分支提示快照中的每一个进行比较。 也就是说,Git 运行相当于:

git diff --find-renames <hash-of-H> <hash-of-J>   # what we changed
git diff --find-renames <hash-of-H> <hash-of-L>   # what they changed

Merge 现在的工作是合并更改,然后将这些合并的更改应用于H中的快照 —合并基础:

  • 如果我们更改了某个文件,我们究竟对该文件做了什么? 如果他们更改了同一个文件,他们会做什么?
  • 如果我们更改了文件,
  • 而他们没有更改文件,Git 需要进行与我们所做的相同更改。 但这意味着改变H的内容以匹配J中的内容。 这很简单:Git 可以从J中获取我们的文件。
  • 如果他们更改了文件而我们没有,Git 可以只获取他们的副本。
  • 如果我们删除了一个文件,而他们没有对它做任何事情,Git 可以只删除;如果他们删除了一个我们没有做任何事情的文件,也是如此。

如果我们和他们都对某个文件进行了一些更改,并且这两个更改发生冲突(例如,影响相同的行),那么 Git 可能必须声明合并冲突。 在这里,事情变得有点复杂:

  • 假设我们和他们都修复了某个文件的同一行相同拼写错误。 然后我们的更改和它们的更改匹配,Git 可以只获取该更改的一个副本。 所以这毕竟不是合并冲突。

  • 或者,也许我们将单词red更改为单词green,他们将相同的单词更改为单词yellow。 在这里,Git 不知道要进行哪些更改,并声明了合并冲突。

  • 也许我们更改了一个文件,他们删除了该文件。 这些冲突:Git 不知道是保留我们的文件,还是完全删除文件,所以 Git 声明了合并冲突。

当 Git确实声明合并冲突时,Git 会继续并尽可能执行剩余的合并工作,但随后git merge自己在中间停止。 否则,如果 Git 认为一切顺利,它将默认进行下一步。 您可以将--no-commit添加到命令行,以告诉 Git 无论如何都要停止。

这个过程,使用三个输入提交来查找和组合更改,我喜欢将合并称为动词。 它是识别(配对)输入文件并进行差异以查看更改的内容的操作,然后使用三个提交中的所有三个输入文件组合和应用组合的差异。

如果一切顺利,而你没有告诉 Git不要这样做,Git 将继续进行新的提交。 这个新提交将像任何其他提交一样,但有一个例外:它将有两个父级,而不是一个父级。 我们稍后会回到这个问题;现在,让我们假设存在冲突并且合并停止,或者您使用了--no-commit.

冲突揭示了一个重要的秘密......好吧,这不是一个秘密,但有时不能很好地解释:合并为动词的过程实际上发生在 Git 的索引中,因为 Git 从其索引构建新的提交。 Git 确实使用您的工作树:当合并因冲突而停止时,Git 将尽最大努力将合并各种文件写入您的工作树。 具有低级别冲突6的那些将包含冲突标记。 与此同时,Git 的索引也得到了扩展:它现在包含每个输入文件的三个副本,而不是一个。 这就是前面提到的那些数字的用武之地:

  • 索引槽 #1 保存文件的合并基副本。
  • 索引槽 #2 保存文件的--ours副本。 您可以使用git checkout --ours将这个发布到您的工作树中。
  • 索引槽 #3 保存文件的--theirs副本。 您可以使用git checkout --theirs将这个发布到您的工作树中。

在高级别冲突中,其中一个插槽可能为空。 我不会在这里详细介绍,因为这个答案已经很长了。

另请注意,您可以使用git checkout -m将冲突恢复到工作树副本。 小心这些git checkout操作中的任何一个,因为它们会立即覆盖为修复合并冲突所做的任何工作!

要解决合并冲突,您通常会编辑工作树副本,或使用合并工具(git mergetool将运行您选择的合并工具:Git 本身没有任何,因此这仅适用于第三方附加组件)。 正确解决冲突后,通常会运行git add告诉 Git:让你的索引副本与我的工作树副本匹配。 (git mergetool命令将为你运行git add,尽管有时它会先询问,具体取决于您使用的第三方工具以及 Git 对它的了解。 此git add清除三个编号的插槽,并在插槽 #0 中放置一个条目(您通常看不到其编号),以便 Git 索引中只有合并文件的单个副本。


6低级别冲突是指发生在文件的某些特定行内的冲突。 这就是上面的"一侧变为红色,但另一侧变为黄色"示例。高级冲突发生在整个文件中,例如,当--ours端对文件进行更改时,但--theirs端完全删除该文件。 这两种冲突都会导致合并暂停,但高级冲突不会在工作树中留下任何标记。


合并为名词或形容词:合并或合并提交

如果没有冲突,或者在解决冲突并运行git merge --continuegit commit之后,Git 现在可以进行合并提交了。 此合并提交具有快照,就像任何其他提交一样。 它有元数据,就像任何其他提交一样。 它唯一特别的是,在此元数据中,此新提交列出了两个父提交。7我们可以像这样绘制新的合并:

I--J
/    
...--F--G--H      M   <-- topic1 (HEAD)
    /
K--L   <-- topic2

请注意,像往常一样,分支名称现在指向新的提交M。 将M点提交回现有的提交J,就像任何提交一样。 特别的是,提交M通过第二个父级指向提交L:我们通过运行git merge topic2选择的那个。

现在可以通过从提交M向后工作来找到提交L,Git 将允许我们删除名称topic1。 结果如下所示:

I--J
/    
...--F--G--H      M   <-- topic1 (HEAD)
    /
K--L

如果有一个分支名称master指向提交H,则该分支名称仍然指向提交H:唯一改变的是 Git 在这里要更改的内容。


7从技术上讲,这是两个或更多。 "或更多"部分是为了适应 Git 所谓的章鱼合并。 这些不会做任何普通双父合并无法做到的事情,我们不会在这里介绍它们。


git merge实际上

不会合并的命令

在上面的图中,我们有一个分支,我们的两个topic名称指向需要向后工作才能找到共同祖先的提交。 但假设我们从这样的master开始:

...--H   <-- master (HEAD)

并添加一个分支名称feature并进行一两次提交:

...--H   <-- master

I--J   <-- feature (HEAD)

然后运行:

git checkout master
git merge feature       # or git merge --ff-only feature

git merge命令将经历与我们之前git merge topic2相同的初始过程,以查找合并基提交。 但是,这一次,当我们从J开始并向后工作时,我们可以提交H并且提交H是我们当前的提交。 因此,当我们从master开始时,我们实际上根本不需要向后工作。 提交H,合并基础,当前的提交。 在这种情况下,git merge对自己说:

嗯,你知道,如果我将提交H与自身进行比较,我根本不会发现任何变化。 将无物与某物结合的结果总是只是某物。 所以我实际上根本不需要合并任何东西。

如果我们不强迫Git 进行真正的合并,它就不会。 它不会合并,而只会签出提交J,但在此过程中将名称向前拖动master,以便我们得到:

...--H

I--J   <-- feature, master (HEAD)

(现在我们可以理顺图纸中的扭结)。

Git 称之为快进操作。 快进通常意味着相对于内部提交箭头的方向向前移动分支标签,以便新位置是当前位置的子位置。git merge执行快进而不是合并时,Git 将其称为快进合并,即使没有发生实际的合并。8

您可以通过以下git merge --no-ff来防止这种情况:

git merge --no-ff feature

将导致:

...--H------K   <-- master (HEAD)
    /
I--J   <-- feature

其中K是新的合并提交。K的第一个父级将是HK的第二个父级将是J。 提交K中的快照将与提交J中的快照匹配。


8一直执行快进的其他操作是git fetchgit push:fetch 将快进您自己的远程跟踪名称,并且推送通常仅在操作快进时才有效。 当这两个无法快进时,他们将使用"强制标志"(如果启用)来强制分支名称运动。

<小时 />

您自己的案例

我有以下分支:

  1. 主分支(当前部署到生产)
  2. 增强功能#1 - 清理未跟踪文件分支:这是从Master生成的。我对 gitignore 文件进行了更新,以在我的存储库中不包含特定的扩展名,并从存储库中删除了那些不必要的文件。
  3. 增强功能#2:这也是从Master创建的。代码更新支持客户请求。

短语"创建非主控"表明您确实做到了:

git checkout master
git checkout -b CleanupUntrackedFiles
git checkout master
git checkout -b Enhancement2

或:

git branch CleanupUntrackedFiles master
git branch Enhancement2 master

正如我们在上面看到的,这些将导致:

...--H   <-- master, CleanupUntrackedFiles, Enhancement2

进行新提交的过程将导致这两个名称出现分歧,以便它们不再指向现有的提交H

但是,我们不知道您是否完全执行了上述操作,或者您是否在不同时间创建了这些名称。 例如,也许您在master指向某个较早的提交时CleanupUntrackedFiles命名,当master指向一些较晚的提交时,该名称Enhancement2

I--J   <-- CleanupUntrackedFiles
/
...--F--G--H   <-- master

K   <-- Enhancement2

由于git merge基于提交工作,并且在某些情况下可以执行快进操作,因此这些细节很重要。

不过,现在让我们假设您具有以前的设置,即:

I--J   <-- CleanupUntrackedFiles
/
...--H

K-----L   <-- Enhancement2

H中的快照与J中的快照之间的区别在于,在H中,.gitignore文件不会列出某些文件;J中它有一些额外的行;而在J中,您在.gitignore中列出的文件不在快照,因此比较HJ将显示这些文件已删除。

同时,快照HL之间的区别在于修改了一些源文件。 我们不知道,因为您没有说,在H-vs- 中显示为"已删除"的任何文件JH-vs-L中显示为已修改。

假设您现在运行:

git checkout master
git merge --no-ff CleanupUntrackedFiles

其中--no-ff选项强制进行真正的合并。 您将获得:

I--J   <-- CleanupUntrackedFiles
/    
...--H------M   <-- master (HEAD)

K-----L   <-- Enhancement2

M中的快照将与J中的快照匹配;M的第一个父级将是H的,第二个是J的。 如果现在运行:

git merge Enhancement2

(不需要--no-ff;如果你使用该选项,它不会受到伤害,但它不会改变任何东西)假设没有冲突,你会得到:

I--J   <-- CleanupUntrackedFiles
/    
...--H------M--N   <-- master (HEAD)
       /
K-----L   <-- Enhancement2

其中N是将ML的合并基础与ML中的快照进行比较的结果。

这里的困难部分是确定哪个提交是ML的最佳共享提交。 我们必须从每个向后工作,以找到一些同时在masterEnhancement2上的提交。 从Enhancement2工作时的步骤更简单:L,然后K,然后H,依此类推。 从master开始,我们按某种顺序查看M,然后是JH(通过M)和I(通过J)和H(通过I),但我们确实得到了H。 因此H再次成为合并基础。

比较快照H中的内容与快照M中的内容。 发生了哪些变化? 答案是:在HJ中改变的是相同的,因为M的快照与J的快照匹配。 因此,更改了.gitignore,并删除了一些文件。

现在,比较快照H中的内容与快照L中的内容。 发生了哪些变化? 正如您所说,某些代码已更改。 这不是.gitignore,所以那里没有冲突。这可能包括已删除的文件:如果是这样,您将需要解决修改/删除冲突,您应该通过保留删除来执行此操作。 如果它不包括已删除的文件,则没有需要解决的冲突:Git 将从M中获取删除以及.gitignore更改,并从L中添加其他更改,以N

如果您有其他设置和/或使用其他选项,请完成示例。 这将告诉您git merge将做什么。

最新更新