繁体   English   中英

搞砸了git merge

[英]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 -agit 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 fetchgit 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 cleannothing 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命令执行的操作太多,这使得解释变得很复杂。 但是,在这种特殊情况下,我们将使用它一次执行两项操作:

  • 更改当前分支名称指向的提交ID,并
  • 清除工作树并使它与新的提交ID匹配。

这意味着在执行此操作之前,您应该非常确定工作树中没有任何有价值的东西。 换句话说,运行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类的原始数字,或HEADmy_branch类的名称)并找到基础提交,然后从那里返回一个提交

特别是,后缀^不带其他内容,可以返回到第一个父元素 对于普通提交,这是唯一的父级,并且是“之前”状态。 对于像这样的合并提交,第一个父级仍处于“之前”状态; 也只有第二个父母,这是合并的东西。

(旁白:你可以重复^进一步退一步名称。 HEAD^^追溯到两次提交, HEAD^^^追溯到3,等上了。 ~是这个简写: HEAD~3HEAD^^^由于有一个^HEAD^HEAD~1仅仅是一个同义词HEAD^ 。事实上,你可以不指定1为好。我避免这种情况,因为有一个HEAD^2的符号以及和它意味着“第二个父级”,因此我尽量使用未编号的^和编号的~ ,以避免使用错误的编号。)

--hard告诉git reset重新设置索引/临时区域和工作树,同时还更改HEAD指示的分支。 因此,当我们将所有这些放在一起时, git reset --hard HEAD^

  1. 查找当前提交( HEAD )的父提交( ^ );
  2. 更改当前分支(内置的reset动作的一部分)以匹配在步骤1中找到的提交;
  3. --hard索引/临时区域(由于--hard );
  4. 重新设置工作树(也归因于--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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM