git何时在合并期间丢失更改?

时间:2017-04-30 13:01:15

标签: git version-control rebase

让我们说:

  1. 我们有一个主分支,其中一位同事意外地添加了一系列应该属于新功能的提交(让他们称之为A B C)。
  2. 我发现了,我告诉他将这些提交移动到一个新的分支,但保留稍后在master中完成的其他无关提交。我问他这个问题并告诉他跟进回复:git: how to move a branch's root two commits back
  3. 几天后,当新功能分支准备就绪时,我将其合并为master。
  4. 解决合并中的所有冲突后,我提交了更改......
  5. ...我发现那些第一次提交(A B C)已经消失了。
  6. 我问我的同事,他说那是"他认为"他使用链接中提到的方法移动了这些更改(基本上:检查最后一个常见提交,然后使用git cherry-pick仅选择我们后面想要的提交),但他不能完全记住。
  7. 我会检查回购消息的历史记录,并在功能分支中查看A B C 。他们看起来像是从大师那里成功迁移过来的。
  8. 鉴于上述情况,有谁可以解释为什么git丢失了这些变化? (我的个人理论是git不知怎的"记得"我们已经撤消了提交A B C,所以当他们来自新功能分支时,git决定不合并它们。编辑:对不起,如果这个解释听起来太像魔法思维了,但我感到很茫然。我欢迎任何尝试用更多技术术语来解释这个问题,如果它是对的话。

    很抱歉无法提供更多详细信息,但我个人没有在回购邮件中进行这些更改,因此无法详细说明所做的事情。

    编辑:好的,正如这里建议的那样,我让我的同事在他的机器上执行git reflog,所以我在这里粘贴结果。要回到我以前的(链接)问题,我们有一个这样的树:

    A - B - C - D - E - F  master
                \ 
                 \- G - H  new feature branch
    

    我们希望将B和C移动到新功能分支。

    所以,他发给我的git reflog就在这里。提交5acb457将对应于"提交A"在上图中:

    4629c88 HEAD@{59}: commit: blah
    f93f3d3 HEAD@{60}: commit: blah
    57b0ea7 HEAD@{61}: checkout: moving from master to feature_branch
    4b39fbf HEAD@{62}: commit: Added bugfix F again
    4fa21f2 HEAD@{63}: commit: undid checkouts that were in the wrong branch
    1c8b2f9 HEAD@{64}: reset: moving to origin/master
    5acb457 HEAD@{65}: checkout: moving from 5acb4576eca4b44e0a7574eea19cca067c039dc5 to master
    5acb457 HEAD@{66}: checkout: moving from master to 5acb4576eca4b44e0a7574eea19cca067c039dc5
    1c8b2f9 HEAD@{67}: checkout: moving from 1c8b2f9bf54ca1d80472c08f3ce7d9028a757985 to master
    1c8b2f9 HEAD@{68}: rebase: checkout master
    5acb457 HEAD@{69}: checkout: moving from master to 5acb4576eca4b44e0a7574eea19cca067c039dc5
    1c8b2f9 HEAD@{70}: checkout: moving from 5acb4576eca4b44e0a7574eea19cca067c039dc5 to master
    5acb457 HEAD@{71}: checkout: moving from master to 5acb4576eca4b44e0a7574eea19cca067c039dc5
    1c8b2f9 HEAD@{72}: merge origin/master: Fast-forward
    5acb457 HEAD@{73}: checkout: moving from master to master
    5acb457 HEAD@{74}: checkout: moving from 5acb4576eca4b44e0a7574eea19cca067c039dc5 to master
    5acb457 HEAD@{75}: checkout: moving from undo_branch to 5acb4576eca4b44e0a7574eea19cca067c039dc5
    5acb457 HEAD@{76}: checkout: moving from master to undo_branch
    1c8b2f9 HEAD@{77}: checkout: moving from undo_branch to master
    525dbce HEAD@{78}: cherry-pick: Bugfix F
    a1a5028 HEAD@{79}: cherry-pick: Bugfix E
    32f8968 HEAD@{80}: cherry-pick: Feature C
    8b003cb HEAD@{81}: cherry-pick: Feature B
    5acb457 HEAD@{82}: checkout: moving from 5acb4576eca4b44e0a7574eea19cca067c039dc5 to undo_branch
    5acb457 HEAD@{83}: checkout: moving from master to 5acb4576eca4b44e0a7574eea19cca067c039dc5
    1c8b2f9 HEAD@{84}: checkout: moving from 1c8b2f9bf54ca1d80472c08f3ce7d9028a757985 to master
    1c8b2f9 HEAD@{85}: pull origin HEAD:master: Fast-forward
    5acb457 HEAD@{86}: checkout: moving from master to 5acb4576eca4b44e0a7574eea19cca067c039dc5
    5acb457 HEAD@{87}: reset: moving to 5acb4576eca4b44e0a7574eea19cca067c039dc5
    1c8b2f9 HEAD@{88}: merge origin/master: Fast-forward
    5acb457 HEAD@{89}: reset: moving to 5acb4576eca4b44e0a7574eea19cca067c039dc5
    1c8b2f9 HEAD@{90}: checkout: moving from 5acb4576eca4b44e0a7574eea19cca067c039dc5 to master
    5acb457 HEAD@{91}: checkout: moving from master to 5acb4576eca4b44e0a7574eea19cca067c039dc5
    1c8b2f9 HEAD@{92}: merge origin/master: Merge made by the 'recursive' strategy.
    7b912cd HEAD@{93}: checkout: moving from 7b912cdf33843d28dd4a7b28b37b5edbe11cf3b9 to master
    7b912cd HEAD@{94}: cherry-pick: Bugfix F
    df7a9cd HEAD@{95}: cherry-pick: Bugfix E
    d4d0e41 HEAD@{96}: cherry-pick: Feature C
    701c8cc HEAD@{97}: cherry-pick: Feature B
    5acb457 HEAD@{98}: checkout: moving from master to 5acb4576eca4b44e0a7574eea19cca067c039dc5
    22ecc3a HEAD@{99}: checkout: moving from 5acb4576eca4b44e0a7574eea19cca067c039dc5 to master
    5acb457 HEAD@{100}: checkout: moving from master to 5acb4576eca4b44e0a7574eea19cca067c039dc5
    22ecc3a HEAD@{101}: commit: bugfix E
    3b568bc HEAD@{102}: checkout: moving from feature_branch to master
    57b0ea7 HEAD@{103}: commit: blah
    152c5b9 HEAD@{104}: checkout: moving from master to feature_branch
    3b568bc HEAD@{105}: commit: bugfix D
    fe3bbce HEAD@{106}: checkout: moving from feature_branch to master
    152c5b9 HEAD@{107}: commit: blah
    2318ebc HEAD@{108}: commit: blah
    cc5ea32 HEAD@{109}: commit: blah
    a5c2303 HEAD@{110}: commit: blah
    544a99a HEAD@{111}: commit: blah
    299f86a HEAD@{112}: commit: Feature G
    fe3bbce HEAD@{113}: checkout: moving from master to feature_branch
    fe3bbce HEAD@{114}: commit: Feature C
    3852e71 HEAD@{115}: commit: Feature B
    5acb457 HEAD@{116}: merge origin/master: Fast-forward
    

    任何人都可以连续了解这4个cherry-pick吗?我怀疑他并没有真正做git cherry-pick master~3事,尤其不是~3部分(当我第一次看到它时,它确实让我失望)。

2 个答案:

答案 0 :(得分:3)

提交A,B和C失败的原因,那是因为这是您与同事共享的链接。让我们通过下面的图表说明:

<强> 1。假设您的同事做的原始提交历史,

...X---A---B---C---D---E  master

<强> 2。将ABC移至feature分支。因此,您的同事从主人创建了一个新的feature分支(提交E }或任何提交。并通过以下步骤进行改造:

git checkout -b feature
git cherry-pick master~5 master~2

...X---A---B---C---D---E  master
                        \
                         A'---B'---C' feature 

第3。修改master分支

git checkout X
git cherry-pick master~2..master
git branch -f master
git checkout master

提交结构如下所示:

...X---D---E  master
     \
       A'---B'---C' feature 

所以直接原因是命令git cherry-pick master~2..master。它将直接在提交D上重新提交EX,因此您无法在主分支上找到ABC

更新

基于git flog,似乎这些HEAD信息不足以显示您的同事所做的事情。 feature分支似乎从提交C而不是D结帐

3b568bc HEAD@{105}: commit: bugfix D
fe3bbce HEAD@{106}: checkout: moving from feature_branch to master
152c5b9 HEAD@{107}: commit: blah
2318ebc HEAD@{108}: commit: blah
cc5ea32 HEAD@{109}: commit: blah
a5c2303 HEAD@{110}: commit: blah
544a99a HEAD@{111}: commit: blah
299f86a HEAD@{112}: commit: Feature G
fe3bbce HEAD@{113}: checkout: moving from master to feature_branch
fe3bbce HEAD@{114}: commit: Feature C

所以结构应该是:

A---B---C---D---E  master
         \
          G---H feature

如果您只想更改提交结构,请执行以下操作:

A ---D---E  master
 \
  B---C---G---H feature

您可以将master分支和feature分支重置为原始分支,然后在master分支上重新提交提交,详细信息如下:

git checkout master
git reset --hard <original commit id for E>
git checkout feature 
git reset --hard  <original commit id for H>
git checkout master
git checkout <commit id for A>
git cherry-pick master~4..master~2 #To make the commits as A---D---E (drop B and C)
git branch -f master
git checkout master

答案 1 :(得分:0)

你已经得到了很长很好的答案。让我补充一点:

  

我的个人理论是git不知怎的&#34;记得&#34;我们撤消了提交A B C,所以当他们来自新功能分支时,git决定不合并它们。

Git从来没有&#34;不知何故&#34; &#34;记得&#34;关于存储库内容的任何信息。根据你之前所做的事情,它也决定不做或不做任何事情。在这方面它非常干净。它的所有命令都只是工具,可以处理其提交的有向非循环图(以及较低级别,它存储的所有其他对象)正在构建。为了使它更容易,它只会添加东西,永远不会改变或删除任何东西。

除了提交(即作者,时间戳,父提交等),树(即目录),blob(即二进制数据)和一些不那么重要的事物之外,实际上没有数据结构或进一步的管理信息关于你的文件等在存储库中。合并提交不会留下任何特定于&#34; merge&#34 ;;的信息。它只是一个多父母的提交。

当然没有任何神奇的,无证件的东西在继续。存储库是非常开放的,您可以使用git命令逐字查看所有内容,并且所有内容都已完整记录(google&#34; git数据结构&#34;或者#34; git internals&#34;如果您感兴趣的话)。如果您愿意,甚至修改内部对象也很容易。

有一点位保留历史信息,这就是所谓的&#34; rerere缓存&#34;它存储以前的冲突解决方案,因此确实可以改变未来合并的行为。确实非常方便,但默认情况下未启用,当然与手头的主题无关。

  编辑:对不起,如果这个解释听起来太像魔法思维&#34;,但我不知所措。如果正确的话,我欢迎任何尝试用更多技术术语来解释这个问题

相信来源,卢克。很高兴您正试图让自己的头脑周围的git,并坚信一切都是平淡无奇的应该有所帮助,希望如此。