合并后丢失的Git提交 [英] Git commit lost after merge

查看:115
本文介绍了合并后丢失的Git提交的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们有3个分支(A,B,C),如下所示:

We have 3 branches (A, B, C) as below:

---\--A1--\------Am------Merge1---An---Merge2---
    \      \              /             /
     \      \--C1---C2---/             /
      \                               /
       \--B1--------------Bn---------/

问题出现在Merge2.分支C上的某些提交(不是全部,而是,例如C2)在Merge2之后丢失了,分支A上的分支A出现在Merge1和Merge2之间.

The problem appears at Merge2. Some commit on branch C ( not all but some, let's say C2) is lost on branch A after Merge2, which is present between Merge1 and Merge2.

执行Merge2时,只有一个文件冲突,与丢失的提交(C2)不相关.并且我们解决了冲突,并成功完成了合并.

When doing Merge2, there is only one file conflict, which not relates to the lost commit (C2). And we resolve the confilict and finish the merge successfully.

似乎分支A上的Merge2反转了C2,没有任何日志.

发生了什么事?造成这种情况的原因可能是什么?

What happened? What might be the cause to this situation?

推荐答案

您的意思不是说本身丢失了,而是所做的更改(到某个文件)中的提交已恢复(未创建).

What you mean is not that the commit itself is lost, but rather that the changes made (to some file) in that commit have been reverted (un-made).

在这里值得注意的是,提交实际上并不是对文件进行更改".相反,每个提交都存储完整的完整文件集.当您要求Git向您显示一些提交时(例如,其ID为c2c2c2...):

It's worth noting here that commits don't really "make changes to" files. Instead, each commit stores a complete set of files, whole and intact. When you ask Git to show you some commit (let's say its ID is c2c2c2...):

$ git show c2c2c2

Git所做的是:

  1. 提取提交c2c2c2...
  2. 提取c2c2c2...
  3. 的父提交
  4. 生成父母到孩子的差异列表
  1. extract commit c2c2c2...
  2. extract the parent commit of c2c2c2...
  3. produce a diff listing from parent to child

这是Git设法向您显示更改的方式:它将您刚刚提交之前所拥有的内容"与截至该提交时您所拥有的内容"进行了比较. (Git可以非常快速,优化地执行此操作,因为每个文件都被简化为唯一的哈希指纹",并且Git可以首先仅比较指纹(哈希).如果哈希相同,则文件相同.如果哈希值不同,实际上只需要麻烦提取实际的文件数据.)

This is how Git manages to show you what changed: it compares "what you had just before that commit" to "what you had as of that commit". (Git can do this pretty quickly, optimized-ly, because every file is reduced to a unique hash "fingerprint", and Git can first just compare the fingerprints (hashes). If the hashes are the same, the files are the same. It only really has to bother extracting the actual file data if the hashes differ.)

此过程(保存整个文件,而不是累积更改)被称为存储快照". (其他版本控制系统往往会累积更改,这称为存储增量".之所以这样做,是因为将 changes 保存到文件显然比保存文件占用的空间少得多.还是比旧的基于delta的版本控制系统更节省磁盘空间.)

This process—saving whole files, instead of accumulating changes—is called "storing snapshots". (Other version control systems tend to accumulate changes, which is called "storing deltas". They do this because saving the changes to files obviously takes far less space than saving the files. Git sneaks around the issue in a clever way and winds up using less disk space than older delta-based version control systems anyway.)

合并提交是特殊的,以一种特殊且显而易见的方式.查看您自己的图并考虑Merge1.即将发生什么提交?

A merge commit is special, in one particular and obvious way. Look at your own diagram and consider Merge1. What commit comes right before it?

这里的答案是AmC2都在Merge1之前.也就是说,提交Merge1具有两个父提交.

The answer here is that both Am and C2 come "right before" Merge1. That is, commit Merge1 has two parent commits.

如果您要求Git证明您提交了Merge1,应该与哪个父级进行比较?

If you ask Git to show you commit Merge1, which parent should it compare-with?

在这里事情变得特别奇怪. git log -pgit show seem 这两个命令非常相似.实际上,它们非常相似.一个明显的区别是git log -p显示了多个提交,而git show仅显示了您告诉它显示的一个提交.您可以运行git log -n 1 -p <commit>仅显示一次提交,现在它似乎就像这些完全一样.

Here's where things get particularly odd. The two commands git log -p and git show seem very similar. In fact, they are very similar. The one obvious difference is that git log -p shows more than one commit, while git show shows just the one commit you tell it to show. You can run git log -n 1 -p <commit> to show just the one commit, and now it seems like these are exactly the same.

他们不是!

当您在合并提交上使用git show时,Git尝试通过同时与两个父母进行比较来解决与哪个提交进行比较"的问题.产生的差异称为组合差异.

When you use git show on a merge commit, Git tries to solve the "what commit to compare against" problem by comparing, simultaneously, against both parents. The resulting diff is called a combined diff.

但是,当您在合并提交上使用git log -p时,Git只是举起它的隐喻之手,说我不能向两个父母展示补丁",然后放弃并继续下一个提交. 换句话说,git log -p甚至不用费心为合并尝试差异.

When you use git log -p on a merge commit, though, Git just throws up its metaphorical hands, says "I can't show patches against two parents", and gives up and goes on to the next commit. In other words, git log -p doesn't even bother trying diffs for the merge.

现在,在这种情况下,您可能会想看看是否可以在两个合并中(特别是在Merge2上)使用git show从提交c2c2c2...中弄清文件c2c2c2...对文件造成了什么影响已还原.但是默认情况下,git show会产生组合差异,组合差异有意忽略了许多差异输出.特别是,组合的差异仅列出文件由所有父母修改的.

Now, in this case you might be tempted to see if you can figure out what happened to your file from commit c2c2c2... using git show on the two merges—in particular, on Merge2, where the changes got reverted. But git show produces, by default, a combined diff, and a combined diff deliberately omits a lot of diff output. In particular, a combined diff lists only files which were modified from all parents.

比方说,您从C2所做的更改被还原的文件是文件f2.而且,从图中可以看出,Merge2的两个父级分别是An(具有f2您想要的方式)和Bn(没有).

Let's say the file where your changes from C2 were reverted is file f2. And, from the graph, the two parents of Merge2 are An (which has f2 the way you want it) and Bn (which doesn't).

这里实际发生的是,在创建Merge2的合并期间,您以某种方式告诉Git使用提交Bn中的f2版本.也就是说,Merge2中的文件f2Bn中的文件f2完全相同,并且与提交An中的f2不同.

What actually happened here is that, during the merge that created Merge2, you somehow told Git to use the version of f2 from commit Bn. That is, file f2 in Merge2 is exactly the same as file f2 in Bn, and different from f2 in commit An.

如果使用git show查看Merge2,则组合的差异将跳过 f2,因为它与Bn.

If you use git show to view Merge2, the combined diff will skip f2, because it is the same as the f2 in Bn.

git log -p同样如此,甚至更糟:它完全跳过合并,因为很难显示差异.

The same is true, only even worse, with git log -p: it skips the merge entirely, because it's just too hard to show diffs.

即使没有-p,当您要求文件已更改"时,git log也会执行相同的操作-完全跳过合并.这就是为什么您无法在日志输出中看到它的原因.

Even without -p, when you ask for "files changed", git log winds up doing the same thing—skipping the merge entirely. That's why you can't see it in the log output.

(顺便说一句,git log master -- f2从未显示commit C2本身的原因是,在git log的选项中添加文件名会启用历史记录简化".在我看来,这是错误的行为, Git结束时简化了太多历史记录,因此它从不显示提交C2.在-- f2还原C2到显示的提交集之前添加--full-history.但是缺少了,因为git log跳过了它.)

(As an aside, the reason git log master -- f2 never shows commit C2 itself is that adding a file name to the options to git log turns on "history simplification". In what I consider to be somewhat buggy behavior, Git winds up simplifying away too much history, so that it never shows commit C2. Adding --full-history before the -- f2 restores C2 to the set of commits shown. The merge is still missing, though, because git log skips it.)

有一个解决方案. git showgit log都带有一个附加标志-m,该标志拆分"合并.也就是说,这些操作不会将Merge2视为合并提交,而是会将合并分为两个虚拟提交".一个将是"Merge2 vs An",您将看到这两个提交之间的所有差异,另一个将是"Merge2 vs Bn",并且您将看到那些两次提交.这将显示文件f2被重新设置为Bn中的方式,从而丢失了An中出现的C2中的版本,而不是Bn中出现的版本.

There is a solution. Both git show and git log take an additional flag, -m, which "splits" merges. That is, instead of treating Merge2 as a merge commit, these will break the merge into two "virtual commits". One will be "Merge2 vs An", and you will see all the differences between those two commits, and the other will be "Merge2 vs Bn", and you will see all the differences between those two commits. This will show that file f2 got re-set to the way it is in Bn, losing the version from C2 that appears in An but not in Bn.

(包括--full-history-m以确保同时显示提交C2.)

(Include --full-history as well as -m to ensure that commit C2 shows up as well.)

这部分完全不是 清楚.您说过存在合并冲突,这意味着git merge停下来并得到了人工的帮助.在此协助期间的某个时候,人类可能会使用Bn中的文件f2版本(或者至少是在C2中没有进行更改的f2版本)来更新索引.

This part is not clear, at all. You said there was a merge conflict, though, which means git merge stopped and got manual assistance, from a human. At some point during this assistance, the human probably updated the index with the version of file f2 from Bn (or at least, a version of f2 that did not have the change made back in C2).

这可能会在合并过程中发生,并且因为有点阴险,因为Git显示了合并了这些压缩(合并)的diff的情况,或者在git log -p的情况下,默认情况下完全没有.这是需要提防的事情,特别是如果合并需要手动解决冲突时.在大多数情况下,捕获此类合并错误的方法是使用自动化测试. (当然,并不是所有的东西都可以测试.)

This can happen during merges, and it's a bit insidious precisely because Git shows merges with these compressed (combined) diffs, or in the case of git log -p, not at all, by default. It's something to watch out for, especially if a merge required manual conflict resolution. In most cases, the way to catch this sort of merge error is with automated tests. (Of course, not everything can be tested.)

这篇关于合并后丢失的Git提交的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆