修改合并分支后如何修改合并?

San*_*kai 2 git

我习惯于--amend --no-edit在分支或 master上使用 向我的最后一次提交添加一些小的更改(如错字或格式)。

然而,今天我第一次遇到了一个小问题。
在将该分支与 master 合并后,我修改了该分支上的提交。

                  * branch (X+) (amended)   (this is a chronological view
                  |                          since in fact there is
                  |                           only one commit here)
* master (X) <=>  * branch (X) (merged)
|                 |
|                /
|               /
+--------------/
|
Run Code Online (Sandbox Code Playgroud)

那么我怎样才能在合并的主服务器上带来这种变化(+)呢?

我尝试了一些其他合并,但这带来了一些奇怪的合并提交。
由于原始更改(X)并不复杂,我只是硬重置一些提交并一次性提交。

那么我怎样才能在合并的主服务器上带来这种变化(+)呢?
因为当我的机会更少并且无法进行硬重置时

非常感谢。

tor*_*rek 6

你说(根据评论)你跑了:

git checkout master
git merge branch
Run Code Online (Sandbox Code Playgroud)

(以该顺序)。假设这有效,有两种主要可能性:您获得了快进(这实际上不是合并),或者您获得了真正的合并。我假设你得到了一个真正的合并,其中 Git 必须在两个分支提示之间找到一个合并基础提交。这是由于在两个分支上的合并基础“提前”提交的情况下出现的(合并基础本身同时在两个分支上,而“提前”提交仅在一个或另一个上):

...--o--o--*--M1--M2   <-- master
            \
             B1--B2--B3   <-- branch
Run Code Online (Sandbox Code Playgroud)

在这里,代替 Git 的原始哈希 ID,我习惯于标记当前仅包含在分支中的提交,并标记当前仅位于名为.MnmasterBnbranch

通过运行git checkout master,你告诉混帐,使M2您的当前提交。名称此时master标识此特定提交,并且特殊名称HEAD(全部大写)现在“附加到” master

...--o--o--*--M1--M2   <-- master (HEAD)
            \
             B1--B2--B3   <-- branch
Run Code Online (Sandbox Code Playgroud)

然后,git merge branch找到 commit B3,也就是这个名字此时branch标识的提交。Git 然后自己定位合并基础提交。在这里,这是承诺*,因为这是“最近”承诺是对,或包含, masterbranch。(请注意,之前的所有提交*也在两个分支上。)

Git 将合并基础*与两个提示提交进行比较,即运行等效于:

git diff --find-renames <hash-of-*> <hash-of-M2>   # what happened on master?
git diff --find-renames <hash-of-*> <hash-of-B3>   # what happened on branch?
Run Code Online (Sandbox Code Playgroud)

然后,Git 将这两组更改组合起来:无论通过前向更改 什么M2,Git 适用于*;并且无论通过前进到 发生什么变化B3,Git适用于*. 如果在这些更改集中的某处,有重叠的更改,Git 会尝试找出如何正确组合它们。这可能会导致合并冲突(如果 Git 无法解决),或者不会(如果 Git 认为可以)。

通常,合并进展顺利,在这种情况下,Git 获取合并的结果并将它们写入工作树(您可以在其中看到它们)和 Git 的索引(Git 用于进行每个新提交)。即使合并失败,Git 仍然会将所有内容写入索引和工作树,它只是使用索引的一个额外功能,并将合并的、冲突的文件留在工作树中供您手动整理。你做这个整理,git add解决索引冲突的结果,手动运行git commit而不是让 Git 自动做。无论哪种方式,最终,您或 Git 都会根据结果进行新的提交。

新提交的特别之处在于它有两个父级。第一个父项是当前提交——在本例中是提交M2——第二个父项是另一个提交,B3在这里:

...--o--o--*--M1--M2----M3
            \          /
             B1--B2--B3   <-- branch
Run Code Online (Sandbox Code Playgroud)

然后 Git 对任何新提交、合并或不合并执行相同的操作:它将提交的哈希 ID 写入当前分支名称,即HEAD附加到的分支名称。所以现在我们知道自己发生了什么master,也可以把它画出来:

...--o--o--*--M1--M2----M3   <-- master (HEAD)
            \          /
             B1--B2--B3   <-- branch
Run Code Online (Sandbox Code Playgroud)

然后你做了一些其他的工作。现在让我们假设这没有改变任何这些提交,所以上面的图片仍然有效。最终,你跑了:

git checkout branch
Run Code Online (Sandbox Code Playgroud)

这使得名称branch指向的提交成为当前提交,并附HEAD加到branch

...--o--o--*--M1--M2----M3   <-- master
            \          /
             B1--B2--B3   <-- branch (HEAD)
Run Code Online (Sandbox Code Playgroud)

然后你运行了有问题的命令:

git commit --amend --no-edit
Run Code Online (Sandbox Code Playgroud)

对于正常的无--amend提交,Git 会在这里做的是获取您索引中的任何内容,从中进行新的提交——我们称之为提交B4——并将当前提交(B3)作为新提交的父提交,并且然后使名称branch指向B4. 结果如下所示:

...--o--o--*--M1--M2----M3   <-- master
            \          /
             B1--B2--B3--B4   <-- branch (HEAD)
Run Code Online (Sandbox Code Playgroud)

是什么--amend呢,不过,是改变的方式Git会创建新的提交B4。Git 仍然使用索引中的任何内容,但不是使B4的父成为B3,而是使B4的父成为B3的父是/是。因为B3是一个带有一个父级的普通提交,即B2Git 将B4withB2作为它的父级:

...--o--o--*--M1--M2----M3   <-- master
            \          /
             B1--B2--B3
                   \
                    B4
Run Code Online (Sandbox Code Playgroud)

——然后,为了完成提交,Git 会做与往常一样的事情:它将新提交的哈希 ID 写入当前分支,为您提供:

...--o--o--*--M1--M2----M3   <-- master
            \          /
             B1--B2--B3
                   \
                    B4   <-- branch (HEAD)
Run Code Online (Sandbox Code Playgroud)

这是您发现自己遇到的问题,但是您能做些什么呢?

对此该怎么比较复杂,这取决于您想要的结果。

您所做的合并提交M3仍然存在于您的存储库中。该合并提交挂在 commit B3。如果你还没有发布这个提交——即,没有通过运行将它提供给其他人git push,或者让他们通过运行git fetch到你的计算机从你那里获取它——那么你是唯一拥有这个提交的存储库M3。这意味着您可以丢弃 M3

git checkout master
git reset --hard HEAD~1
Run Code Online (Sandbox Code Playgroud)

git reset移动名字master到以点提交指定。在这里,名称的HEAD~1意思是查找当前提交,然后找到它的第一个父项,然后再查找一次该第一项。 commit 的第一个父级M3是 commit M2,所以这使得名称master指向 commit M2

...--o--o--*--M1--M2   <-- master (HEAD)
            \       `---.
             B1--B2--B3--M3
                   \
                    B4   <-- branch
Run Code Online (Sandbox Code Playgroud)

我们再也找不到任何名字可以找到了M3。效果就像你从来没有做过一样M3。由于 commitM3也是您可以找到 commit 的唯一方法B3,因此假装的副作用B3也消失了。所以我们现在可以把它画成:

...--o--o--*--M1--M2   <-- master (HEAD)
            \
             B1--B2--B4   <-- branch
Run Code Online (Sandbox Code Playgroud)

现在你可以重复合并了。

如果您不想重复合并,或者如果您已将合并提交推M3送到另一个 Git 存储库,您的选项会发生变化,但我不会在此处详细介绍,因为这已经足够长了。