我遇到了一个奇怪的问题,我想了解发生的事情:
我们有一个feature-a
分支,其中一位同事和我基于以下新功能分支:feature-b
和feature-a
。
在他的分支(foo.js
)中,他向feature-b
添加了行(例如:行100-120)。
在我的分支(foo.js
)中,我删除了develop
中的行(例如:第90-110行)。
他的功能已合并到develop
,然后我将我的分支重新关闭feature-a
。
没有合并冲突,但是当一切都说完了,他的台词就不再存在了。
我怀疑发生的事情是:
develop
到feature-b
我实际上希望git足够聪明,不要这样做并留下“新”行。这不是正确的假设吗?
有没有好办法检查发生了什么?
答案 0 :(得分:3)
你是正确的,根据你的描述,你应该在变基期间遇到合并冲突。要找出你是否没有以及为什么不这样做,你可以重复这个rebase,或者把它分解为commit-by-commit副本。
运行git rebase
运行重复的git cherry-pick
操作序列,或运行git am --3way
生成的修补程序的邮箱文件的一个大git format-patch
。无论哪种方式,这个复制原始提交到更新的,据称更好的提交。
请注意,所有提交都是只读的。这意味着没有什么可以改变现有的提交。您现有的提交,预先变基,不受反弹的影响!诀窍是找到原始提交。没有明显的名称可以找到它们,Git最终会(默认30天后)删除。
让我们看一个典型的例子,绘制一些提交,其哈希ID由您的各种分支名称记录。我们不知道提交的哈希ID,但无论如何它们都太笨重了,所以我们只使用大写字母:
...--D--E <-- master
\
F--G <-- develop
|\
| H--I <-- feature-a
\
J--K <-- feature-b (HEAD)
此处您处于feature-b
,并且您有两个提交J
和K
,这些提交对您的工作而言是独一无二的。您的同事在feature-a
,并且还有两次提交。你们两个共享提交G
作为你们两个开始的基础;提交G
位于分支develop
以及两个功能分支上。提交G
目前是develop
的提示提交。提交F
也在所有三个分支上; F
返回(记录哈希ID)E
,这是master
的提示。 E
指向D
,依此类推到存储库中的第一个提交。此时提交E
及更早版本在所有四个分支上。
让我们说,在提交I
中,他将这些行添加到foo.js
,以便快照H
没有行,快照I
执行。他的功能现已获得批准,因此某些存储库中的某些人具有非常相似的分支名称并且指向相同的提交,这些命令执行这两项命令:
git checkout develop
git merge feature-a # or maybe git merge --no-ff feature-a
第一个将HEAD
附加到他们的名称develop
,以便合并结果将被添加或保存在那里。如果指定了--no-ff
或者需要真正的合并,或者如果可能并且不禁止进行快进操作,则第二次执行真正的合并。为简单起见,我假设快进,结果是:
...--D--E <-- master
\
F--G
\
H--I <-- develop (HEAD), feature-a
(我已经将你的提交完全留在这里,假设它们没有被推动;它不会影响结果。)
在他们的存储库中。如果您现在运行git fetch origin
以获取他们的状态,那么您最终会在自己的存储库中找到它:
...--D--E <-- master, origin/master
\
F--G <-- develop
|\
| H--I <-- origin/develop, feature-a, origin/feature-a
\
J--K <-- feature-b (HEAD)
请注意,您的origin/*
名称是其名称的副本,更改为远程跟踪名称而非分支名称。与您的分支名称一样,这些远程跟踪名称指向 tip 提交。您的git fetch
步骤可以确保您自己的分支名称不受影响。
您现在可以运行git rebase origin/develop
或git rebase origin/feature-a
甚至(在此特定情况下)git rebase feature-a
,因为我们所关心的只是选择正确的提交 。这将:
HEAD
可从目标提交无法访问的所有(非合并)提交:J
和K
; I
; 要复制提交,Git实际上通过将提交与其父级进行比较将其转换为一组更改。因此,对于J
,Git会将G
中的快照与J
中的快照进行比较。无论你在那里做了什么改变,Git都会对当前的提交做同样的事情,然后进行新的提交。让我们调用新的提交J'
:
...--D--E <-- master, origin/master
\
F--G <-- develop
|\
| H--I <-- origin/develop, feature-a, origin/feature-a
| \
| J' <-- temporary HEAD
\
J--K <-- feature-b
Git重复K
的副本,比较J
与K
并对J'
应用相同的更改:
...--D--E <-- master, origin/master
\
F--G <-- develop
|\
| H--I <-- origin/develop, feature-a, origin/feature-a
| \
| J'-K' <-- temporary HEAD
\
J--K <-- feature-b
现在所有的提交都被复制了,rebase完成了#34;剥离标签&#34; feature-b
关闭提交K
并使其指向提交K'
:
...--D--E <-- master, origin/master
\
F--G <-- develop
|\
| H--I <-- origin/develop, feature-a, origin/feature-a
| \
| J'-K' <-- feature-b (HEAD)
\
J--K [abandoned]
现在您的提交K
没有名称,您很难找到它。
你的一个提交删除了一些行。您认为复制提交已删除太多行。现在您可以看到每个提交的复制方式,您可以:
查找原始提交的哈希ID。
这可能有点棘手。它们的名称feature-b
不再指向它们。它指向副本。但有几种选择:
ORIG_HEAD
:git rebase
将此名称设置为指向原始最终提交(上图中的K
)。但是,此名称也会被其他操作重用,因此它可能不再指向您的原始链。你可以查看一下。
Reflogs:每个分支都有一个 reflog ,还有HEAD
的分支。git reflog
。正在运行HEAD
将显示git reflog feature-b
;正在运行feature-b
会显示feature-b
的那个。 reflog会记住哪个提交分支名称用于指向,并记住至少30天。查看您的git checkout
的reflog,以便在之前找到您的提交。
重复复制操作,以便您可以看到发生了什么。
现在您已拥有原件并知道您的分支名称用于指向的位置(使用各种reflog),您可以找到所有原始提交,这些提交仍处于其原始状态。
然后你可以git cherry-pick
(作为一个独立的HEAD)在出现错误之前提交任何提交,然后使用git format-patch -1 <hash> --stdout | git am --3way
或git rebase
复制它,就像{{1}}一样那样。
虽然这应该像以前一样做(错误的)事情,但现在你有足够的细节来问一个更具体的问题:为什么在这个特定的快照上复制这个提交会导致这个特定的破坏?