合并两个分支A和B,其中A包含从B中删除的文件

时间:2016-05-11 00:47:51

标签: git github merge

好的,所以基本的想法是有些文件我想确保恢复或“未删除”。

我不确定发生了什么,但我有git分支A和B而且我99%,B从A分支出来。在我创建B之后,我从B中删除了一些文件然后进行了很多更改。我现在想要与A合并,但我担心当合并分支时,B中删除的文件将被永久删除。

之前有没有人处理过这种情况?我要尝试的第一件事是从A创建一个分支C,然后尝试将C与B合并,但我的猜测是不能解决问题。

1 个答案:

答案 0 :(得分:3)

  

我不确定发生了什么,但是我有git分支A和B,而我99%是B从A分支出来。

合并过程并不关心你如何到达现在的位置, 1 只在你真正的位置。更具体地说,它需要三个项目:

  • 您当前的提交(HEAD),无论它在哪个分支上,如果有的话。让我们称之为"我们的"提交和/或"本地"承诺。当我们需要它时(很快),Git有--ours

  • 您的"其他"提交,从参数git merge:例如,git merge foo将解析名称foo以查找其特定的提交ID。 Git的大多数部分都给它命名,称之为"他们的" commit(虽然有一部分称之为" remote" commit,这特别令人困惑,因为它与Git的fetch和push remotes无关)。让我们称之为"其他"提交,但是当我们需要它时(很快)Git使用--theirs

    即使两个提交都是由您提出的,这个--ours--theirs内容仍然存在(另一个原因是将它们称为"本地"以及"其他",I想)。

  • 合并基础 2 这是上的最新 3 提交分支,即两个分支发散的点。

这是第三件事 - 合并基础 - 你有点模糊。您可以使用命令git merge-base简单地指示Git告诉您哪个提交。

  

创建B之后,我从B中删除了一些文件然后进行了很多更改。我现在想要与A合并,但我担心当合并分支时,B中删除的文件将被永久删除。

他们可能会 - 除了Git中没有任何东西是永久性的。或者我们可以说所有都是永久的,这意味着什么都不是永久性的,这是非常哲学的,但基本上就像是说如果一切都是100%批判,那么没有什么是真正重要的,或者至少,没有什么比什么都重要。 : - )

以下是需要考虑的一些事项,然后是提供这些内容的一些工具:

  • Git通过将合并基础与两个(本地和其他)提交进行比较来执行合并。这实际上说明了每一方面发生的事情"自公共基础提交以来发生的分支。

    合并是通过获取每一方所做的每个更改的一个副本来完成的。只要不能做到,Git就会宣布冲突。

    因此,假设文件misc.txt在基本到本地的更改集中被删除,并且在基础到其他更改集中添加了一行。我们如何结合"删除整个文件"与"添加此行"? Git的答案是:"我没有,我只是在该文件中宣布合并冲突。"

    另一方面,假设文件samp.dat在基础到本地被删除,并且在基础到其他基础上没有进行任何更改。我们如何结合这些?答案很明确:删除文件。

    还有许多其他冲突案件,但上面显示的是你特别担心的案件,在这里。

  • 您想将A合并为B,还是将B合并为A?究竟是什么区别?

    例如,上面,我们只是说如果base-to-local删除samp.dat而base-to-other将其保留为未修改会发生什么:Git会删除合并结果中的samp.dat。如果base-to-other删除了该文件,并且base-to-local使其保持不变,则Git 删除该文件,因为更改仍然"方说删除,一方说什么都不做"。 哪个方面说出来并不重要,只是它正好是一方。 (或者,如果两个方面都说删除文件,那么也没有问题。)

    实际上,合并结果总是相同的:一组更改表示做X,另一组表示做Y;如果Git可以将它们组合在一起,那么结果就是组合,否则结果就是"冲突",无论Git最好记录两组变化。但在所有情况下,结果都是对称的:组合(X,Y)≡组合(Y,X)。 (如果它没有显示在所有字体中,那么在两个"组合&#之间有一个三行" ="符号,"相当于"符号34; S)

    唯一的区别是,当你启动进程时,Git将在你所处的任何分支上进行新的合并提交 - 当然,无论你在哪个分支,你都需要命名 other < / {>分支(或其提示提交)在git merge命令中。它是相同的工作树(组合合并结果),而不是提交本身。

  • 最后,假设您有一些提交 - 可能是合并提交,或者可能只是一个普通的提交 - 删除文件misc.txt。现在,记住Git是一个版本控制系统并永远保存所有内容, 4 假设我们告诉Git在提交之前从提交中提取misc.txt,并进行新的提交。作为工作树内容,新提交具有删除misc.txt 加上从先前提交中复活的misc.txt版本的所有内容。

    如果我指示您查看新提交而不是旧提交,您是否会关心 misc.txt如何复活? ( 但它最终得到了哲学问题,即只要一切都是任何永久删除{/ 1}}并且新的永久复活它,那么&#34;永久&#34;甚至意味着 5

  • 由于合并本身就是一个新的提交,因此您可以随时重复合并,或至少任何自动的,Git执行的部分。旧提交是永久性的,你可以简单地从一个旧提交中获取一个匿名分支,并合并另一个旧提交,重复Git的工作。如果旧提交因合并冲突而停止,则新提交将以相同的冲突停止(只要您使用相同的合并策略和选项,无论如何)。

  • 如果发生冲突,您无法直接重复您选择的任何手部分辨率......但您不必这样做,因为最终解决方案会记录在合并提交中。如果你弄错了,你可以重做合并,获取 正确的任何最终解决方案,并重新解决剩余的冲突。这包括删除和/或复活文件。

无论如何,有了这些工具,让我们看看这些合并工具:

  • misc.txt。 Git将把合并基础提交的更改与两个提交提交相结合。您可以在合并之前,期间和/或之后查看这些更改。合并前有一个很好的快捷方式,在合并后停止工作(因为当前的分支名称现在指向新的合并提交):

    git diff

    (注意三个点)。第一个命令将合并基础与提交git diff A...B git diff B...A 进行比较,第二个命令将合并基础与提交B进行比较。添加A(以及可选--name-status仅选择状态为“刚刚”的文件)以查看哪些文件在哪一侧被删除,并查看这些文件是否已被修改(--diff-filter=D,另一方面为“M&#;;”;这将告诉您--diff-filter=M是否会删除它们。

  • git merge添加到--no-commit并在合并后停止,即使您没有可以解决的冲突:

    git merge

    您现在可以对树进行手动更改。当然,这些都不是自动的,因此后来的重新合并将不会这样做。这是一个好主意吗?这取决于您对永久提交的要求!

  • 在解析期间(来自Automatic merge went well; stopped before committing as requested 或由于合并冲突),您可以从合并的任一侧提取特定文件。

    通常,在仔细检查所有内容后,在合并冲突期间执行此操作。例如,我们可能会决定合并应该从我们的本地提交中获取我们的文件版本,完全忽略它们对其他提交的更改:

    --no-commit

    或者我们可能会认为其他提交是正确的:

    git checkout --ours -- file.ext
    

    仅当文件发生冲突时才会起作用。如果Git自己解决了合并,我们可能需要一种不同的方式来命名文件。我们可以简单地指定具有我们想要的版本的提交:

    git checkout --theirs -- file.ext
    
    例如

    。但是输入SHA-1哈希值很难看并且容易出错。幸运的是,我们可以使用两个名称:git checkout a012345 -- file.ext 是本地提交(我们的),HEAD是另一个提交(他们的),所以:

    MERGE_HEAD

    (通常不需要git checkout HEAD -- file.ext git checkout MERGE_HEAD -- restored1.py restored2.tex ,通常只是一个好主意:如果您需要的文件是奇怪的--,这是一个很好的习惯,可以保护您。 / p>

最后,您当然可以进行合并,让它进行提交,然后提取您想要恢复的任何文件 - 可能是从另一个分支的提示:

--theirs

并进行另一次提交,或使用git checkout A -- restore.me restore.me.too 将合并提交替换为包含已还原文件的新合并提交。 6

1 为了更好地检测文件重命名,它可能应该关心,或者至少应该能够关注。这完全是另一个讨论。

2 从技术上讲,可能有多个合并基础。如果是这样,git commit --amend会找到所有这些内容;如果是这样,预测会发生什么需要更多的工作。但是,对于大多数合并,只有一个。

3 Git不会(也绝不)关心提交的时间戳,而是关注图的形状。因此&#34;最近&#34;是不是很正确,但它确实捕获了如果两个分支上有许多提交 - 这几乎总是正确的 - 我们希望最接近&#34;本地和其他承诺,我们进一步回到&#34;第一&#34;一,这些提交的年龄越大,就图表历史而言,即使它们上的时间戳是奇怪的。

4 事实上,有些东西确实会被丢弃,但只有在未引用之后才会被丢弃,这是一个单独讨论的话题。

5 事实上,这个问题有一个严格正确的答案,至少对于Git来说。这个答案与Git哈希的身份联系在一起,这与你如何使用数字签名来签署&#34;特别提交。任何事物的实际持久性都是它的哈希ID:只要哈希ID保持有效,提交仍然存在,保持&#34;永久性&#34;永久......除非你设法打破SHA-1哈希。

6 请注意,git merge-base --all(以及--amend,尤其是rebase似乎违反了&#34;永远保存所有东西&#34;规则。但事实上,他们并不是:他们不会替换任何,他们只是添加新的提交,并隐藏-i中的旧提交。隐藏的那些最终变得完全没有引用,因此在脚注4中到期,但你有足够的时间 - 通常至少一个月 - 让它们回来。并且,任何仍然可以在常规git log 中查看的提交永远不会过期 - 它只是被git log --all或者rebase推到一边的那些,一旦不在视线范围内,最终到期。