这个Git回购发生了什么,我怎样才能恢复它的理智?

时间:2016-10-17 19:29:00

标签: git tfs

我从会议中回来,在一个关键项目的主分支中发现了一个令人愉快的怪异。我在会议之前提交的一些文件不再出现在主人的头上。

症状

如果我通过TFS的Web界面或Visual Studio中的集成查看包含丢失文件的目录的历史记录,那么对目录中任何内容的最后一次提交就是我添加了几个丢失的文件。没有后续提交删除它们。

如果我通过TFS Web界面浏览项目,则目录中不存在我的文件。

如果我使用git log [directory]从命令行检查它们,那么最后一次提交是在8月24日之前提交的,在添加丢失文件之前。缺少五个提交。

如果我在项目的顶层执行git log .,则会出现这五条提交消息

当我离开时,项目有两个提交由当前不可用的同事完成。它们不会触及文件丢失的项目的相同部分。 8月24日之后,同事很可能最后从主人那里撤出,但在随后的五次提交之前。

理论

从我有限的理解来看,我理论上说同事在试图改变时遇到了冲突,而不是解决它们,可能已经做了git reset以便继续进行,但不可否认的是,我知道那些git的境界充其量是不稳定的。

问题

  • 这符合上述症状吗?
  • 我可以查找日志中的内容是否表明会有这样的行为?
  • 如果这是可能的原因,我该如何恢复似乎已撤消的五个或那样的提交?

1 个答案:

答案 0 :(得分:2)

短版

  1. 如果需要,可以使用--full-history,也可以使用-m以及更多标记。

  2. 您可以使用git cherry-pick将原始提交重新复制到新的合并后提交;或者手动执行某些操作可能更简单。您可以在任何给定的"坏"上使用git show -m合并以获得对父母的差异,然后手动应用掉落的变化,甚至按摩差异并使用git apply。或者,您可以在错误合并之前检查提交,手动重新自己进行合并,将树差异化为"错误"针对更正的树合并树,因此提出了一个可能适用于您现在拥有的提交链的补丁。

  3. 第2点的长版

    使用git log --full-history -- <path>以避免丢弃某些触及给定路径的提交。如果没有--full-history,Git倾向于放弃仅从一侧修改文件的合并,例如,在每个文件的基础上,使用我们或他们自己的策略解决冲突。 (这对我来说似乎是一个错误:一些提交限制的情况,就像这里的路径一样,可能包括一些甚至所有合并diff 明确丢弃的合并但是,这是一个哲学上的改变/&#34; bug&#34;而不是彻头彻尾的&#34;显然不正确的行为&#34; bug。)

    添加-m --name-status以观察合并提交本身的修改。同时添加--merges以观察合并:

    git log --full-history -m --name-status --merges -- <path>...
    

    您可以将--name-status替换为-p,以使git log显示差异。或者,一旦找到可疑的合并(通过其哈希ID),请使用git show -m <hash>详细查看它们。

    请注意,对于-mgit loggit show的合并输出会有所改变:

    commit <sha1> (from <parentN>)
    Merge: <parent1> <parent2>
    Author: ...
    

    这里-m标志的作用是将合并(为差异目的)拆分为多个虚拟提交,每个父提交一个。然后针对该特定父母完成差异。

    场景(或者你如何到达这里,或点1的长版本)

    通常情况下,有人会运行git merge并错误地处理合并冲突,只需在左侧选择&#34;版本&#34;或&#34;版本在右边&#34;而不是&#34;主要来自基地,在适当的情况下从左边稍微一点,而在需要的时候稍微从右边开始#34;结果是冲突的文件丢失了合并的一半的所有更改。

    有时 是正确的事情,有时它不是。 Git产生了冲突,因为至少有一些的更改发生了冲突。当某些变化发生冲突时,很可能只有&#34;列A&#34;或者&#34; B列和34号;有正确的答案。但是我们可能会在同一个文件中的不同行上进行 - 冲突更改。

    例如,考虑一个(相当人为的)文本文件的片段:

      #       base             column A           column B
    
     94:   We show that       We show that       We show that
     95:   2 + 2 = 5.         2 + 2 = 3.         2 + 2 = 4.
     96:
     97:
     98:
     99:
    100:
    101:
    102:   This moth proof... This math proof... This moth proof...
    

    &#34; base&#34;版本有两个错误,一个是2 + 2 = 5,另一个是moth proof

    一个分支上的版本,在&#34; A&#34;列中,试图纠正它:2 + 2不是5,它是数学(或"maths")证明,而不是蛾子证明。

    另一个分支上的版本,&#34; B&#34;列, 更正了 - 但该版本的作者错过了第102行的错误。

    在这种情况下,正确的合并策略是从第二个更改版本获取第95行,从第一个更改版本获取第102行。一些合并工具使这很容易正确 - 我自己在vim上使用merge.conflictstyle = diff3合并冲突标记的文件,因此甚至不必在没有冲突的第102行 - 一些合并工具试图向您展示一个全局视图,这使得它太容易引人注目并且说&#34;哦,只需使用第二个父母&#34; (B栏)。

    简而言之,你的理论可能是正确的(虽然git reset可能不是罪魁祸首)。但是,要确定,你必须观察同一个人重新做同样的合并 - 假设他或她还没有学会正确地进行合并!

    第3点的长版

    我认为这最好用一些提交图的绘图。您可以使用各种工具获取此功能,包括git log --graph(可选择--oneline--decorate等标记),以及gitk等可视化工具。它们倾向于垂直呈现图形,更新提交到顶部。由于文字原因,我喜欢水平绘制它们,向右侧绘制更新的提交。

    让我们说图表目前看起来像这样:

    ...--o--*--o--o--o--E--M--o--o--F   <-- branch
             \            /
              A--B--C--D-´   <-- topic [label may no longer exist]
    

    此处,*是错误合并M的合并基础。当有人破坏事物时,更主要的branch的提示是提交E,而topic分支的提示是提交D。合并的人在分支branch上,事情看起来像这样:

    ...--o--*--o--o--o--E   <-- branch
             \
              A--B--C--D    <-- topic
    

    他们运行git merge branch,或者可能从某些GUI中获得了相同的功能,从而导致他们沿着错误的合并花园路径行进。这产生了合并冲突,他们以不恰当的方式解决并承诺。

    您可以重新执行整个合并。只需按ID查看提交E,和/或为其指定分支名称。使用git checkout -b我们可以同时执行这两项操作:

    git checkout -b remerge <id-of-E>
    

    (或者,您可以在&#34;分离的HEAD&#34;模式中执行此操作,这是我为快速一次性测试所做的。您始终可以为分离的HEAD指定名称{{1}稍后。)

    现在提交git checkout -b是最新的,只需重新运行合并:

    E

    Git将执行相同的合并操作&#34;当时&#34;,因为它将同一个提交合并到同一个提交中,从同一个图开始。 (注意:如果您启用了git merge <id-of-D> ,您可能希望暂时 dis 能够在此处使用它,特别是如果您是进行错误合并的人。:-))

    如果您现在解决冲突(这次正确)并提交结果,您将获得新的合并git rerere

    M2

    您现在可以比较...--o--*--o--o--o--E--M--o--... <-- branch \ \/ A--B--C--D-´\ \ \ `--M2 <-- remerge M

    M2

    查看将git diff <id-of-M> HEAD 更改为M所需的内容。

    这里有很多选择

    无论您是否M2,您都可以:

    • 查看提交M2,如果您只需要部分或全部,请使用A--B--C--D将其复制到您现在的位置:

      git cherry-pick

      此处...--o--*--o--o--o--E--M--o--o--F--A'-B'-C'-D' <-- branch \ / A--B--C--D-´ A'B'C'D'A的精选副本。

    • 尝试D - 在git apply之上的MM2之间的差异:

      F

      (将...--o--*--o--o--o--E--M--o--o--F--G <-- branch \ / A--B--C--D-´ 输出保存在文件中,或重新运行git diff并输送到git diff。)

    • 您甚至可以立即将git apply合并到M2(虽然这可能会非常混乱:提交branchF的合并基础是M2D的虚拟合并,这是一个糟糕的情况,因为我们首先在这里预先假定冲突!),或樱桃挑选E对抗其中一个父M2

    根据具体情况,如果没有太多,我可能会选择挑选E,或者甚至将它们复制到一个新的主题分支并合并它;或只是A--B--C--D - git apply - vs - M差异

    使用rebase复制主题分支,以另一种方式重做合并

    &#34;复制到新主题分支&#34;但是,这里的方法可能值得拥有它自己的一点描述,因为M2是执行此操作的命令。以下是使用git rebasegit rebase复制到topic的方法。让我们假设我们从这开始:

    retopic

    首先,我们需要一个名为...--o--*--o--o--o--E--M--o--o--F <-- branch \ / A--B--C--D-´ <-- topic 的分支:

    retopic

    (如果名称git checkout -b retopic topic 消失,请使用提交topic的ID。)现在我们有:

    D

    现在只需运行...--o--*--o--o--o--E--M--o--o--F <-- branch \ / A--B--C--D-´ <-- topic, HEAD -> retopic 。如果git rebase --onto branch <id-of-E>的ID不方便但提交E的ID为,则使用A(请注意帽子后缀)以生成提交<id-of-A>^的ID。我们在这里所做的就是指示*复制以git rebase结尾的提交(其中D点),并从提交retopic开始(通过排除提交{{ 1}}和更早,可以从提交A)。

    在冲突发生时解决冲突 - 你甚至可以在开始变装之前启用* - 当你完成后你就有了这个:

    E

    您现在可以git rerere A'-B'-C'-D' <-- HEAD -> retopic / ...--o--*--o--o--o--E--M--o--o--F <-- branch \ / A--B--C--D-´ <-- topic 从rebase复制的提交中进行 new 合并git checkout branch。 (注意:git merge --no-ff retopicM2中的部分或全部可能会在复制过程中完全丢失,具体取决于错误合并A'中保留的内容。)