如何在未实际合并之前测试合并?

226
有没有一种方法在不做任何更改的情况下模拟当前工作分支和主分支(master)之间的git合并(git merge)?
我在进行git合并时经常遇到冲突。是否有一种方法可以先模拟合并来避免这种情况?

2
请注意: 参见 https://dev59.com/13RB5IYBdhLWcg3wz6ad - Andre Holzner
9个回答

192

你可以使用 git merge --no-commit 命令来防止合并被实际提交,如果你不喜欢合并的结果,只需重置到原始版本。

如果你绝对不想完成合并,即使它是一个快进合并(因此根据定义没有冲突),你也可以添加 --no-ff


1
我认为 git merge --abort 不存在。也许你的意思是 git reset --merge - Mark Longair
1
不好意思,我只是忘记了与 rebase 不同的是 git merge 没有 --abort 选项。 - Amber
11
我也会加上 --no-ff。这样可以防止快进式合并的发生。 - Andy
3
@Andy的--no-ff在这里几乎是强制性的,因为--no-commit不能阻止快进更改。 - jackr
3
@Anant Anand Gupta - 这是一个不错的技巧,但应该是:git config --global alias.tm "merge --no-commit --no-ff"。 - pasx
显示剩余4条评论

177

我认为没有一种方法可以模拟合并操作前会发生什么。但是,如果你确保在执行合并操作前git status的输出为空,那么尝试合并操作是非常安全的。如果出现冲突,你可以使用以下命令立即回到之前的状态:

git reset --merge
自Git 1.7.4版本起,您也可以通过执行以下操作来中止合并过程:
git merge --abort

(正如添加该选项的提交消息所解释的那样,这是为了与git rebase --abort等命令保持一致性。)


5
@Amber的答案正好回答了问题“如何模拟合并”。在我看来,使用“--no-commit”要容易得多。 - samirahmed
21
@samirahmed: @Amber的回答更加直接,确实,尽管使用--no-commit会改变索引和工作树,这并不完全等同于"没有进行任何更改" :) 我想表达的是,当人们问这种类型的问题时,通常是因为他们不知道查看合并结果最好的方法是_尝试合并_,往往是因为他们不知道如果出现问题,回到之前的状态有多容易。 - Mark Longair
2
我不知道这是否是在较新版本的git中添加的,但在文档(1.8.4)中指出:“当存在MERGE_HEAD时,git merge --abort等同于git reset --merge”,所以无论哪个更容易记住 :) - Samuel Meacham
@SamuelMeacham:感谢您指出这一点-它是在1.7.4中引入的。我已经更新了答案。谢谢! - Mark Longair
这个建议在git 1.9.4上对我没有任何作用。 - djangofan
显示剩余2条评论

140

如果我想将一个主题分支上的更改与主分支进行比较,我觉得最简单且最安全的方法是执行以下操作:

git checkout master
git checkout -b trial_merge
git merge topic_branch

合并完成后,很容易看到来自主分支的汇总更改。

git diff master

完成后,只需删除trial_merge分支即可

git checkout master
git branch -D trial_merge

这样,主分支就永远不会改变。


8
你也可以使用 git checkout --detach 命令测试任何想要的内容。如果想要保留更改,可以使用 git checkout -b new_branch 命令创建一个新分支;如果想要放弃更改,则可以切换到任何分支(例如 git checkout master)。 - Shayan Toqraee
如果topic_branch很大(如果您首先看到此问题,这可能是情况),则diff master输出可能太大,无法通过目测判断合并是否会导致冲突。 - Crescent Fresh
1
我非常喜欢这个...安全简单。 - leo

13

这是我找到的解决方案: git merge-tree 在内存中合并并打印差异,而不会触及您的工作目录。您甚至可以在不检出分支的情况下测试分支。

获取合并差异

首先,执行以下操作以确保仓库了解所有远程分支:

$ git fetch --all

现在使用此 bash 片段查看分支 $branch 将如何合并到 $master 中:

现在使用此 bash 片段查看分支 $branch 将会如何 合并到 $master 中:

$ branch='feature'
$ git merge-tree $(git merge-base $branch master) master $branch

您的工作目录和索引不会发生任何更改。这是一次模拟合并

从输出中获取信息

输出是一个差异。 如果分支已经合并,它将为空。

要查找是否存在冲突,请使用<<<进行grep:

$ git merge-tree $(git merge-base $branch master) master $branch | fgrep '<<<'

要提取冲突差异,使用sed提取在<<<>>>之间的行:

$ git merge-tree $(git merge-base $branch master) master $branch | \
  sed -ne '/^\+<<</,/^\+>>>/ p'

特点

  • 如果一个分支已经被合并,差异将为空
  • 使用grep/sed来提取冲突信息
  • 使用origin/feature来测试你从未使用过的分支
  • 可以用来查看两个分支如何分叉

将其添加到收藏夹中

获取合并的差异:

git config --global alias.mergediff '!f(){ branch="$1" ; into="$2" ; git merge-tree $(git merge-base "$branch" "$into") "$into" "$branch" ; };f '

用法:

$ git mergediff <feature-branch> <merge-into>
$ git mergediff feature master

获取合并冲突:

git config --global alias.mergetest '!f(){ git mergediff $@ | sed -ne "/^+<<</,/^+>>>/ p" ; };f '

用法:

$ git mergetest <feature-branch> <merge-into>
$ git mergetest feature master

1
太棒了!你的“git merge-tree”语句可以检查合并是否会导致冲突,而不会干扰本地仓库的工作区。这在Git需要更新程序的环境中非常完美。 - froggythefrog

7
为什么不创建一个临时分支(git checkout -b),在那里进行测试合并呢?

3
您的建议实际上是Ian简洁地表达出来的答案 :) - tzot

5

我使用:

git merge --ff-only

根据文档:

如果当前HEAD已经是最新的或合并可以被解决为快进,则拒绝合并并以非零状态退出。

如果两个分支之间没有冲突,那么会进行快进合并,因此它不是真正的模拟。但如果存在冲突,您将会收到通知,但不会发生任何事情。

即使这两个分支之间没有冲突,如果它们指向不同的提交,或者无法进行快进合并,这个操作也会失败(正如它所说)。 - Kim

3

最近我学会了使用git merge --abort。不过,只有在合并冲突时才能使用这个命令。如果你确定不想提交,那么请使用上述其他方法。


1
以上提到的其他方法是什么?它们都提到了 git merge --abort。为了使你的回答具有未来性,你应该指定你所参考的答案是由谁编写的。 - Michael Fulton

0
我想补充一下@kolypto的出色回答(这应该是一条评论,但是我没有足够的积分)。 git merge-tree的man页面中提到,在“应避免的错误”部分:

不要查看生成的顶层树以尝试找到冲突的文件[...]冲突标记无法表示的冲突类型有很多(修改/删除、模式冲突、二进制文件在两边都发生了更改、文件/目录冲突、各种重命名冲突排列等)

所以,在git merge-tree的输出上执行fgrep '<<<'并不是确定是否存在冲突的正确方法。相反,你应该检查git merge-tree的退出代码。
对于一个成功且无冲突的合并操作,退出状态为0。当合并操作存在冲突时,退出状态为1。如果由于某种错误无法完成(或开始)合并操作,则退出状态为非0或1(且输出未指定)。
示例:
#!/bin/bash

branch='myFeatureBranch'
ret=0
mergeTest=`git merge-tree --write-tree master $branch` || ret=$?
if [ ! $ret -eq 0 ];then
   echo "Merge would result in conflicts:"
   echo "$mergeTest" | grep CONFLICT
   ## Do something useful here
   exit 1
fi

-1

我不确定这是否适用于您的情况,但是您的问题让我想起有时我会开始一个功能,然后在几天内提交并多次将其合并到开发分支上。

在这一点上,我失去了对我更改的确切文件的控制,只有当我的功能关闭并且我的代码进入开发分支时才会知道。

在这种情况下,了解您所做的修改(而不是来自合并的其他修改)的好方法是使用Sourcetree。

您必须右键单击基本分支并选择Diff Against Current

Sourcetree's feature to know the diff between two branches

然后,Sourcetree 将会展示所有的修改,如果你将你的分支合并到基础分支中,这些修改将会被合并。

Results

当然,它不会显示冲突,但在合并中是一个有用的工具。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接