[英]Messed up git merge
所以我有一个git repo具有以下结构:
.DS_Store
.git
.gitignore
README.md
folder1/
folder2/
folder1
是我的工作对象,无需理会folder2
。 我分支master并开始处理folder1
的文件。 另一个开发人员还处理folder1
文件。 然后,他继续进行并合并他的更改。 现在轮到我合并更改了。 我典型的git merge
工作流程如下:
git fetch
git rebase -i origin/master
squash commits
git push -f my_branch
但是,在这种情况下,我继续前进,并在分支中进行了git pull,这引起了一些冲突。 使用git diff
我解决了冲突。 然后像白痴一样继续执行git commit -a
,它在提交屏幕中将folder2
中的文件显示为修改后的文件。 因为我意识到有些问题,所以我中止了提交。 我得到的不是通常的“未输入提交消息,提交中止”消息,而是: Merge branch 'master' of https://github.com/comapny/my_repo into my_branch
。 与其将我的分支重新置于master之上,不如我将master重新建立到我的分支上。 git log
显示如下:
ME:
Merge branch 'master' of https://github.com/company/my_repo into my branch
ME:
final commit on my_branch
Other dev:
His merge
Other dev:
His merge
Other dev:
His merge
Other dev:
His merge
Other dev:
His merge
ME:
2nd commit on my_branch
ME:
1st commit on my_branch
我真的不确定发生了什么,如果有人能告诉我我搞砸了的话,Id会很棒。 我什至不知道谷歌要做什么。
PS -当我访问我的repo
上github.com大师是不动,我的分支有最后一次提交的final commit on my_branch
所以我刚搞砸本地
好吧,首先,让我们回顾一下(这是为了帮助您感到更加确定,或者无论如何不确定)。
通常,当您执行git commit -a
或git add foo; git add bar; git commit
git add foo; git add bar; git commit
git add foo; git add bar; git commit
,Git会启动您在原本完全为空的提交(或更确切地说,由空白行和一些删除的注释组成的提交)上配置的任何编辑器。 然后,如果不编写消息就退出编辑器,则会得到:
Aborting commit due to empty commit message.
不过,在这种情况下,您正处于git merge
的中间(因为git pull
只是git fetch
和git merge
)。 合并中存在一些冲突,但您已解决了它们。 解决冲突后,您可以使用git commit
commit进行合并,就像处理非合并一样。
但是,这里有一个很大的不同,那就是您要注意的一点: 合并提交以非空消息开始。 因此,当您退出编辑器时,Git会读取文件...,其中包含非空消息,然后继续进行合并提交。
现在为时已晚,但是在这种情况下停止合并提交的一个技巧是删除非空合并消息 ,写出一个空文件。 这样,Git看到空消息并中止合并提交。 (不过,合并本身仍在进行中:要停止该操作,您将需要使用git merge --abort
。但这完全是另一种恢复过程,而不是我们现在需要的过程。)
还有一件非常有用的事情要知道: 在您推动之前,所有工作都是本地的。 (好吧,除非您让别人从您这里获取东西,但您不会这么做;如果您这样做了,您就会知道的。)
git push -f
将-f
(强制标志)与git push
一起使用git push
安全检查。 如果您和其他人都在同时推动,则其中一个将获胜而另一个将失去。 这是悲伤的秘诀。 正常的工作流程将避免使用-f
:如果您成功打了其中一场比赛,则git push
将会失败,并且您将不得不再次git fetch && git rebase
,但这应该几乎没有痛苦,并且不会强制执行推动,因此不会丢失任何东西。
git status
在您做任何事情之前,先检查一下自己在什么地方都不错。 git status
命令执行此操作。 使用它:它将告诉您您现在在哪个分支上。
从上面可以看出,它会说On branch my_branch
,但是检查一下很好。 它也应该说nothing to commit, working directory clean
也nothing to commit, working directory clean
,可能nothing to commit, working directory clean
。
您可能要保存刚才所做的合并提交,因为它具有解决冲突的功能。 要将其保存为易于使用的方法,请指向它创建一个新的分支或标记名称。 (请小心不要推送新名称,除非您确实希望推送新名称。)
(您甚至可以将SHA-1散列保存在某个地方,包括将其记下来或将其复制粘贴到剪贴板等。这可以防止您不小心给出名称,但记住19cfa3105d2...
类的长字符串19cfa3105d2...
烦人,并且是分支和标记名称的用途。此外,没有名称的情况下,只要提交保留在您的存储库中,此值就保持有效:默认值是每次提交都通过reflog受到至少30天的保护,但是没有包含它的分支或标签名称,它最终将过期。)
让我们使用一个临时分支名称,因为这很简单:
$ git branch saved-merge HEAD
这将使新分支saved-merge
其分支提示是当前( HEAD
)提交。 我们可以省略HEAD
但这只是明确的。
如果您确实不想进行合并(如果您想返回到正常的工作流程,请从当前分支中删除此提交),则将git命令从分支中删除它是git reset
。
git reset
命令执行的操作太多,这使得解释变得很复杂。 但是,在这种特殊情况下,我们将使用它一次执行两项操作:
这意味着在执行此操作之前,您应该非常确定工作树中没有任何有价值的东西。 换句话说,运行git status
并仔细检查结果。 您可能只是做了,但是再做一次,对您有好处。 :-)认真地讲,偶尔的反身git status
是一个好习惯。 例如,它告诉我我仍处于被遗忘的基础或合并的中间,这使我免于不得不多次恢复。 在较旧的1.5版中, git status
错过了很多; 现在好多了。
然后:
$ git reset --hard HEAD^
将删除合并。 (在某些外壳中, ^
字符是特殊字符,备用拼写HEAD~1
更易于使用。)
首先,让我们快速看一下HEAD^
(aka HEAD~1
)。
名称HEAD
始终是指当前提交和/或当前分支。 需要分支而不是提交的命令使用分支部分。 像git reset
这样的命令都需要同时使用。
git reset
命令重置您的HEAD。 暗含了这个特定的HEAD:如果您说git reset 1234567
它仍然会重置HEAD
,但是改为1234567
。 在我们的例子中,我们说git reset HEAD^
,所以它重置的头,但它重置是HEAD^
。
该^
后缀是个不错的技巧。 它的作用是接受所有提交(例如1234567
类的原始数字,或HEAD
或my_branch
类的名称)并找到基础提交,然后从那里返回一个提交 。
特别是,后缀^
不带其他内容,可以返回到第一个父元素 。 对于普通提交,这是唯一的父级,并且是“之前”状态。 对于像这样的合并提交,第一个父级仍处于“之前”状态; 也只有第二个父母,这是合并的东西。
(旁白:你可以重复^
进一步退一步名称。 HEAD^^
追溯到两次提交, HEAD^^^
追溯到3,等上了。 ~
是这个简写: HEAD~3
指HEAD^^^
由于有一个^
在HEAD^
, HEAD~1
仅仅是一个同义词HEAD^
。事实上,你可以不指定1
为好。我避免这种情况,因为有一个HEAD^2
的符号以及和它意味着“第二个父级”,因此我尽量使用未编号的^
和编号的~
,以避免使用错误的编号。)
--hard
告诉git reset
重新设置索引/临时区域和工作树,同时还更改HEAD
指示的分支。 因此,当我们将所有这些放在一起时, git reset --hard HEAD^
:
HEAD
)的父提交( ^
); reset
动作的一部分)以匹配在步骤1中找到的提交; --hard
索引/临时区域(由于--hard
); 和 --hard
)。 请注意,我们只需要执行一次此操作,因为如果再次执行此操作,则会重新设置另一个提交。 (我们可以使用reflog或保存了合并提交ID的事实进行恢复。基本上,一旦提交,您几乎总是可以将至少30天的时间取回,这就是为什么有些人使用“提早提交”的原因。并且经常”。)
现在,您可以像平常的工作流程一样使用git rebase -i
。
这很可能会导致合并冲突。
您已经在合并提交中解决了这些合并冲突。 我们可以使用这些决议! (现在,您知道我们为什么保存它了。)
在git log
输出中,您显示了两次提交。 您可能只会在一个上出现合并冲突,或者您可能会在两个上出现合并冲突。 如果幸运的话,它们只会发生一次,并且保存的合并结果就是您想要的那个文件和/或案例的结果。
(如果您不走运,则可能需要或需要重新进行多次合并,在这种情况下,您可能无法重新使用已经完成的工作,但是您通常说无论如何都要压缩所有内容,例如最后,您要求对所有内容进行最后一次提交,例如,不要对某些东西进行一次提交,然后对其余部分进行第二次提交。)
因此,假设您进行了rebase -i
更改,但将除第一个pick
所有内容都更改为squash
并使其运行。 Git将尝试挑选并合并所有提交,并将它们应用于上游分支的尖端。 其中一些可能会遇到合并冲突,但是最后,您需要您之前制定的解决方案。 因此,我们可以作弊,即使重复出现合并冲突,我们也可以采用已知的最终结果,如下所示:
$ git rebase -i
[edit, write, exit editor]
... rebase begins ...
... conflict occurs on files folder1/foo and folder2/bar ...
$ git checkout saved-merge -- folder1/foo folder2/bar
$ git rebase --continue
... another conflict, on just folder2/bar this time maybe ...
$ git checkout saved-merge -- folder2/bar
$ git rebase --continue
这里要注意的一件事是,有时您可以通过这种方式重复进行更改。 仔细检查最终结果。 但是,即使您没有看到合并冲突,也应该检查最终结果,因为Git不聪明,有时会复制您不希望发生的事情,或者自动合并错误。
(自动测试很好,因为它们往往会抓住这些情况。)
完成所有操作并完全满意结果后,请使用git branch -D
删除临时保存的分支。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.