Bagaimana cara melihat perubahan antara dua komit tanpa komit di antara?

643

Bagaimana Anda membuat git diffhanya menunjukkan perbedaan antara dua komit, tidak termasuk komit lainnya di-antara?

Vadim Kotov
sumber
15
"git diff" selalu menunjukkan perbedaan antara dua commit (atau commit dan direktori kerja, dll.).
Jakub Narębski
21
@ JakubNarębski, dia bertanya bagaimana melihat perbedaan antara perubahan yang diperkenalkan oleh satu perintah dan perubahan yang diperkenalkan oleh komit lain. Dengan kata lain, diff dari diff atau interdiff.
psusi
1
dan jika Anda menambahkan --dirstat = parameter file ke perintah diff, Anda akan mengambil tangkapan layar yang sangat bagus pada proyek yang tepat dan file yang diubah, bersama dengan persentase perubahan. Seperti ini: git diff [commit-number] [commit-number] --dirstat = file
Óscar Ibáñez Fernández

Jawaban:

606

Anda cukup memberikan 2 commit ke git diff seperti:

-> git diff 0da94be  59ff30c > my.patch
-> git apply my.patch
OneOfOne
sumber
1
Itu berhasil bagi saya, tetapi sekarang, Bagaimana saya bisa mendaftar my.patchke cabang lain?
nacho4d
2
@ nacho4d: checkout git-cabang lain && git terapkan my.patch && git add. && git commit -am "Message"
Felix Rabe
1
Keuntungan menggunakan git apply vs. patch adalah Anda dapat memasukkan nama baru dan beberapa perubahan lain yang khusus untuk git. Saya suka menggunakan format git-patch dan git.
Russell
58
Jawaban ini sama sekali gagal menjawab pertanyaan, jadi saya tidak tahu mengapa ada begitu banyak upvotes. OP secara khusus menanyakan bagaimana TIDAK untuk mendapatkan perintah pertama yang Anda berikan, dan yang kedua tidak ada hubungannya dengan apa pun.
psusi
3
Jawaban ini sama sekali tidak gagal menjawab apa pun. Ini bekerja dengan sempurna. Jika Anda bercabang di kemudian hari dari dua commit yang bersangkutan, maka terapkan diff ini ke cabang baru itu, Anda akan melihat perubahan antara kedua commit tersebut tanpa sakit kepala dari commit yang berselang.
Craig Labenz
142

Meminta perbedaan / antara / dua komit tanpa menyertakan komit di antaranya tidak masuk akal. Komit hanyalah snapshot dari isi repositori; meminta perbedaan antara keduanya harus mencakup mereka. Jadi pertanyaannya adalah, apa yang sebenarnya Anda cari?

Seperti yang disarankan William, memetik ceri dapat memberi Anda delta dari satu komit yang direbus di atas yang lain. Itu adalah:

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

Ini membutuhkan komit 'abcdef', membandingkannya dengan leluhur langsungnya, kemudian menerapkan perbedaan itu di atas '012345'. Perbedaan baru ini kemudian ditampilkan - satu-satunya perubahan adalah konteksnya berasal dari '012345' daripada 'leluhur langsung abcdef. Tentu saja, Anda mungkin mendapatkan konflik dan lain-lain, jadi itu bukan proses yang sangat berguna dalam kebanyakan kasus.

Jika Anda hanya tertarik dengan abcdef itu sendiri, Anda dapat melakukannya:

$ git log -u -1 abcdef

Ini membandingkan abcdef dengan leluhur langsungnya, sendirian, dan biasanya itulah yang Anda inginkan.

Dan tentu saja

$ git diff 012345..abcdef

memberi Anda semua perbedaan antara kedua komitmen tersebut.

Ini akan membantu untuk mendapatkan ide yang lebih baik tentang apa yang ingin Anda capai - seperti yang saya sebutkan, meminta perbedaan antara dua komitmen tanpa apa yang ada di antaranya sebenarnya tidak masuk akal.

omong kosong
sumber
41
Saya akan setuju bahwa, secara umum, tidak masuk akal untuk membandingkan dua komitmen. Tapi git sangat pandai tidak memberitahu Anda bagaimana Anda harus berpikir. Misalkan Anda memiliki dua cabang, masing-masing dengan komit berbeda yang terlihat seperti mereka membuat perubahan yang sama untuk set file yang sama. Saya ingin dapat menggunakan git untuk memberi tahu saya jika kedua tambalan ini sama tanpa harus mempercayai mata saya. Saya pikir ada utilitas dalam hal ini.
Chris Cleeland
9
@ ChrisCleeland, utilitas interdiff bisa berguna dalam kasus itu. Gunakan git diff untuk mendapatkan diff dari setiap komit terhadap induk langsungnya, kemudian gunakan interdiff untuk membandingkan diff.
bdonlan
3
@ ChrisCleeland, git tidak menyimpan tambalan. Ini menyimpan konten file. Itu memang memiliki skema kompresi yang menggunakan delta, tetapi sumber delta tidak selalu berkorelasi dengan sejarah sebenarnya dari file.
bdonlan
11
Perbedaan antara dua komit tidak termasuk komit lain di cabang masing-masing masuk akal: satu komit dipilih dari yang lain, tetapi mungkin memiliki beberapa perbedaan halus. Anda ingin melihat apa yang mereka tanpa berantakan dengan semua omong kosong yang tidak terkait lainnya yang berbeda antara dua cabang.
psusi
2
Atau katakan Anda rebase master ke cabang fitur, dan harus menyelesaikan konflik. Setelah membandingkan origin/featurebranch#HEADdengan local/featurebranch#HEADdapat membantu Anda memastikan Anda tidak membuang apa pun selama resolusi konflik.
kidal
91

Untuk membandingkan dua git melakukan 12345 dan abcdef sebagai tambalan kita dapat menggunakan perintah diff sebagai

diff <(git show 123456) <(git show abcdef)
plexoo
sumber
8
Mengapa Anda menggunakan GNU diff dengan git?
OneOfOne
7
@OneOfOne git diff <(git show 123456) <(git show abcdef)tidak berfungsi; diff <(...) <(...)tidak. (Saya baru mencobanya).
Menachem
@Menachem git diff 123456 abcdef.
OneOfOne
15
@OneOfOne Itu tidak melakukan hal yang sama. Apa yang Anda sarankan akan membandingkan pohon masing-masing komit, menunjukkan satu tambalan . Apa yang saya (dan @plexoos) lakukan adalah membandingkan dua tambalan , masing-masing telah diperkenalkan oleh komit terpisah - dengan kata lain, diffmenghasilkan output dari dua diffs. Ini melibatkan membaca dan membandingkan dua aliran input. diff(GNU, atau Unix, diff) dapat melakukan itu, sementara git difftidak bisa. Beberapa orang mungkin bertanya-tanya mengapa seseorang ingin melakukan itu. Saya sedang melakukan itu sekarang, membersihkan gabungan yang menjadi buruk.
Menachem
1
tidakkah ini termasuk gnu diff dari semua metadata di git diff?
joelb
61
git diff <a-commit> <another-commit> path

Contoh:

git diff commit1 commit2 config/routes.rb

Ini menunjukkan perbedaan pada file itu antara komit tersebut.

roadev
sumber
24

Untuk memeriksa perubahan lengkap:

  git diff <commit_Id_1> <commit_Id_2>

Untuk memeriksa hanya file yang diubah / ditambahkan / dihapus:

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

CATATAN : Untuk memeriksa perbedaan tanpa komit di antaranya, Anda tidak perlu meletakkan id komit.

bit_cracker007
sumber
20

Katakanlah Anda punya ini

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

Dan Anda ingin memastikan bahwa Aitu sama dengan A0.

Ini akan melakukan trik:

$ git diff B A > B-A.diff
$ git diff D A0 > D-A0.diff
$ diff B-A.diff D-A0.diff
Juanpa
sumber
3
Dapat juga disingkat sebagai one-liner seperti jawaban oleh @plexoos : diff <(git diff B A) <(git diff D A0)(hasil yang sama seperti dengan git show)
pogosama
14

Misalkan Anda ingin melihat perbedaan antara komit 012345 dan abcdef. Berikut ini harus melakukan apa yang Anda inginkan:

checkout $ git 012345
$ git cherry-pick -n abcdef
$ git diff --cached
William Pursell
sumber
Terima kasih, itu ide yang bagus untuk memeriksa hasil Anda setelah melakukan squashing. Sebagai contoh, Anda dapat checkout cabang Anda dengan komit non-squash dan ceri memilih komit squashing Anda untuk melihat apakah semuanya berjalan lancar dengan rebase interaktif. Selain itu, ketika master telah maju dari cabang.
akostadinov
10

Bagaimana dengan ini:

git diff abcdef 123456 | less

Berguna hanya untuk mengurangi menjadi pipa jika Anda ingin membandingkan banyak perbedaan yang berbeda dengan cepat.

Aliran Overstack
sumber
6

Sejak Git 2.19, Anda cukup menggunakan:

git range-diff rev1...rev2 - membandingkan dua pohon komit, dimulai oleh nenek moyang mereka bersama

atau git range-diff rev1~..rev1 rev2~..rev2 - bandingkan perubahan yang dilakukan oleh 2 commit yang diberikan

Tomáš Diviš
sumber
4

aliasPengaturan saya di ~/.bashrcfile untuk 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
sumber
2

aliasPengaturan saya di ~/.zshrcfile untuk git diff:

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

Terima kasih @ Jinmiao Luo


git diff HEAD~2 HEAD

selesaikan perubahan antara komitmen terbaru ke-2 dan saat ini.

HEAD nyaman

dengST30
sumber
1

Saya menulis sebuah skrip yang menampilkan perbedaan antara dua commit, berfungsi dengan baik di 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)
Yakub Abraham
sumber