搞砸了git merge

时间:2016-05-02 22:40:11

标签: 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。而不是在主人身上重新设置我的分支,我认为我将主人重新安排到我的分支上。 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

我真的不确定发生了什么事,如果有人能告诉我我搞砸了什么,我会很高兴。我甚至不知道谷歌的用途。

P.S。 - 当我在github.com上访问我的repo时,主人没有动过,我的分支最后一次提交为final commit on my_branch所以我刚刚搞砸了本地

1 个答案:

答案 0 :(得分:3)

好的,首先,让我们稍微回顾一下(这可以帮助你更加确定,或者更不确定)。

通常当你执行git commit -agit 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的合并,就像您对非合并一样。

虽然这里有一个很大的区别,这就是你的意思:合并提交以非空消息开始。因此当你退出编辑器时,Git会读取文件。 ..它有一个非空消息,它继续进行并进行合并提交。

现在已经太晚了,但在这种情况下停止合并提交的一个技巧是删除非空合并消息,写出一个空文件。这样Git就会看到空消息并中止合并提交。 (合并本身仍然在进行中:要停止 你需要使用git merge --abort。但这完全是另一个恢复过程,而不是我们现在需要的那个。)

另一个非常有用的事情要知道:在你推动之前,你所有的工作都是本地的。(好吧,除非你让其他人从你那里取来 - 但你不要;如果你做了,你会知道的。)

尽量避免git push -f

使用-f(强制标记)和git push取消安全检查。如果你和其他人都在同一时间推进,你们中的一个会赢,另一个会输。这是悲伤的秘诀。您的正常工作流程将无需-f:如果您设法参加其中一场比赛,您的git push将失败,您将不得不再次git fetch && git rebase,但它应该几乎无痛,并且不会强行推动,因此不会失去任何东西。

找出您现在的位置:git status

在你做任何事之前检查你的位置从来都不错。 git status命令执行此操作。使用它:它会告诉你你现在在哪个分支。

从上面可以看出,它会说On branch my_branch,但检查起来不错。它也应该说nothing to commit, working directory clean

保存合并提交

您可能希望保存刚刚进行的合并提交,因为它具有您的冲突解决方案。要将其保存在易于使用的方法中,请创建一个指向它的新分支或标记名称。 (除非你真的想要推新名称,否则不要推新名称。)

(你甚至可以在某处保存SHA-1哈希,包括将其写下来或复制粘贴到剪贴板或其他任何内容。这可以防止你意外地给出名称,但记住像19cfa3105d2...这样的长字符串是烦人的,并且是分支和标记名称的用途。此外,没有名称,只要提交保留在您的存储库中,此值仅保持有效:默认情况下,每个提交通过您的reflogs保护至少30天,但没有包含它的分支或标记名称,它最终会过期。)

让我们使用临时分支名称,因为这很简单:

$ git branch saved-merge HEAD

这使得新分支saved-merge的分支提示是当前(HEAD)提交。我们可以省略HEAD,但这只是明确的。

摆脱合并

如果你真的不想要这个合并 - 如果你想回到正常的工作流程,通过从当前分支中删除这个提交 - git命令将其从分支中移除git reset。 / p>

git reset命令做了太多事情,这使得解释起来很复杂。但是,在这种特殊情况下,我们将使用它同时做两件事:

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

这意味着在我们执行此操作之前,您应该非常确定工作树中没有任何有价值的东西。换句话说,运行git status并仔细检查结果。你可能只是做了,但再做一次,它对你有好处。 :-)说真的,偶尔反身git status是一个好习惯。例如,通过告诉我我仍然处于被遗忘的变形或合并的中间,它已经使我不必多次恢复。在糟糕的旧版本 - 1.5天内,git status错过了很多;现在好多了。

然后:

$ git reset --hard HEAD^

将删除合并。 (在某些shell中,^字符是特殊的,备用拼写HEAD~1更易于使用。)

如何运作

首先,让我们快速浏览HEAD^(又名HEAD~1)。

名称HEAD 始终是指当前提交和/或当前分支。需要分支而不是提交的命令使用分支部分;像git reset这样需要两者的命令,将它们用于两者。

git reset命令重置您的HEAD。暗示了这个特定的HEAD:如果您说git reset 1234567它仍会重置HEAD,而是重置为1234567。在我们的例子中,我们说git reset HEAD^,所以它会重置HEAD,但它重置的是HEAD^

^后缀是个不错的小技巧。它做的是接受任何提交 - 像1234567这样的原始数字,或者像HEADmy_branch这样的名称 - 并找到基础提交,然后返回一个提交来自有

特别是,后面没有任何其他内容的^后缀会返回第一个父。对于普通提交,这是唯一的父母,并且是"之前"州。对于像这样的合并提交,第一个父级仍然是"之前的#34;州;那里只有一个第二个父母,这就是合并的东西。

(旁白:您可以重复^以进一步退回。名称HEAD^^返回两次提交,HEAD^^^返回3,依此类推。{{1} }是这方面的简写:~表示HEAD~3。由于HEAD^^^中有一个^HEAD^只是HEAD~1的同义词。事实上,你也可以不用HEAD^。我避免这样做,因为有一个1符号,它意味着"第二个父母",所以我试着坚持使用未编号的HEAD^2并编号^,以避免使用错误的。{/ p>

~告诉--hard重新设置索引/登台区域和工作树,同时还要更改分支git reset指示的内容。因此,当我们将这些全部放在一起时,HEAD

  1. 找到当前提交的父提交(git reset --hard HEAD^)(^);
  2. 更改当前分支(HEAD的内置操作的一部分)以匹配步骤1中找到的提交;
  3. 重新设置索引/暂存区域(由于reset);和
  4. 重新设置工作树(也由于--hard)。
  5. 请注意,我们需要完成一次,因为如果我们再次这样做,那将会重新设置另一个提交。 (我们可以使用reflogs,或者我们保存合并提交ID的事实来恢复。基本上,一旦你提交,你几乎总能让事情恢复至少30天,这就是为什么有些人会使用这句话&#34 ;提前和经常提交#.;)

    现在你又回到了你想去的地方......

    现在您可以使用--hard,就像通常的工作流程一样。

    这很可能会发生合并冲突。

    您已在合并提交中解决了这些合并冲突。我们可以使用这些决议! (现在你知道我们为什么要保存了它。)

    git rebase -i输出中,您显示了两次提交。您可能只在一个上遇到合并冲突,或者您可能会在两者上发生合并冲突。如果你很幸运,他们只会发生一次,保存的合并结果就是你想要的那个文件和/或案例。

    (如果你运气不好,你可能想要或者需要重新进行多次合并,在这种情况下你可能无法重复使用已经完成的工作,但是你说你通常会把所有东西都压下来,即,最后你要求只有一个最终提交的东西,而不是一些东西的提交,然后是第二次提交剩下的东西,例如。)

    所以,让我们说你执行git log并将除rebase -i之外的所有内容更改为pick并让其运行。 Git将尝试挑选并组合所有提交并将它们应用到上游分支的顶端。其中一些可能会遇到合并冲突,但最后,您需要先前做出的解决方案。所以我们可以作弊,即使有重复的合并冲突,我们也可以采取已知的最终结果,如下所示:

    squash

    在这里要注意的一件事是,您有时可以通过这种方式进行重复更改。仔细检查最终结果。你应该检查最终结果,即使你看到合并冲突,因为Git不聪明,有时会复制你不希望它的东西,或者自动合并错误。< / p>

    (自动化测试很好,因为他们倾向于捕捉这些案例。)

    完成所有操作并对结果完全满意后,使用$ 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 删除临时保存的分支。