Git 恢复错误合并后恢复丢失的更改

Git 恢复错误合并后恢复丢失的更改,git,git-merge,git-revert,Git,Git Merge,Git Revert,我对Git比较陌生,在误解了一篇帮助文章后犯了一个(愚蠢的)错误,我不知道如何使用Git完全修复这个问题,而不是手动重新引入对目标分支的更改 S---sc1---sc2---sc3-----sc4----M4---R1---M5---sc5 \ \ / / T1-------------------M2---M3--------R2 \ \ / F---fc1---fc2---M1

我对Git比较陌生,在误解了一篇帮助文章后犯了一个(愚蠢的)错误,我不知道如何使用Git完全修复这个问题,而不是手动重新引入对目标分支的更改

S---sc1---sc2---sc3-----sc4----M4---R1---M5---sc5
 \                         \  /         /
  T1-------------------M2---M3--------R2
   \               \  /
    F---fc1---fc2---M1
一些注释:
S
是此场景中的主要分支,
T1
是从
S
提取的团队分支,
F
是从
T1
提取的我的功能分支

我已经设置了自动合并,因此当提交到
T1
分支时,它们将通过连续集成运行,然后自动合并到
S
T1
分支中有一个文件与另一个团队成员提交的
S
存在合并冲突,因此我决定在完成
F
的工作后修复该问题

我将
T1
合并到
F
M1
),然后将
F
合并到
T1
M2
)。考虑到我过去遇到的合并冲突解决方案不符合我的预期的问题,我想我应该尝试一些新方法:只将
S
中的冲突文件合并到
T1
,解决那里的合并冲突,从合并中删除所有其他文件,然后允许连续集成将所有内容合并到
S

我在未提交的情况下启动了从
S
T1
M3
)的合并,解决了冲突,从合并中删除了其他(~200)个文件,然后提交。这将自动合并到
S
M4

我立刻注意到,排除这些约200个文件似乎已经完全消除了更改,这相当于两个团队大约一个月的工作量。我(错误地)认为最好的做法是迅速行动,在我的错误进入其他任何人的本地回购之前,恢复合并提交
M4
M3
。我首先还原了
M4
R1
),提交后,我还原了
M3
R2
)。我认为这是一个正确的顺序,因为我不确定当自动合并开始时,另一种方式是否会带来问题。最后,
R2
被提交并自动合并到
S
M5

这解决了所有其他人的更改都被删除的问题,但是我在
F
中的所有更改以及最初存在合并冲突的文件都从
s
中删除了。我能够将单个文件的更改直接提交到
s
sc5
),但是
F
中的更改要复杂得多。它们仍然生活在
T1
中,但由于它们是作为
R1
的一部分从
S
还原的,所以我不能将它们重新提交

我花了一天的大部分时间试图找出如何最好地将这些更改升级到
S
,但是
git-rebase
git-cherry-pick
似乎不能满足我的需要,尽管我很清楚我在这方面可能是错的。如果任何比我更擅长Git的人能提出至少一个起点,那将是令人惊讶的。谢谢

编辑:从图表中删除无用/混乱的点<由于我试图用
M3
解决合并冲突,code>M2在
S
之前未自动合并

编辑2:在阅读了torek的精彩解释后,我开始尝试重新基址。我忘记了在
F
的历史中,我多次将
T1
分支合并到
F
分支中,因为该功能分支跨越了多少时间。这意味着有很多很多合并冲突需要解决

在torek对此的回应中,我尝试了合并挤压。我最初的想法是,我需要将新分支从合并挤压合并到
T1
分支,然后将
T1
分支合并到
S
,但我遇到了同样的问题,它没有看到更改。我认为这是因为更改已经存在于
T1
中,所以它基本上只是将相同的、先前还原的更改反馈回
S
,而
S不需要这些更改


编辑3:多亏了torek非常详细的解释(非常感谢!),我将完成合并挤压,然后在解决冲突后将其结果合并到
S
分支。

这相当长,所以请随意跳过您已经知道的部分(或者一直滚动到最后)。每个部分都有设置信息,在后面的部分中解释正在发生的事情或我们正在做的事情

y型钻头简介 首先,让我以我喜欢的方式重新绘制此图(我认为这是一种局部图,但它包含我们需要的关键提交):

S0--sc1---sc2---sc3-----sc4----M4---R1---M5---sc5   <-- branch-S
  \                        \  /         /
   T0-------------o----M2---M3--------R2   <---- branch-T1
    \              \  /
    F0--fc1---fc2---M1   <------------------- branch-F
其他合并是由软件进行的,只有在没有冲突时才会发生。
R
提交是从运行开始的:

git checkout <branch>
git revert -m 1 <hash ID of some M commit>
在这里,我们需要提到Git的索引。该索引最好描述为Git构建下一次提交的位置。它最初包含当前提交中保存的每个文件(此处
C
):签出此提交,用提交
C
中的文件填充索引和工作树。名称
master
指向此提交,名称
HEAD
附加到名称
master

然后修改工作树中的文件,使用
git add
将它们复制回索引中,如果需要,使用
git add
将新文件复制到索引中,然后运行
git commit
。通过将这些索引副本冻结到快照中来进行新的提交。git然后添加快照元数据您的姓名和电子邮件、日志m以及当前提交的哈希ID,以便新提交指向现有提交。结果是:git checkout <branch> git revert -m 1 <hash ID of some M commit>
A--B--C   <-- master (HEAD)
A--B--C   <-- master (HEAD)
       \
        D
A--B--C--D   <-- master (HEAD)
          o--...--L   <-- mainline (HEAD)
         /
...--o--*
         \
          o--...--R   <-- feature
git diff --find-renames <hash-of-*> <hash-of-L>   # what we changed
git diff --find-renames <hash-of-*> <hash-of-R>   # what they changed
          o--...--L
         /         \
...--o--*           M   <-- mainline (HEAD)
         \         /
          o--...--R   <-- feature
 ...--sc4----M4---R1
         \  /
...--M2---M3--------R2
T0-------------o   <-- branch-T1
 \
 F0--fc1---fc2   <--- branch-F (HEAD)
T0-------------o   <-- branch-T1
 \              \
 F0--fc1---fc2---M1   <--- branch-F (HEAD)
T0-------------o----M2   <-- branch-T1 (HEAD)
 \              \  /
 F0--fc1---fc2---M1   <--- branch-F
git checkout branch-T1
git merge branch-S
S0--sc1---sc2---sc3-----sc4   <-- branch-S
  \
   T0-------------o----M2   <-- branch-T1 (HEAD)
    \              \  /
    F0--fc1---fc2---M1   <-- branch-F
git checkout branch-S
git merge --no-ff branch-T1
S0--sc1---sc2---sc3-----sc4----M4   <-- branch-S
  \                        \  /
   T0-------------o----M2---M3   <-- branch-T1
    \              \  /
    F0--fc1---fc2---M1   <-- branch-F
git diff <hash-of-sc4> <hash-of-R1>
T0   <-- revised-F (HEAD)
 \
  F0--fc1--fc2--M1   <-- branch-F
T0-----....
 \
  F0--fc1--fc2--M1   <-- branch-F, revised-F (HEAD)
git checkout -b revised-F <hash of T0>   # for merge --squash method
git checkout -b revised-f branch-F^1      # for rebase -f method
git merge --squash branch-F
   fc1--fc2--M1   <-- branch-F
  /
F0-------------F3   <-- revised-F (HEAD)
git rebase -f <hash-of-T0>
  F0'-fc1'-fc2'   <-- revised-F (HEAD)
 /
T0-----....
 \
  F0--fc1--fc2--M1   <-- branch-F
S0--sc1---sc2---sc3-----sc4----M4---R1---M5---sc5   <-- branch-S
  \                        \  /         /
   T0-----o-------o----M2---M3--------R2   <---- branch-T1
    \      \       \  /
    F0--fc1-o-fc2---M1   <--------------- branch-F
S0--sc1---sc2---sc3-----sc4----M4---R1---M5---sc5   <-- branch-S
  \                        \  /         /
   T0-----o-------o----M2---M3--------R2   <---- branch-T1
    \      \       \  /
    F0--fc1-o-fc2---M1   <--------------- branch-F
      \
       ---------------------------------F3   <-- revised-F
git checkout branch-T1
git merge revised-F
git merge-base --all branch-T1 revised-F
S0--sc1---sc2---sc3-----sc4----M4---R1---M5---sc5   <-- branch-S
  \                        \  /         /
   T0-----o-------o----M2---M3--------R2-----M6   <---- branch-T1
    \      \       \  /                     /
    F0--fc1-o-fc2---M1   <-- branch-F      /
      \                                   /
       ---------------------------------F3   <-- revised-F
S0--sc1---sc2---sc3-----sc4----M4---R1---M5---sc5   <-- branch-S
  \                        \  /         /
   T0-----o-------o----M2---M3--------R2   <---- branch-T1
    \      \       \  /
    F0--fc1-o-fc2---M1   <--------------- branch-F
      \
       ---------------------------------F3   <-- revised-F
S0--sc1---sc2---sc3-----sc4----M4---R1---M5---sc5----M6   <-- branch-S
  \                        \  /         /           /
   T0-----o-------o----M2---M3--------R2   <------ / -- branch-T1
    \      \       \  /                           /
    F0--fc1-o-fc2---M1   <-- branch-F            /
      \                                         /
       ---------------------------------------F3   <-- revised-F
git merge-base --all branch-S revised-F