Jak zobaczyć zmiany między dwoma zatwierdzeniami bez zatwierdzeń pomiędzy nimi?

642

Jak sprawić, aby git diffpokazywała tylko różnicę między dwoma zatwierdzeniami, wyłączając pozostałe zatwierdzenia pomiędzy nimi?

Wadim Kotow
źródło
15
„git diff” zawsze pokazuje różnicę między dwoma zatwierdzeniami (lub zatwierdzeniem i katalogiem roboczym itp.).
Jakub Narębski
21
@ JakubNarębski, pyta, jak zobaczyć różnicę między zmianami wprowadzonymi przez jedno polecenie a zmianami wprowadzonymi przez inne zatwierdzenie. Innymi słowy, diff diffs lub interdiff.
psusi
1
a jeśli dodasz parametr --dirstat = pliki do polecenia diff, zrobisz bardzo ładny zrzut ekranu dokładnie tych projektów i plików, które zostaną zmienione, wraz z procentem zmiany. W ten sposób: git diff [numer-zatwierdzenia] [numer-zatwierdzenia] --dirstat = pliki
Óscar Ibáñez Fernández

Odpowiedzi:

605

możesz po prostu przekazać 2 zatwierdzenia do git diff, takie jak:

-> git diff 0da94be  59ff30c > my.patch
-> git apply my.patch
OneOfOne
źródło
1
To działało dla mnie, ale teraz, jak mogę złożyć wniosek my.patchw innym oddziale?
nacho4d
2
@ nacho4d: git checkout other-branch && git Apply my.patch && git add. && git commit -am „Message”
Felix Rabe
1
Zaletą używania git Apply vs. Patch jest to, że możesz dołączyć nazwy i niektóre inne zmiany, które są specyficzne dla git. Lubię używać git format-patch i git am.
Russell
58
Ta odpowiedź całkowicie nie odpowiada na pytanie, więc nie mam pojęcia, dlaczego ma tak wiele pozytywnych opinii. OP dokładnie pyta, jak NIE uzyskać pierwszego polecenia, które wydasz, a drugie nie ma z tym nic wspólnego.
psusi
3
Ta odpowiedź nie całkowicie nie odpowiada. Działa idealnie. Jeśli odłączysz późniejsze z dwóch zatwierdzeń, o których mowa, to zastosujesz różnicę do tego nowego oddziału, zobaczysz zmiany między dwoma zatwierdzeniami bez bólu głowy przerywanych zatwierdzeń.
Craig Labenz
142

Pytanie o różnicę / między / dwoma zatwierdzeniami bez uwzględnienia między nimi nie ma sensu. Zatwierdzenia to tylko migawki zawartości repozytorium; pytanie o różnicę między dwoma koniecznie obejmuje je. Pytanie zatem brzmi: czego tak naprawdę szukasz?

Jak sugerował William, wybieranie wiśni może dać ci deltę jednego zatwierdzenia opartego na drugim. To jest:

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

Pobiera zatwierdzenie „abcdef”, porównuje go do jego bezpośredniego przodka, a następnie stosuje tę różnicę do „012345”. Ta nowa różnica jest następnie pokazana - jedyną zmianą jest kontekst pochodzi od „012345”, a nie od bezpośredniego przodka abcdef. Oczywiście możesz mieć konflikty itp., Więc w większości przypadków nie jest to bardzo przydatny proces.

Jeśli jesteś zainteresowany samym abcdef, możesz:

$ git log -u -1 abcdef

To porównuje abcdef do jego bezpośredniego przodka, samego i jest zwykle tym, czego chcesz.

I oczywiście

$ git diff 012345..abcdef

daje wszystkie różnice między tymi dwoma zatwierdzeniami.

Pomogłoby to uzyskać lepszy obraz tego, co próbujesz osiągnąć - jak już wspomniałem, proszenie o różnicę między dwoma zatwierdzeniami bez tego, co pomiędzy, nie ma sensu.

bdonlan
źródło
41
Zgadzam się, że generalnie porównanie dwóch zatwierdzeń nie ma większego sensu. Ale git naprawdę dobrze mówi ci, jak powinieneś myśleć. Załóżmy, że masz dwie gałęzie, każda z odrębnymi zatwierdzeniami, które wyglądają, jakby wprowadzały te same zmiany w tych samych zestawach plików. Chciałbym móc użyć git, aby powiedzieć mi, czy te dwie łatki są takie same, bez konieczności ufania moim oczom. Myślę, że jest w tym użyteczna.
Chris Cleeland
9
@ChrisCleeland, narzędzie interdiff może się przydać w takim przypadku. Użyj git diff, aby uzyskać różnicę każdego zatwierdzenia względem jego bezpośredniego rodzica, a następnie użyj interdiff do porównania różnic.
bdonlan,
3
@ChrisCleeland, git nie przechowuje łatek. Przechowuje zawartość pliku. Ma schemat kompresji wykorzystujący delty, ale źródła delty niekoniecznie są skorelowane z faktyczną historią plików.
bdonlan,
11
Różnica między tymi dwoma zatwierdzeniami z wyłączeniem innych zatwierdzeń w ich odpowiednich gałęziach ma idealny sens: jeden zatwierdzenie został wybrany z drugiego, ale może mieć pewne subtelne różnice. Chcesz zobaczyć, jakie one są, bez zaśmiecania się wszystkimi innymi niepowiązanymi bzdurami, które różnią się między dwiema gałęziami.
psusi
2
Lub powiedz, że bazujesz na gałęzi funkcji i musisz rozwiązywać konflikty. Później porównanie origin/featurebranch#HEADz local/featurebranch#HEADmoże pomóc ci upewnić się, że nic nie zepsułeś podczas rozwiązywania konfliktu.
lefnire
91

Aby porównać dwa git zatwierdza 12345 i abcdef jako łaty, można użyć polecenia diff jako

diff <(git show 123456) <(git show abcdef)
pleksi
źródło
8
Dlaczego miałbyś używać GNU diff z git?
OneOfOne
7
@OneOfOne git diff <(git show 123456) <(git show abcdef)nie działa; diff <(...) <(...)robi. (Właśnie tego spróbowałem).
Menachem,
@Menachem git diff 123456 abcdef.
OneOfOne,
15
@OneOfOne To nie robi tego samego. Sugerujesz, aby porównać drzewa każdego zatwierdzenia, pokazując pojedynczą łatkę . To, co robię (i @plexoos), to porównywanie dwóch łat , z których każda została wprowadzona osobnymi zatwierdzeniami - innymi słowy, diffotrzymując wynik z dwóch diffs. Obejmuje to odczytywanie i porównywanie dwóch strumieni wejściowych. diff(GNU lub Unix diff) może to zrobić, podczas gdy git diffnie. Niektórzy mogą się zastanawiać, dlaczego ktoś chciałby to zrobić. Jestem w trakcie robienia tego teraz, usuwając połączenie, które poszło nie tak.
Menachem,
1
czy nie obejmie to gnu diff wszystkich metadanych w git diff?
joelb
61
git diff <a-commit> <another-commit> path

Przykład:

git diff commit1 commit2 config/routes.rb

Pokazuje różnicę w tym pliku między tymi zatwierdzeniami.

roadev
źródło
24

Aby sprawdzić pełne zmiany:

  git diff <commit_Id_1> <commit_Id_2>

Aby sprawdzić tylko zmienione / dodane / usunięte pliki:

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

UWAGA : Aby sprawdzić różnicę bez zatwierdzenia pomiędzy, nie musisz umieszczać identyfikatorów zatwierdzenia.

bit_cracker007
źródło
20

Powiedzmy, że masz to

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

I chcesz się upewnić, że Ato samo A0.

To załatwi sprawę:

$ git diff B A > B-A.diff
$ git diff D A0 > D-A0.diff
$ diff B-A.diff D-A0.diff
Juanpa
źródło
3
Może być również skrócony jako jedną wkładką właśnie jako odpowiedź przez @plexoos : diff <(git diff B A) <(git diff D A0)(taki sam wynik jak z pokazu git)
pogosama
14

Załóżmy, że chcesz zobaczyć różnicę między zatwierdzeniami 012345 a abcdef. Następujące czynności powinny robić, co chcesz:

$ git Checkout 012345
$ git cherry-pick -n abcdef
$ git diff --cached
William Pursell
źródło
Dzięki, to dobry pomysł, aby sprawdzić swój wynik po zgnieceniu zatwierdzeń. Na przykład możesz sprawdzić swój oddział za pomocą niezgniecionych zatwierdzeń i Cherry wybrać zgnieciony zatwierdzenie, aby zobaczyć, czy wszystko poszło gładko dzięki interaktywnemu rebase. Dodatkowo, gdy mistrz wyprzedza gałąź.
akostadinov,
10

A co z tym:

git diff abcdef 123456 | less

Przydatne jest po prostu zmniejszanie go, jeśli chcesz porównywać wiele różnych różnic w locie.

Przepełnienie przepływu
źródło
6

Od wersji Git 2.19 możesz po prostu używać:

git range-diff rev1...rev2 - porównaj dwa drzewa zatwierdzeń, zaczynając od ich wspólnego przodka

lub git range-diff rev1~..rev1 rev2~..rev2 - porównaj zmiany wprowadzone przez 2 dane zmiany

Tomáš Diviš
źródło
4

Moje aliasustawienia w ~/.bashrcpliku dla 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
Jinmiao Luo
źródło
2

Moje aliasustawienia w ~/.zshrcpliku dla git diff:

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

Dzięki @Jinmiao Luo


git diff HEAD~2 HEAD

całkowita zmiana między ostatnim 2. zatwierdzeniem a bieżącym.

HEAD jest wygodne

dengST30
źródło
1

Napisałem skrypt, który wyświetla różnice między dwoma zatwierdzeniami, działa dobrze na 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)
Jakub Abraham
źródło