我习惯使用--amend --no-edit
在分支或主服务器上添加一些小的更改(如拼写错误或格式化)到上次提交。
然而,今天我第一次遇到一个小问题 在将该分支与master合并后,我在分支上修改了一个提交。
* branch (X+) (amended) (this is a chronological view
| since in fact there is
| only one commit here)
* master (X) <=> * branch (X) (merged)
| |
| /
| /
+--------------/
|
那么我怎样才能在合并后的主人身上带来变化(+)?
我尝试了其他一些合并,但这带来了一些奇怪的合并提交 由于原始更改(X)并不复杂,我只需要重置一些提交并一次性提交它们。
那么我怎样才能在合并的主人身上带来变化(+)? 因为我的机会较少,而且无法进行硬重置
非常感谢。
答案 0 :(得分:1)
你说(根据评论)你跑了:
git checkout master git merge branch
(按此顺序)。假设这有效,有两种主要可能性:你有一个快进(实际上并不是合并),或者你有一个真正的合并。我假设您有一个真正的合并,其中Git必须在两个分支提示之间找到合并基础提交。这是因为在#34;之前有提交的情况。两个分支上的合并基础(合并基础本身同时在两个分支上,而&#34;提前#34;提交仅在一个或另一个上):
...--o--o--*--M1--M2 <-- master
\
B1--B2--B3 <-- branch
此处,我使用Mn
来标记当前仅包含在master
分支中的提交,而不是Git的原始哈希ID,而Bn
标记当前仅在名为branch
的分支上的提交。
通过运行git checkout master
,您告诉Git将M2
作为当前提交。名称master
此时标识此特定提交,特殊名称HEAD
(全部为大写)现在&#34;附加到&#34; master
:
...--o--o--*--M1--M2 <-- master (HEAD)
\
B1--B2--B3 <-- branch
然后,git merge branch
找到提交B3
,这是branch
此时标识的提交。然后Git自己定位 merge base 提交。在这里,提交*
,因为那是&#34;最近的&#34;提交 master
和branch
*
和*
。 (请注意,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?
之前的所有提交也都在两个分支上。)
Git将合并基础M2
与两个提示提交进行比较,即运行相当于:
*
然后Git将这两组更改结合起来:无论通过向前推进到B3
而改变了什么,Git都适用于*
;通过前往git add
而改变的任何内容,Git 也适用于git commit
。如果在这些更改集中的某处,存在重叠更改,Git会尝试找出如何正确组合它们。这可能会导致合并冲突(如果Git无法解决),或者不会(如果Git认为可以)。
通常情况下,合并进展顺利,在这种情况下,Git会将合并后的结果写入工作树(您可以看到它们)和Git的索引 (Git使用它来进行每次新提交)。即使合并失败,Git仍会将所有内容写入索引和工作树,它只使用索引的额外功能,并在工作树中保留组合的冲突文件,以便您手动进行排序。您执行此排序,M2
结果以解决索引冲突,并手动运行B3
而不是让Git自动执行。无论哪种方式,最后,您或Git都会从结果中进行新的提交。
新提交的特殊之处在于它有两个父项。第一个父项是当前提交 - 在这种情况下,提交...--o--o--*--M1--M2----M3
\ /
B1--B2--B3 <-- branch
- 第二个父项是另一个提交,这里是HEAD
:
master
然后Git做了与任何新提交,合并或不合并一样的事情:它将新提交的哈希ID写入当前分支名称,即...--o--o--*--M1--M2----M3 <-- master (HEAD)
\ /
B1--B2--B3 <-- branch
所附加的名称。所以现在我们知道git checkout branch
本身发生了什么,也可以把它画出来:
branch
然后你做了一些其他的工作。现在让我们假设这并没有改变任何这些提交,因此上面的图片仍然有效。最后,你跑了:
HEAD
使名称branch
指向的提交成为当前提交,并将...--o--o--*--M1--M2----M3 <-- master
\ /
B1--B2--B3 <-- branch (HEAD)
附加到git commit --amend --no-edit
:
--amend
然后你运行了有问题的命令:
B4
对于正常的,没有 - B3
提交,Git在这里要做的就是立即获取索引中的任何内容,从它进行新的提交 - 调用提交{{1} - 并将新提交的当前提交(branch
)作为其父项,然后将名称B4
指向...--o--o--*--M1--M2----M3 <-- master
\ /
B1--B2--B3--B4 <-- branch (HEAD)
。结果如下:
--amend
但是,B4
的作用是更改 Git创建新提交B4
的方式。 Git仍然使用索引中的任何内容,但不是让B3
的父级为B4
,而是使B3
的父级成为B3
{1}}的父母是/。由于B2
是一个普通提交,只有一个父级,即B4
,因此Git将B2
作为其父级...--o--o--*--M1--M2----M3 <-- master
\ /
B1--B2--B3
\
B4
:
...--o--o--*--M1--M2----M3 <-- master
\ /
B1--B2--B3
\
B4 <-- branch (HEAD)
- 然后,为了完成提交,Git执行它始终做的事情:它将新提交的哈希ID写入当前分支,为您提供:
M3
做什么关于这个更复杂,取决于你想要的结果。
您所做的合并提交B3
仍存在于您的存储库中。合并提交会挂起以提交git push
。如果您尚未发布此提交 - 即,未通过运行git fetch
将其提供给其他人,或者让他们通过M3
向您的计算机运送您那么你就是只有的存储库,它有这个提交M3
。这意味着您可以放弃 git checkout master
git reset --hard HEAD~1
:
git reset
master
将名称HEAD~1
移动到指向您指定的提交。在这里,名称M3
表示查找当前提交,然后查找其第一个父级,并执行第一次父级查找。提交M2
的第一个父级是提交master
,这样就会使名称M2
指向提交...--o--o--*--M1--M2 <-- master (HEAD)
\ `---.
B1--B2--B3--M3
\
B4 <-- branch
:
M3
我们无法找到任何名称 M3
。效果就好像你从未做过M3
一样。由于提交B3
也是您可以找到提交B3
的唯一方法,因此假装...--o--o--*--M1--M2 <-- master (HEAD)
\
B1--B2--B4 <-- branch
的副作用也已消失。所以我们现在可以把它画成:
M3
现在你正在重复合并。
如果您不希望重复合并,或者您已将合并提交{{1}}推送到另一个Git存储库,那么您的选项会更改,但我赢了&#39;在这里进一步详细说明,因为这已经足够长了。