Làm thế nào để xem các thay đổi giữa hai lần xác nhận mà không có cam kết ở giữa?


642

Làm thế nào để bạn thực hiện git diffchỉ hiển thị sự khác biệt giữa hai cam kết, ngoại trừ các cam kết khác ở giữa?


15
"Git diff" luôn hiển thị sự khác biệt giữa hai lần xác nhận (hoặc thư mục cam kết và làm việc, v.v.).
Jakub Narębski

21
@ JakubNarębski, anh ta đang hỏi làm thế nào để thấy sự khác biệt giữa các thay đổi được giới thiệu bởi một lệnh và các thay đổi được đưa ra bởi một cam kết khác. Nói cách khác, diff của diffs hoặc interdiff.
psusi

1
và nếu bạn thêm tham số --dirstat = files vào lệnh diff, bạn sẽ có một ảnh chụp màn hình rất đẹp về các dự án và tệp chính xác được thay đổi, cùng với tỷ lệ phần trăm thay đổi. Như thế này: git diff [số xác nhận] [số xác nhận] --dirstat = tập tin
Óscar Ibáñez Fernández

Câu trả lời:


605

bạn chỉ có thể chuyển 2 cam kết sang git diff như:

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

1
Điều đó làm việc cho tôi, nhưng bây giờ, Làm thế nào tôi có thể áp dụng my.patchcho chi nhánh khác?
nacho4d

2
@ nacho4d: kiểm tra git chi nhánh khác && git áp dụng my.patch && git add. && git commit -am "Tin nhắn"
Felix Rabe

1
Ưu điểm của việc sử dụng git áp dụng so với bản vá là bạn có thể bao gồm đổi tên và một số thay đổi khác dành riêng cho git. Tôi thích sử dụng git format-patch và git am.
Russell

58
Câu trả lời này hoàn toàn không giải quyết được câu hỏi, vì vậy tôi không biết tại sao nó lại có quá nhiều upvote. OP đặc biệt hỏi làm thế nào KHÔNG nhận lệnh đầu tiên bạn đưa ra và lệnh thứ hai không liên quan gì.
psusi

3
Câu trả lời này không hoàn toàn không trả lời bất cứ điều gì. Nó hoạt động hoàn hảo. Nếu bạn tách nhánh sau của hai cam kết trong câu hỏi, sau đó áp dụng khác này cho nhánh mới đó, bạn sẽ thấy các thay đổi giữa hai lần xác nhận mà không phải đau đầu với các cam kết không liên tục.
Craig Labenz

142

Yêu cầu sự khác biệt / giữa / hai cam kết mà không bao gồm các cam kết ở giữa có ý nghĩa rất nhỏ. Cam kết chỉ là ảnh chụp nhanh nội dung của kho lưu trữ; yêu cầu sự khác biệt giữa hai nhất thiết bao gồm chúng. Vì vậy, câu hỏi sau đó là, bạn thực sự đang tìm kiếm cái gì?

Như William đề xuất, việc hái anh đào có thể mang lại cho bạn đồng bằng của một cam kết duy nhất nổi loạn trên đầu người khác. Đó là:

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

Điều này có cam kết 'abcdef', so sánh nó với tổ tiên trực tiếp của nó, sau đó áp dụng sự khác biệt đó trên đầu trang '012345'. Sự khác biệt mới này sau đó được hiển thị - thay đổi duy nhất là bối cảnh đến từ '012345' thay vì 'tổ tiên ngay lập tức' abcdef '. Tất nhiên, bạn có thể bị xung đột và vân vân, vì vậy đó không phải là một quá trình rất hữu ích trong hầu hết các trường hợp.

Nếu bạn chỉ quan tâm đến abcdef, bạn có thể làm:

$ git log -u -1 abcdef

Điều này so sánh abcdef với tổ tiên trực tiếp của nó, một mình, và thường là những gì bạn muốn.

Và dĩ nhiên

$ git diff 012345..abcdef

cung cấp cho bạn tất cả sự khác biệt giữa hai cam kết.

Nó sẽ giúp có được một ý tưởng tốt hơn về những gì bạn đang cố gắng đạt được - như tôi đã đề cập, yêu cầu sự khác biệt giữa hai cam kết mà không có gì ở giữa không thực sự có ý nghĩa.


41
Tôi sẽ đồng ý rằng, nói chung, sẽ không có ý nghĩa gì khi so sánh hai cam kết. Nhưng git thực sự rất giỏi trong việc không nói cho bạn biết bạn nên nghĩ như thế nào. Giả sử bạn có hai nhánh, mỗi nhánh có các cam kết riêng biệt trông giống như chúng đang thực hiện các thay đổi giống nhau cho cùng một bộ tệp. Tôi muốn có thể sử dụng git để nói với tôi nếu hai bản vá này giống nhau mà không cần phải tin vào mắt tôi. Tôi nghĩ rằng có tiện ích IS trong này.
Chris Cleeland

9
@ChrisCleeland, tiện ích interdiff có thể có ích trong trường hợp đó. Sử dụng git diff để lấy khác biệt của từng cam kết với cha mẹ trực tiếp của nó, sau đó sử dụng interdiff để so sánh các khác biệt.
bdonlan

3
@ChrisCleeland, git không lưu trữ các bản vá. Nó lưu trữ nội dung tập tin. Nó có sơ đồ nén sử dụng deltas, nhưng các nguồn delta không nhất thiết phải tương quan với lịch sử thực tế của các tệp.
bdonlan

11
Sự khác biệt giữa hai cam kết không bao gồm các cam kết khác trên các nhánh tương ứng của chúng có ý nghĩa hoàn hảo: một cam kết được chọn từ anh đào kia, nhưng có thể có một số khác biệt tinh tế. Bạn muốn xem những gì họ đang có mà không bị lộn xộn với tất cả các crap không liên quan khác là khác nhau giữa hai nhánh.
psusi

2
Hoặc giả sử bạn rebase master vào một nhánh tính năng và phải giải quyết xung đột. Sau đó, so sánh origin/featurebranch#HEADvới local/featurebranch#HEADcó thể giúp bạn đảm bảo rằng bạn đã không làm hỏng bất cứ điều gì trong khi giải quyết xung đột.
lefnire

91

Để so sánh hai git cam kết 12345 và abcdef dưới dạng các bản vá, người ta có thể sử dụng lệnh diff như

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

8
Tại sao bạn lại sử dụng GNU diff với git?
OneOfOne

7
@OneOfOne git diff <(git show 123456) <(git show abcdef)không hoạt động; diff <(...) <(...)làm. (Tôi vừa thử nó).
Menachem

@Menachem git diff 123456 abcdef.
OneOfOne

15
@OneOfOne Điều đó không làm điều tương tự. Những gì bạn đề xuất sẽ so sánh cây của từng cam kết, hiển thị một bản vá duy nhất . Những gì tôi (và @plexoos) đang làm là so sánh hai bản vá , mỗi bản vá được giới thiệu bởi các cam kết riêng biệt - nói cách khác, difflấy đầu ra từ hai diffs. Điều này liên quan đến việc đọc và so sánh hai luồng đầu vào. diff(GNU, hoặc Unix, diff) có thể làm điều đó, trong khi git diffkhông thể. Một số có thể tự hỏi tại sao một người muốn làm điều đó. Tôi đang ở giữa để làm điều đó ngay bây giờ, làm sạch một sự hợp nhất đã trở nên tồi tệ.
Menachem

1
Điều này có bao gồm gnu diff của tất cả các siêu dữ liệu trong git diff không?
tham gia

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

Thí dụ:

git diff commit1 commit2 config/routes.rb

Nó cho thấy sự khác biệt trên tập tin đó giữa các cam kết đó.


24

Để kiểm tra thay đổi hoàn toàn:

  git diff <commit_Id_1> <commit_Id_2>

Để chỉ kiểm tra các tệp đã thay đổi / thêm / xóa:

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

LƯU Ý : Để kiểm tra khác biệt mà không có cam kết ở giữa, bạn không cần đặt id cam kết.


20

Hãy nói rằng bạn có cái này

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

Và bạn muốn chắc chắn rằng Anó giống như A0.

Điều này sẽ thực hiện các mẹo:

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

3
Cũng có thể rút ngắn dưới dạng một lớp lót giống như câu trả lời của @plexoos : diff <(git diff B A) <(git diff D A0)(kết quả tương tự như với chương trình git)
pogosama

14

Giả sử bạn muốn thấy sự khác biệt giữa các lần xác nhận 012345 và abcdef. Sau đây nên làm những gì bạn muốn:

$ git thanh toán 012345
$ git cherry-pick -n abcdef
$ git diff - đã đóng gói

Cảm ơn, đó là một ý tưởng tốt để kiểm tra kết quả của bạn sau khi cam kết. Ví dụ, bạn có thể kiểm tra chi nhánh của mình bằng các cam kết không bị bẹp và anh đào chọn cam kết bị đè bẹp của bạn để xem mọi thứ có suôn sẻ với phản ứng tương tác hay không. Ngoài ra khi chủ đã đi trước chi nhánh.
akostadinov

10

Cái này thì sao:

git diff abcdef 123456 | less

Thật tiện lợi khi chỉ đưa nó xuống ít hơn nếu bạn muốn so sánh nhiều khác biệt khác nhau khi đang bay.


6

Kể từ Git 2.19, bạn chỉ cần sử dụng:

git range-diff rev1...rev2 - so sánh hai cây cam kết, bắt đầu bởi tổ tiên chung của chúng

hoặc git range-diff rev1~..rev1 rev2~..rev2 - so sánh các thay đổi được giới thiệu bởi 2 cam kết đã cho


4

aliasCài đặt của tôi trong ~/.bashrctệp cho 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

aliasCài đặt của tôi trong ~/.zshrctệp cho git diff:

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

Cảm ơn @Jinmiao Luo


git diff HEAD~2 HEAD

thay đổi hoàn toàn giữa cam kết thứ 2 mới nhất và hiện tại.

HEAD thuận tiện


1

Tôi đã viết một tập lệnh hiển thị khác nhau giữa hai lần xác nhận, hoạt động tốt trên 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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.