有没有git-merge --dry-run选项?

994

我正在合并一个可能存在很多冲突的远程分支。我如何知道它是否会有冲突?

我没有在git-merge中看到像--dry-run这样的选项。


8
这可能会有帮助:https://dev59.com/RW025IYBdhLWcg3wwYz4 - Coeffect
1
再解释一下:我不想触碰工作树,因为我不想将其签出。如果我想查看几个分支的信息,那将需要很长时间。 - Penz
哦 - 你的意思是你不想保留目标分支的本地副本? - Ian Robinson
13
由于使用Git分支十分方便,为什么不先检出一个副本,这样就无需进行试运行了呢?你可以随后将该副本丢弃。 - Alexander Mills
3
了解一下:自2020年6月1日起,pull操作已经有了dry-run标志(但merge操作仍没有)。 - Codr
21个回答

1128

如之前所述,传入--no-commit标志,但为了避免快速向前合并提交,还要传入--no-ff,就像这样:

$ git merge --no-commit --no-ff $BRANCH
检查暂存区中的更改:
$ git diff --cached

即使是快进式合并,您也可以撤销合并:

$ git merge --abort

36
仅当合并可行时,退出状态为 0 - Ciro Santilli OurBigBook.com
2
酷!但是我该如何撤销此操作?它说“所有冲突都已解决,但您仍在合并中”。调用git checkout .不会将文件恢复到合并前的状态。 - J-bob
13
明白了,你需要运行 git merge --abort 命令来取消合并。 - J-bob
1
@JayaKrishna 不,为了提交,Git会“暂存”/缓存后续提交实际执行的更改。git diff --cached(或--staged)仅显示这些更改。 - Ti Strga
1
@losaliens:快进合并会自动提交。由于意图是在不提交合并的情况下检查合并结果,因此需要使用“--no-ff”来防止它被自动提交。(在较新版本的Git中不需要这样做。) - mipadi
显示剩余4条评论

291

我刚刚实现了一个自动查找仓库和远程之间冲突的方法。该解决方案在内存中执行合并操作,因此不会影响索引或工作树。我认为这是解决这个问题最安全的方式。以下是它的工作原理:

  1. 将远程库拉取到你的本地仓库中。例如: git fetch origin master
  2. 运行 git merge-base:git merge-base FETCH_HEAD master
  3. 运行 git merge-tree:git merge-tree mergebase master FETCH_HEAD (mergebase 是上一步 merge-base 命令输出的十六进制 id)

现在假设你想要将远程的 master 分支与本地的 master 分支合并,但你也可以使用其他分支。git merge-tree 将在内存中执行合并,并将结果打印到标准输出。搜索包含<<>> 模式的行。或者你可以将输出打印到文件中进行检查。如果你发现一行以 'changed in both' 开头,则很可能存在冲突。


29
顺便说一下,步骤2和步骤3可以合并为一个步骤,使用Linux控制台的反引号操作符,它会原地计算其内容:git merge-tree \git merge-base FETCH_HEAD master` FETCH_HEAD master`。 - jakub.g
22
在.gitconfig文件中添加别名: dry = "!f() { git merge-tree `git merge-base $2 $1` $2 $1; }; f" #检查将dev合并到master的情况:git dry dev master - Noel
13
我最喜欢的GIT命令行是:git merge-tree `git merge-base clieop master` clieop master | grep -A3 "changed in both" 简直太棒了! +100 - Rudie
8
在测试中,我发现使用“在两个分支中都更改”的grep标记会合并那些修改了同一文件但不会导致合并冲突的分支。为了仅识别实际的冲突,我发现需要使用类似于+<<<<<<< .our开头的冲突标记进行grep匹配。因此,我使用类似于grep -q '^+<* \.our$'的grep表达式。 - Guy
3
不错!这是我目前最喜欢的命令:git merge-tree $(git merge-base $1 $2) $1 $2 | sed -n '/+<<<<<<< .our/,/+>>>>>>> .their/p;/^changed in both/{n;N;N;s/^/#/mg;p}' | cdiff。它可以提取“changed in both”行以及冲突部分。 - MvG
显示剩余9条评论

116

我假设你只是想在尝试合并之前先了解一下会遇到多少麻烦...如果合并失败后重置为最后一次提交,那么这个过程就相对容易,如果这是预期的方法,我也不会感到惊讶。

话虽如此,如果你真的不想触碰工作树中现有的文件 - 你可以创建一个补丁并将其与目标分支进行测试。这样做的好处是能够清楚地显示对哪些文件进行了哪些更改 - 只需在文本编辑器中打开补丁文件即可。

git checkout -b mycrazybranch
[change some stuff...]
git add .
git commit -m "changed some stuff"
git format-patch master --stdout > crazy.patch
git checkout master
git apply crazy.patch --check
[all good! cleanup...]
rm crazy.patch

正如你所看到的,这将创建一个补丁文件,然后可以使用 --check 进行测试,查看是否有任何错误,然后删除该补丁文件。


56
成功了!如果你这样做的话,甚至不需要一个临时文件:git format-patch master --stdout | git-apply --check -这个命令可以实现相同的效果。请注意,不要改变原文意思。 - Penz
6
在我的Git版本v1.8.1.1中,git-apply不能工作。它应该改为使用 git apply - yunzen
13
对我来说这不是准确的 - git 在应用补丁时无法使用“递归”合并策略,因此存在冲突,但使用“git merge”可以正常工作。 - rescdsk
4
另一个问题是,补丁文件对于在新分支中创建的新文件会报错。 - Tahsin Turkoz

83

当发现有冲突时,您可以执行 git merge --abort 命令来中止合并操作。


42
明白,但如果你想即使没有冲突也不应用它,那么这种方法就行不通了,实际上只是“检查”一下。 - OJFord
4
使用 "--no-commit --no-ff" 选项怎么样? - Samuel Robert
22
换句话说,执行git merge other-branch --no-commit --no-ff; git merge --abort 看起来效果不错。 - seanf
我该如何查看合并操作对当前分支/工作树所做的更改? - PatS

74

总结已有的答案,检查是否存在合并冲突有两种方法:

git format-patch $(git merge-base branch1 branch2)..branch2 --stdout | git apply --3way --check -

注意,在运行上述命令时,你应该处于branch1分支。

另一种方法:

git merge --no-commit branch2
# check the return code here
git merge --abort

3
如果您将 branch1 替换为 HEAD,您的命令将适用于任何分支。 - Mark Lodato
10
实际上,你的第一个命令并没有进行正确的三方合并,因此会得到许多错误的结果。第二种方法效果更好。 - Mark Lodato
1
@MarkLodato 我也更喜欢第二种,但你可以在 git apply 中提供 --3way - okdewit

64

我对此的简单暴力解决方案是:

  1. 创建“pre-master”分支(从主分支开始)

  2. 将您想要合并的所有内容合并到此pre-master分支中。
    然后,您可以查看合并情况,而不接触主分支。

    • 将pre-master分支合并到主分支 OR
    • 将所有欲发布的分支合并到主分支中

无论如何,我会遵循@orange80的建议。


8
我喜欢@akostajti提出的解决方案,但这是另一个被低估的选项。实际上,我更喜欢采取防御性策略并创建一个新的临时分支(当然只有在预期会冲突时才会这样做,否则会过度),如果出了问题,就简单地将其删除。 - jakub.g
2
不知道这是否是一个“肮脏”的解决方案,但它确实完成了工作。我喜欢它!(Y) - sara
4
在我看来,这应该是被接受的解决方案。它快速、简单、安全、可逆、直观,并且只要在开始之前没有未提交的更改,就不会产生任何副作用。 - Bob Ray
4
这个解决方案告诉你不理解 git 如何工作。分支仅仅是指针,而你只是在创建一个多余的指针。你可能有一种糟糕的感觉,认为合并分支会对其造成影响,但实际上不会。如果有冲突,你可以随时使用 git merge --abort 命令来中止合并,如果已经合并,则可以使用 git reset --hard HEAD~1 命令来回到之前的状态,或者使用 git reset --hard origin/master 命令重置为远程主干。创建另一个分支只是给你一种安全感,但如果你了解 git 的工作原理,就会明白这种恐惧是没有根据的。如果关注的是不改变工作副本,这并不提供任何解决方案。 - Thibault D.
2
@thibault-d 请考虑一下,如果您不从一个干净的分支开始,解决方案会变得多么复杂。 如果可以快进合并,则 git merge --no-commit 不会中止合并。 如果已经合并,则 git merge --abort 无法工作。 如果您想将其编写为脚本,则很尴尬,因为 git merge 没有回复足够好的错误代码来解释不同类型的冲突。 使用新分支可以防止破损的脚本使您的存储库处于需要手动干预的状态。 当然,您什么也不会失去。 但是,否则构建更容易。 - Erik Aronesty
显示剩余2条评论

56

我为了做这件事情创建了一个别名,非常方便,操作如下:

 git config --global alias.mergetest '!f(){ git merge --no-commit --no-ff "$1"; git merge --abort; echo "Merge aborted"; };f '

现在我只是打电话

git mergetest <branchname>

查看是否存在任何冲突。


48
撤销 git 合并是如此简单,您甚至不必担心干运行:
$ git pull $REMOTE $BRANCH
# uh oh, that wasn't right
$ git reset --hard ORIG_HEAD
# all is right with the world

编辑:如下面的评论所述,如果您在工作目录或暂存区中有更改,您可能需要在执行上述操作之前将它们藏起来(否则它们将在上面的git reset之后消失)。


8
简单地检查合并是否可以快进(FF)只需要检查“git branch --contains HEAD”列表,甚至更直接的方法是使用“git merge --ff-only”。 - Brian Phillips
9
"git reset --hard" 是Git中少数没有撤销操作的删除信息命令之一,因此应该非常谨慎地使用。由于这个原因,给出-1。 - Kzqai
10
@Tchalvak,仍然可以使用reflog。 - Kissaki
4
--dry-run 不仅会“简单地检查合并是否可以快进”。它将返回与合并完全相同的输出:文件、冲突等。是否可以快进并不是真正感兴趣的,是吗? - Rudie
4
git stash; git reset --hard 怎么样?@BrianPhillips - Code Whisperer
显示剩余8条评论

27

只需将当前分支与远程分支进行比较,这将告诉您在执行pull / merge操作时会发生什么变化。

#see diff between current master and remote branch
git diff master origin/master

2
有趣的想法。我该如何查看输出并确定合并是否有效? - Tyler
10
这并不会告诉你是否会发生冲突...但它会让你大概了解如果你执行拉取/合并操作会发生什么。 - timh
16
这只会告诉您两个分支之间的区别,而不会告诉您合并的结果。这是一个重要的区别,因为合并有时会自动获取来自不同分支的更改,具体取决于它们何时提交。因此,实际上进行 diff 可能会让您认为您的某些更改将被还原,但实际上,合并过程会自动选择较新的更改而非较旧的更改。希望这样清楚易懂。 - markquezada
4
延续 @mirthlab 的评论,如果之前有人使用"ours"合并策略(或其他手动合并修复)进行了一次合并,那么diff和merge之间将会有显著的差异;此时,diff也会显示已经被视为“已合并”的差异。 - Tao

26

我很惊讶还没有人建议使用补丁。

假设您已经签出了 master 分支,现在您想要测试从 your_branch 合并到 master 的修改:

$ git diff master your_branch > your_branch.patch
$ git apply --check your_branch.patch
$ rm your_branch.patch

这应该能解决问题。

如果你遇到像……这样的错误

error: patch failed: test.txt:1
error: test.txt: patch does not apply

这意味着补丁未成功,合并将产生冲突。没有输出则表示补丁干净,您可以轻松合并分支。


请注意,这不会实际更改您的工作树(除了创建补丁文件之外,但是您可以安全地在此之后删除该文件)。引用 git-apply 文档:

--check
    Instead of applying the patch, see if the patch is applicable to the
    current working tree and/or the index file and detects errors. Turns
    off "apply".

注意:如果有比我更聪明/经验更丰富的git用户,请告诉我我是否错了,以及这种方法是否显示不同于常规合并的行为。看起来很奇怪,在这个问题存在了8年以上的时间里,没有人会提出这个看似显而易见的解决方案。



2
这个方法是这个问题的被接受答案,但评论中有一些注意事项,比如"git 无法使用'recursive'合并策略"和"补丁文件对新文件会报错"。除此之外,看起来很不错。 - neno
5
一种不需要创建临时补丁文件的简短方法是:git diff master your_branch | git apply --check - ks1322

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