如何查看两次提交之间的更改而中间没有提交?


641

您如何git diff只显示两次提交之间的差异,而不包括两者之间的其他提交?


15
“ git diff” 总是显示两次提交(或提交和工作目录等)之间的差异。
2009年

21
@JakubNarębski,他正在询问如何查看一个命令引入的更改与另一提交引起的更改之间的差异。换句话说,diff的差异或interdiff。
psusi

1
如果在diff命令中添加--dirstat = files参数,您将获得一个非常漂亮的屏幕截图,其中包含更改的确切项目和文件以及更改百分比。就像这样:git的差异[提交数] [提交数] --dirstat =文件
奥斯卡·班斯·费尔南德斯

Answers:


604

您可以简单地将2次提交传递给git diff,例如:

-> git diff 0da94be  59ff30c > my.patch
-> git apply my.patch

1
这对我有用,但是现在,我如何申请my.patch其他分支机构?
nacho4d 2011年

2
@ nacho4d:git checkout other-branch && git应用my.patch && git add。&& git commit -am“邮件”
Felix

1
使用git apply vs. patch的优点是您可以包括重命名和某些特定于git的更改。我喜欢使用git format-patch和git am。
罗素

58
这个答案完全没有解决这个问题,所以我不知道为什么会有这么多的投票。OP专门询问如何不获取您给出的第一个命令,而第二个命令则与之无关。
psusi

3
这个答案完全不会回答任何问题。它运作完美。如果您从有问题的两个提交中的较晚分支分支出来,然后将此差异应用于新分支,您将看到两个提交之间的更改,而不必担心间歇性提交。
Craig Labenz 2015年

142

要求两次提交之间的差异而不包含两次之间的提交几乎没有道理。提交只是存储库内容的快照;要求两者之间的差异必然包括它们。那么问题来了,您到底在寻找什么?

正如William所建议的那样,挑选樱桃可以为您提供一次基于另一个提交的提交的增量。那是:

$ git checkout 012345
$ git cherry-pick -n abcdef
$ git diff --cached

这将提交'abcdef',将其与其直接祖先进行比较,然后将该差异应用于'012345'之上。然后显示了这一新差异-唯一的变化是上下文来自“ 012345”而不是“ abcdef”的直接祖先。当然,您可能会遇到冲突等,因此在大多数情况下,这不是一个非常有用的过程。

如果您仅对abcdef本身感兴趣,可以执行以下操作:

$ git log -u -1 abcdef

这将abcdef与它的直接祖先进行比较,通常是您想要的。

而且当然

$ git diff 012345..abcdef

给您这两个提交之间的所有差异。

这将有助于更好地了解您要实现的目标-正如我提到的那样,在两次提交之间没有区别的情况下寻求差异实际上是没有意义的。


41
我会同意,总的来说,比较两个提交没有多大意义。但是git真的很擅长不告诉您应该怎么想。假设您有两个分支,每个分支都有不同的提交,看起来它们对相同的文件集进行了相同的更改。我希望能够使用git告诉我这两个补丁是否相同,而不必相信我的眼睛。我认为这很有用。
克里斯·克莱兰

9
@ChrisCleeland,在这种情况下,interdiff实用程序会派上用场。使用git diff来获取每个提交与其直接父对象之间的差异,然后使用interdiff来比较差异。
bdonlan

3
@ ChrisCleeland,git不存储补丁。它存储文件内容。它确实有一个使用增量的压缩方案,但增量源不一定与文件的实际历史记录相关。
bdonlan

11
两次提交之间的差异(不包括其各自分支上的其他提交)非常有意义:一个提交是从另一个提交中挑选出来的,但可能会有一些细微的差异。您希望看到它们是什么,而不会被两个分支之间不同的所有其他无关的废话弄乱。
psusi

2
或者说您将master重新建立到功能分支上,并且必须解决冲突。事后比较origin/featurebranch#HEADlocal/featurebranch#HEAD可以帮助你确保你没有解决冲突的过程中渣土什么。
lefnire 16/02/26

91

要将两个git commit 12345和abcdef作为补丁进行比较,可以使用diff命令作为

diff <(git show 123456) <(git show abcdef)

8
为什么要在git中使用GNU diff?
OneOfOne 2014年

7
@OneOfOne git diff <(git show 123456) <(git show abcdef)不起作用;diff <(...) <(...)做。(我刚刚尝试过)。
Menachem 2015年

@Menachem git diff 123456 abcdef
OneOfOne 2015年

15
@OneOfOne不会做同样的事情。您所建议的将比较每个提交的,显示一个补丁。我(和@plexoos)正在做的是比较两个补丁,每个补丁都是由单独的commit引入的-换句话说,diff从两个diffs中获取输出。这涉及读取和比较两个输入流。 diff(GNU或Unix diff)可以做到,而git diff不能。有些人可能想知道为什么要这样做。我现在正在这样做,正在清理合并失败的合并。
Menachem 2015年

1
这不包括git diff中所有元数据的gnu diff吗?
joelb

61
git diff <a-commit> <another-commit> path

例:

git diff commit1 commit2 config/routes.rb

它显示了这些提交之间该文件的区别。


24

要检查完整的更改:

  git diff <commit_Id_1> <commit_Id_2>

仅检查更改/添加/删除的文件:

  git diff <commit_Id_1> <commit_Id_2> --name-only

注意:要检查diff而没有提交,则不需要放置提交ID。


20

假设您有这个

A
|
B    A0
|    |
C    D
\   /
  |
 ...

并且您要确保A与相同A0

这将达到目的:

$ git diff B A > B-A.diff
$ git diff D A0 > D-A0.diff
$ diff B-A.diff D-A0.diff

3
也可以像@plexoos的答案一样缩短为单行代码:(diff <(git diff B A) <(git diff D A0)与git show相同的结果)
pogosama

14

假设您要查看提交012345和abcdef之间的区别。以下应做您想要的:

$ git checkout 012345
$ git cherry-pick -n abcdef
$ git diff-缓存

谢谢,这是在压缩提交后检查结果的好主意。例如,您可以使用未压缩的提交检出分支,然后使用樱桃选择您的压缩的提交,以查看使用交互式rebase一切是否顺利。此外,当master进入分支之前。
akostadinov

10

那这个呢:

git diff abcdef 123456 | less

如果您想即时比较许多不同的差异,将其减少就可以了。


6

从Git 2.19开始,您可以简单地使用:

git range-diff rev1...rev2 -比较两个提交树,从它们的共同祖先开始

git range-diff rev1~..rev1 rev2~..rev2 -比较2次给定提交引入的更改


4

alias~/.bashrc文件中的设置git diff

alias gdca='git diff --cached' # diff between your staged file and the last commit
alias gdcc='git diff HEAD{,^}' # diff between your latest two commits

2

alias~/.zshrc文件中的设置git diff

alias gdf='git diff HEAD{'^',}' # diff between your recent tow commits

谢谢@罗金m


git diff HEAD~2 HEAD

在最新的第二次提交和当前之间完成更改。

HEAD 方便


1

我编写了一个脚本,该脚本显示两次提交之间的差异,在Ubuntu上效果很好。

https://gist.github.com/jacobabrahamb4/a60624d6274ece7a0bd2d141b53407bc

#!/usr/bin/env python
import sys, subprocess, os

TOOLS = ['bcompare', 'meld']

def getTool():
    for tool in TOOLS:
        try:
            out = subprocess.check_output(['which', tool]).strip()
            if tool in out:
                return tool
        except subprocess.CalledProcessError:
            pass
    return None

def printUsageAndExit():
    print 'Usage: python bdiff.py <project> <commit_one> <commit_two>'
    print 'Example: python bdiff.py <project> 0 1'
    print 'Example: python bdiff.py <project> fhejk7fe d78ewg9we'
    print 'Example: python bdiff.py <project> 0 d78ewg9we'
    sys.exit(0)

def getCommitIds(name, first, second):
    commit1 = None
    commit2 = None
    try:
        first_index = int(first) - 1
        second_index = int(second) - 1
        if int(first) < 0 or int(second) < 0:
            print "Cannot handle negative values: "
            sys.exit(0)
        logs = subprocess.check_output(['git', '-C', name, 'log', '--oneline', '--reverse']).split('\n')
        if first_index >= 0:
            commit1 = logs[first_index].split(' ')[0]
        if second_index >= 0:
            commit2 = logs[second_index].split(' ')[0]
    except ValueError:
        if first != '0':
            commit1 = first
        if second != '0':
            commit2 = second
    return commit1, commit2

def validateCommitIds(name, commit1, commit2):
    if commit1 == None and commit2 == None:
        print "Nothing to do, exit!"
        return False
    try:
        if commit1 != None:
            subprocess.check_output(['git', '-C', name, 'cat-file', '-t', commit1]).strip()
        if commit2 != None:
            subprocess.check_output(['git', '-C', name, 'cat-file', '-t', commit2]).strip()
    except subprocess.CalledProcessError:
        return False
    return True

def cleanup(commit1, commit2):
        subprocess.check_output(['rm', '-rf', '/tmp/'+(commit1 if commit1 != None else '0'), '/tmp/'+(commit2 if commit2 != None else '0')])

def checkoutCommit(name, commit):
    if commit != None:
        subprocess.check_output(['git', 'clone', name, '/tmp/'+commit])
        subprocess.check_output(['git', '-C', '/tmp/'+commit, 'checkout', commit])
    else:
        subprocess.check_output(['mkdir', '/tmp/0'])

def compare(tool, commit1, commit2):
        subprocess.check_output([tool, '/tmp/'+(commit1 if commit1 != None else '0'), '/tmp/'+(commit2 if commit2 != None else '0')])

if __name__=='__main__':
    tool = getTool()
    if tool == None:
        print "No GUI diff tools"
        sys.exit(0)
    if len(sys.argv) != 4:
        printUsageAndExit()

    name, first, second = None, 0, 0
    try:
        name, first, second = sys.argv[1], sys.argv[2], sys.argv[3]
    except IndexError:
        printUsageAndExit()

    commit1, commit2 = getCommitIds(name, first, second)

    if not validateCommitIds(name, commit1, commit2):
        sys.exit(0)

    cleanup(commit1, commit2)
    checkoutCommit(name, commit1)
    checkoutCommit(name, commit2)

    try:
        compare(tool, commit1, commit2)
    except KeyboardInterrupt:
        pass
    finally:
        cleanup(commit1, commit2)
    sys.exit(0)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.