Wie kann man die Änderungen zwischen zwei Commits ohne dazwischen liegende Commits sehen?


640

Wie lässt sich git diffnur der Unterschied zwischen zwei Commits anzeigen, ausgenommen die anderen dazwischen liegenden Commits?


15
"git diff" zeigt immer den Unterschied zwischen zwei Commits (oder Commit und Arbeitsverzeichnis usw.).
Jakub Narębski

21
@ JakubNarębski, er fragt, wie man den Unterschied zwischen den durch einen Befehl eingeführten Änderungen und den durch ein anderes Commit eingeführten Änderungen erkennt. Mit anderen Worten, der Unterschied zwischen Diffs oder Interdiff.
Psusi

1
Wenn Sie dem Befehl diff den Parameter --dirstat = files hinzufügen, erhalten Sie einen sehr schönen Screenshot der genauen Projekte und Dateien, die geändert werden, zusammen mit einem Änderungsprozentsatz. So: git diff [Festschreibungsnummer] [Festschreibungsnummer] --dirstat = files
Óscar Ibáñez Fernández

Antworten:


603

Sie können einfach die 2 Commits an git diff übergeben, wie:

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

1
Das hat bei mir funktioniert, aber jetzt, wie kann ich mich bei my.patcheiner anderen Branche bewerben ?
Nacho4d

2
@ nacho4d: git checkout other-branch && git wende my.patch && git add an. && git commit -am "Nachricht"
Felix Rabe

1
Der Vorteil der Verwendung von git apply gegenüber patch besteht darin, dass Sie Umbenennungen und einige andere Änderungen vornehmen können, die für git spezifisch sind. Ich benutze gerne Git Format-Patch und Git Am.
Russell

58
Diese Antwort geht überhaupt nicht auf die Frage ein, daher habe ich keine Ahnung, warum es so viele positive Stimmen gibt. Das OP fragt speziell, wie Sie NICHT den ersten Befehl erhalten sollen, den Sie geben, und der zweite hat nichts mit irgendetwas zu tun.
Psusi

3
Diese Antwort versäumt es nicht, irgendetwas zu beantworten. Es funktioniert perfekt. Wenn Sie den späteren der beiden fraglichen Commits abzweigen und diesen Unterschied auf diesen neuen Zweig anwenden, werden die Änderungen zwischen den beiden Commits ohne Kopfschmerzen bei den intermittierenden Commits angezeigt.
Craig Labenz

142

Nach dem Unterschied / zwischen / zwei Commits zu fragen, ohne die dazwischen liegenden Commits einzubeziehen, macht wenig Sinn. Commits sind nur Momentaufnahmen des Inhalts des Repositorys. Nach dem Unterschied zwischen zwei zu fragen, schließt sie notwendigerweise ein. Die Frage ist also, wonach suchen Sie wirklich?

Wie William vorgeschlagen hat, kann das Kirschernten Ihnen das Delta eines einzelnen Commits geben, das auf einem anderen basiert. Das ist:

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

Dies erfordert Commit 'abcdef', vergleicht es mit seinem unmittelbaren Vorfahren und wendet diesen Unterschied dann auf '012345' an. Dieser neue Unterschied wird dann gezeigt - die einzige Änderung ist, dass der Kontext eher von '012345' als von 'abcdefs unmittelbarem Vorfahren' stammt. Natürlich kann es zu Konflikten usw. kommen, daher ist dies in den meisten Fällen kein sehr nützlicher Prozess.

Wenn Sie nur an abcdef selbst interessiert sind, können Sie Folgendes tun:

$ git log -u -1 abcdef

Dies vergleicht abcdef mit seinem unmittelbaren Vorfahren allein und ist normalerweise das, was Sie wollen.

Und natürlich

$ git diff 012345..abcdef

gibt Ihnen alle Unterschiede zwischen diesen beiden Commits.

Es würde helfen, eine bessere Vorstellung davon zu bekommen, was Sie erreichen möchten - wie ich bereits erwähnt habe, macht es keinen Sinn, nach dem Unterschied zwischen zwei Commits zu fragen, ohne dass das dazwischen liegt.


41
Ich werde zustimmen, dass es im Allgemeinen nicht viel Sinn macht, zwei Commits zu vergleichen. Aber Git ist wirklich gut darin, dir nicht zu sagen, wie du denken sollst. Angenommen, Sie haben zwei Zweige mit jeweils unterschiedlichen Commits, die so aussehen, als würden sie dieselben Änderungen an denselben Dateigruppen vornehmen. Ich möchte mit Git sagen können, ob diese beiden Patches gleich sind, ohne meinen Augen vertrauen zu müssen. Ich denke, das ist nützlich.
Chris Cleeland

9
@ChrisCleeland, das Interdiff-Dienstprogramm kann in diesem Fall nützlich sein. Verwenden Sie git diff, um den Diff jedes Commits gegen das unmittelbare übergeordnete Element abzurufen, und verwenden Sie dann interdiff, um die Unterschiede zu vergleichen.
Bdonlan

3
@ChrisCleeland, Git speichert keine Patches. Es speichert Dateiinhalte. Es gibt ein Komprimierungsschema, das Deltas verwendet, aber die Deltaquellen sind nicht unbedingt mit dem tatsächlichen Verlauf der Dateien korreliert.
Bdonlan

11
Der Unterschied zwischen den beiden Commits, der andere Commits in ihren jeweiligen Zweigen ausschließt, ist durchaus sinnvoll: Ein Commit wurde von dem anderen ausgewählt, kann jedoch einige subtile Unterschiede aufweisen. Sie möchten sehen, was sie sind, ohne mit all dem anderen, nicht verwandten Mist überfüllt zu sein, der zwischen den beiden Zweigen unterschiedlich ist.
Psusi

2
Oder sagen Sie, Sie stützen den Master auf einen Feature-Zweig und müssen Konflikte lösen. Wenn Sie anschließend mit vergleichen origin/featurebranch#HEAD, local/featurebranch#HEADkönnen Sie sicherstellen, dass Sie während der Konfliktlösung nichts durcheinander gebracht haben.
Lefnire

91

Um zwei Git-Commits 12345 und abcdef als Patches zu vergleichen, kann der Befehl diff als verwendet werden

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

8
Warum sollten Sie GNU diff mit git verwenden?
OneOfOne

7
@OneOfOne git diff <(git show 123456) <(git show abcdef)funktioniert nicht; diff <(...) <(...)tut. (Ich habe es gerade versucht).
Menachem

@ Menachem git diff 123456 abcdef.
OneOfOne

15
@OneOfOne Das macht nicht das Gleiche. Was Sie vorgeschlagen haben, würde die Bäume jedes Commits vergleichen und einen einzelnen Patch anzeigen . Was ich (und @plexoos) tun, ist, zwei Patches zu vergleichen , die jeweils durch separate Commits eingeführt wurden - mit anderen Worten, diffdie Ausgabe von zwei diffs. Dies beinhaltet das Lesen und Vergleichen von zwei Eingabeströmen. diff(GNU oder Unix diff) kann das, während git diffes nicht geht. Einige mögen sich fragen, warum man das tun möchte. Ich bin gerade dabei, das zu tun und eine Fusion zu bereinigen, die schlecht gelaufen ist.
Menachem

1
Enthält dies nicht den Gnu-Diff aller Metadaten im Git-Diff?
Joelb

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

Beispiel:

git diff commit1 commit2 config/routes.rb

Es zeigt den Unterschied zwischen diesen Commits in dieser Datei.


24

So überprüfen Sie vollständige Änderungen:

  git diff <commit_Id_1> <commit_Id_2>

Zum Überprüfen nur der geänderten / hinzugefügten / gelöschten Dateien:

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

ANMERKUNG : Um diff ohne Commit dazwischen zu überprüfen, müssen Sie die Commit-IDs nicht eingeben.


20

Nehmen wir an, Sie haben das

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

Und Sie möchten sicherstellen, dass dies Adasselbe ist wie A0.

Dies wird den Trick machen:

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

3
Kann auch als Einzeiler gekürzt werden, genau wie die Antwort von @plexoos : diff <(git diff B A) <(git diff D A0)(gleiches Ergebnis wie bei Git Show)
Pogosama

14

Angenommen, Sie möchten den Unterschied zwischen den Commits 012345 und abcdef sehen. Folgendes sollte tun, was Sie wollen:

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

Vielen Dank, das ist eine gute Idee, um Ihr Ergebnis nach dem Squashing von Commits zu überprüfen. Zum Beispiel können Sie Ihren Zweig mit nicht gequetschten Commits auschecken und Ihren gequetschten Commit auswählen, um zu sehen, ob mit der interaktiven Rebase alles reibungslos verlief. Zusätzlich, wenn der Meister der Niederlassung vorausgegangen ist.
Akostadinov

10

Was ist damit:

git diff abcdef 123456 | less

Es ist praktisch, es auf weniger zu pfeifen, wenn Sie viele verschiedene Unterschiede im laufenden Betrieb vergleichen möchten.


6

Seit Git 2.19 können Sie einfach verwenden:

git range-diff rev1...rev2 - Vergleichen Sie zwei Commit-Bäume, beginnend mit ihrem gemeinsamen Vorfahren

oder git range-diff rev1~..rev1 rev2~..rev2 - Vergleich von Änderungen, die durch 2 gegebene Commits eingeführt wurden


4

Meine aliasEinstellungen in der ~/.bashrcDatei für 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

Meine aliasEinstellungen in der ~/.zshrcDatei für git diff:

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

Danke @Jinmiao Luo


git diff HEAD~2 HEAD

vollständiger Wechsel zwischen dem letzten 2. Commit und dem aktuellen.

HEAD ist bequem


1

Ich habe ein Skript geschrieben, das Unterschiede zwischen zwei Commits anzeigt und unter Ubuntu gut funktioniert.

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)
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.