Come vedere le modifiche tra due commit senza commit in mezzo?


643

Come fai a git diffmostrare solo la differenza tra due commit, esclusi gli altri commit nel mezzo?


15
"git diff" mostra sempre la differenza tra due commit (o commit e directory di lavoro, ecc.).
Jakub Narębski,

21
@ JakubNarębski, sta chiedendo come vedere la differenza tra le modifiche introdotte da un comando e le modifiche introdotte da un altro commit. In altre parole, il diff di diff o interdiff.
psusi,

1
e se aggiungi il parametro --dirstat = files al comando diff, otterrai uno screenshot molto bello sugli esatti progetti e file che vengono modificati, insieme a una percentuale di modifica. In questo modo: git diff [commit-number] [commit-number] --dirstat = files
Óscar Ibáñez Fernández

Risposte:


606

puoi semplicemente passare i 2 commit a git diff come:

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

1
Ha funzionato per me, ma ora, come posso fare domanda my.patchper un altro ramo?
nacho4d,

2
@ nacho4d: git checkout altro ramo && git applica my.patch && git add. && git commit -am "Messaggio"
Felix Rabe

1
Il vantaggio di usare git apply contro patch è che puoi includere rinominazioni e alcune altre modifiche specifiche di git. Mi piace usare git format-patch e git am.
Russell,

58
Questa risposta non riesce assolutamente a rispondere alla domanda, quindi non ho idea del perché abbia così tanti voti positivi. L'OP chiede specificamente come NON ottenere il primo comando che dai, e il secondo non ha nulla a che fare con nulla.
psusi,

3
Questa risposta non manca assolutamente di rispondere a nulla. Funziona perfettamente. Se si ramifica il secondo dei due commit in questione, quindi si applica questo diff a quel nuovo ramo, vedrai le modifiche tra i due commit senza mal di testa dei commit intermittenti.
Craig Labenz,

142

Chiedere la differenza / tra / due commit senza includere i commit tra i due ha poco senso. I commit sono solo istantanee del contenuto del repository; chiedere la differenza tra due li include necessariamente. Quindi la domanda è: cosa stai davvero cercando?

Come ha suggerito William, la raccolta delle ciliegie può darti il ​​delta di un singolo commit riproposto su un altro. Questo è:

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

Questo prende commit 'abcdef', lo confronta con il suo antenato immediato, quindi applica quella differenza sopra '012345'. Questa nuova differenza viene quindi mostrata: l'unico cambiamento è che il contesto deriva da "012345" anziché dall'antenato immediato di "abcdef". Certo, potresti avere conflitti ed ecc., Quindi nella maggior parte dei casi non è un processo molto utile.

Se sei solo interessato a abcdef stesso, puoi fare:

$ git log -u -1 abcdef

Questo confronta abcdef al suo antenato immediato, da solo, ed è di solito quello che vuoi.

E naturalmente

$ git diff 012345..abcdef

ti dà tutte le differenze tra questi due commit.

Sarebbe utile avere un'idea migliore di ciò che stai cercando di ottenere - come ho già detto, chiedere la differenza tra due commit senza ciò che sta nel mezzo non ha senso.


41
Concordo sul fatto che, in generale, non ha molto senso confrontare due commit. Ma Git è davvero bravo a non dirti come dovresti pensare. Supponiamo di avere due rami, ognuno con commit distinti che sembrano apportare le stesse modifiche agli stessi set di file. Vorrei poter usare Git per dirmi se queste due patch sono uguali senza dovermi fidare dei miei occhi. Penso che ci sia utilità in questo.
Chris Cleeland,

9
@ChrisCleeland, l'utilità interdiff può tornare utile in quel caso. Usa git diff per ottenere il diff di ogni commit rispetto al suo genitore immediato, quindi usa interdiff per confrontare i diff.
bdonlan,

3
@ChrisCleeland, git non memorizza le patch. Memorizza il contenuto del file. Ha uno schema di compressione che utilizza delta, ma le fonti delta non sono necessariamente correlate alla cronologia effettiva dei file.
bdonlan,

11
La differenza tra i due commit escludendo altri commit sui rispettivi rami ha perfettamente senso: un commit è stato scelto dall'altro, ma potrebbe avere alcune sottili differenze. Volete vedere cosa sono senza essere ingombra di tutte le altre stronzate non correlate che sono diverse tra i due rami.
psusi,

2
Oppure dì di reimpostare il master su un ramo di funzionalità e devi risolvere i conflitti. Successivamente il confronto origin/featurebranch#HEADcon local/featurebranch#HEADpuò aiutarti a garantire di non aver rovinato nulla durante la risoluzione dei conflitti.
Lefnire,

91

Per confrontare due commit git 12345 e abcdef come patch si può usare il comando diff as

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

8
Perché dovresti usare GNU diff con git?
OneOfOne,

7
@OneOfOne git diff <(git show 123456) <(git show abcdef)non funziona; diff <(...) <(...)lo fa. (L'ho appena provato).
Menachem,

@Menachem git diff 123456 abcdef.
OneOfOne,

15
@OneOfOne Non fa la stessa cosa. Quello che hai suggerito avrebbe confrontato gli alberi di ogni commit, mostrando una singola patch . Quello che io (e @plexoos) stiamo facendo sono il confronto di due patch , ognuna delle quali è stata introdotta da commit separati - in altre parole, diffl'output da due diffsecondi. Ciò comporta la lettura e il confronto di due flussi di input. diff(GNU o Unix diff) possono farlo, mentre git diffnon possono. Alcuni potrebbero chiedersi perché si vorrebbe farlo. Sono nel mezzo di farlo proprio ora, ripulendo un'unione che è andata male.
Menachem,

1
questo non includerà lo gnu diff di tutti i metadati nel git diff?
Joelb,

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

Esempio:

git diff commit1 commit2 config/routes.rb

Mostra la differenza su quel file tra questi commit.


24

Per verificare le modifiche complete:

  git diff <commit_Id_1> <commit_Id_2>

Per controllare solo i file modificati / aggiunti / eliminati:

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

NOTA : per controllare la differenza senza commit in mezzo, non è necessario inserire gli ID di commit.


20

Diciamo che hai questo

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

E vuoi assicurarti che Asia lo stesso di A0.

Questo farà il trucco:

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

3
Può anche essere abbreviato come one-liner proprio come la risposta di @plexoos : diff <(git diff B A) <(git diff D A0)(stesso risultato come con git show)
pogosama

14

Supponiamo di voler vedere la differenza tra commit 012345 e abcdef. Quanto segue dovrebbe fare quello che vuoi:

$ git verifica 012345
$ git cherry-pick -n abcdef
$ git diff - cache

Grazie, è una buona idea controllare il tuo risultato dopo aver commesso uno schiacciamento. Ad esempio, puoi controllare il tuo ramo con commit non compressi e cherry pick il tuo commit schiacciato per vedere se tutto è andato liscio con il rebase interattivo. Inoltre, quando il master ha superato il ramo.
Akostadinov

10

Che dire di questo:

git diff abcdef 123456 | less

È utile collegarlo a meno se si desidera confrontare al volo molte differenze diverse.


6

Da Git 2.19, puoi semplicemente usare:

git range-diff rev1...rev2 - confronta due alberi di commit, a partire dal loro antenato comune

oppure git range-diff rev1~..rev1 rev2~..rev2 - confrontare le modifiche introdotte da 2 commit determinati


4

Le mie aliasimpostazioni nel ~/.bashrcfile per 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

Le mie aliasimpostazioni nel ~/.zshrcfile per git diff:

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

Grazie @Jinmiao Luo


git diff HEAD~2 HEAD

passaggio completo tra l'ultimo 2o commit e l'attuale.

HEAD è conveniente


1

Ho scritto uno script che mostra diff tra due commit, funziona bene su 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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.