จะดูการเปลี่ยนแปลงระหว่างสองคอมมิทโดยไม่คอมมิทได้อย่างไร?


642

คุณจะgit diffแสดงความแตกต่างระหว่างการผูกมัดสองรายการได้อย่างไร


15
"git diff" จะแสดงความแตกต่างระหว่างการคอมมิทสองครั้ง (หรือคอมมิทและคอมมิทการทำงาน ฯลฯ )
Jakub Narębski

21
@ JakubNarębskiเขาถามว่าจะเห็นความแตกต่างระหว่างการเปลี่ยนแปลงที่แนะนำโดยคำสั่งเดียวและการเปลี่ยนแปลงที่ทำโดยคอมมิชชันอื่นได้อย่างไร กล่าวอีกนัยหนึ่งความต่างของ diffs หรือ interdiff
psusi

1
และถ้าคุณเพิ่มพารามิเตอร์ --dirstat = files เข้ากับคำสั่ง diff คุณจะถ่ายภาพหน้าจอที่ดีมากในโครงการและไฟล์ที่เปลี่ยนแปลงพร้อมกับเปอร์เซ็นต์การเปลี่ยนแปลง เช่นนี้ diff คอมไพล์ [หมายเลขกระทำ-] [กระทำหมายเลข] --dirstat = ไฟล์
ออสการ์อิบาเน็ซFernández

คำตอบ:


605

คุณสามารถผ่าน 2 คอมมิตเพื่อคอมไพล์เช่น:

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

1
ที่ได้ผลสำหรับฉัน แต่ตอนนี้ฉันmy.patchจะสมัครสาขาอื่นได้อย่างไร
nacho4d

2
@ nacho4d: git checkout สาขาอื่น ๆ && git สมัคร my.patch && git เพิ่ม && git commit -am "ข่าวสาร"
เฟลิกซ์ราเบะ

1
ข้อดีของการใช้ git ใช้กับแพทช์คือคุณสามารถรวมการเปลี่ยนชื่อและการเปลี่ยนแปลงอื่น ๆ ที่เฉพาะเจาะจงกับคอมไพล์ ฉันชอบใช้ git format-patch และ git am
รัสเซล

58
คำตอบนี้ไม่สามารถตอบคำถามได้อย่างเต็มที่ดังนั้นฉันจึงไม่รู้ว่าทำไมถึงมี upvotes มากมาย OP จะถามว่าจะไม่รับคำสั่งแรกที่คุณให้มาอย่างไรและคำสั่งที่สองไม่มีส่วนเกี่ยวข้องกับสิ่งใด
psusi

3
คำตอบนี้ไม่ได้ล้มเหลวอย่างเต็มที่ที่จะตอบอะไร มันทำงานได้อย่างสมบูรณ์ หากคุณแยกสาขาออกในภายหลังจากข้อผูกพันทั้งสองที่มีปัญหาให้นำสิ่งนี้ไปใช้กับสาขาใหม่นั้นคุณจะเห็นการเปลี่ยนแปลงระหว่างทั้งสองคอมมิทโดยไม่ต้องปวดหัวกับการกระทำที่ไม่ต่อเนื่อง
Craig Labenz

142

การถามถึงความแตกต่าง / ระหว่าง / สองการกระทำโดยไม่รวมการผูกมัดในระหว่างนั้นจะสมเหตุสมผล การคอมมิทเป็นเพียงสแน็ปช็อตของเนื้อหาของที่เก็บ ขอความแตกต่างระหว่างสองจำเป็นต้องรวมถึงพวกเขา ดังนั้นคำถามคือคุณต้องการอะไรจริงๆ

ตามที่ William แนะนำการเก็บเชอร์รี่สามารถมอบเดลต้าของการคอมมิชชันเดี่ยวให้คุณได้ นั่นคือ:

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

สิ่งนี้จะกระทำ 'abcdef' เปรียบเทียบกับบรรพบุรุษทันทีจากนั้นนำความแตกต่างนั้นไปใช้กับ '012345' ความแตกต่างใหม่นี้จะปรากฏขึ้น - การเปลี่ยนแปลงเพียงอย่างเดียวคือบริบทมาจาก '012345' แทนที่จะเป็น 'บรรพบุรุษของ abcdef ทันที แน่นอนคุณอาจได้รับความขัดแย้งและอื่น ๆ ดังนั้นจึงไม่ใช่กระบวนการที่มีประโยชน์มากในกรณีส่วนใหญ่

หากคุณสนใจ abcdef เองคุณสามารถทำได้:

$ git log -u -1 abcdef

สิ่งนี้จะเปรียบเทียบ abcdef กับบรรพบุรุษที่อยู่คนเดียวและมักเป็นสิ่งที่คุณต้องการ

และแน่นอนว่า

$ git diff 012345..abcdef

ให้ความแตกต่างทั้งหมดระหว่างคุณทั้งสอง

มันจะช่วยให้ได้แนวคิดที่ดีขึ้นเกี่ยวกับสิ่งที่คุณกำลังพยายามบรรลุ - ดังที่ฉันได้กล่าวถึงการขอความแตกต่างระหว่างการกระทำสองอย่างโดยที่ไม่ได้ทำอะไรเลย


41
ฉันจะยอมรับว่าโดยทั่วไปแล้วมันไม่สมเหตุสมผลเลยที่จะเปรียบเทียบสองคอมมิท แต่คอมไพล์ดีมากที่จะไม่บอกคุณว่าคุณควรคิดอย่างไร สมมติว่าคุณมีสองสาขาแต่ละสาขามีความมุ่งมั่นที่แตกต่างกันซึ่งดูเหมือนว่าพวกเขากำลังทำการเปลี่ยนแปลงเดียวกันกับไฟล์ชุดเดียวกัน ฉันต้องการใช้ git เพื่อบอกฉันว่าแผ่นแปะทั้งสองนี้เหมือนกันหรือไม่โดยไม่ต้องเชื่อสายตา ฉันคิดว่ามีประโยชน์ในเรื่องนี้
Chris Cleeland

9
@ChrisCleeland ยูทิลิตี้ interdiff สามารถมีประโยชน์ในกรณีนี้ ใช้ git diff เพื่อรับผลต่างของการคอมมิตกับพาเรนต์ทันทีจากนั้นใช้ interdiff เพื่อเปรียบเทียบดิฟ
bdonlan

3
@ChrisCleeland, git ไม่ได้เก็บแพทช์ มันเก็บเนื้อหาของไฟล์ มันมีรูปแบบการบีบอัดที่ใช้เดลตา แต่แหล่งที่มาของเดลต้าไม่จำเป็นต้องมีความสัมพันธ์กับประวัติที่แท้จริงของไฟล์
bdonlan

11
ความแตกต่างระหว่างสองคอมมิชชันที่ไม่รวมการคอมมิชชันอื่น ๆ ในแต่ละสาขาทำให้รู้สึกสมบูรณ์แบบ: คอมมิชชันหนึ่งถูกเลือกจากเชอร์รี่ แต่อาจมีความแตกต่างเล็กน้อย คุณต้องการที่จะเห็นสิ่งที่พวกเขากำลังโดยไม่รกกับอึที่ไม่เกี่ยวข้องอื่น ๆ ที่แตกต่างกันระหว่างสองสาขา
psusi

2
หรือพูดว่าคุณ rebase ต้นแบบลงบนสาขาฟีเจอร์และต้องแก้ไขข้อขัดแย้ง หลังจากนั้นการเปรียบเทียบorigin/featurebranch#HEADที่จะlocal/featurebranch#HEADช่วยเหลือคุณสามารถให้แน่ใจว่าคุณทำอะไรไม่ได้โคลนความขัดแย้งระหว่างความละเอียด
lefnire

91

เพื่อเปรียบเทียบสองคอมไพล์ให้กระทำ 12345 และ abcdef เป็นแพตช์หนึ่งสามารถใช้คำสั่ง diff เป็น

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

8
ทำไมคุณถึงใช้ GNU ต่างกับคอมไพล์?
OneOfOne

7
@OneOfOne git diff <(git show 123456) <(git show abcdef)ไม่ทำงาน; diff <(...) <(...)ทำ. (ฉันแค่ลองมัน)
Menachem

git diff 123456 abcdef@Menachem
OneOfOne

15
@OneOfOne นั่นไม่ได้ทำสิ่งเดียวกัน สิ่งที่คุณแนะนำจะเปรียบเทียบต้นไม้ของแต่ละกระทำแสดงแพทช์เดียว สิ่งที่ฉัน (และ @plexoos) กำลังทำอยู่คือการเปรียบเทียบแพตช์สองชุดแต่ละอันได้รับการแนะนำโดยคอมมิทที่แยกจากกัน - กล่าวอีกอย่างคือdiffเอาท์พุทจากสองdiffs สิ่งนี้เกี่ยวข้องกับการอ่านและการเปรียบเทียบอินพุตสองสตรีม diff(GNU หรือ Unix diff) สามารถทำสิ่งนั้นได้ในขณะที่git diffไม่สามารถทำได้ บางคนอาจสงสัยว่าทำไมเราจึงอยากทำเช่นนั้น ฉันกำลังทำสิ่งนี้อยู่ในตอนนี้ทำความสะอาดจุดรวมที่ไม่ดี
Menachem

1
สิ่งนี้จะไม่รวม gnu diff ของข้อมูลเมตาทั้งหมดใน git diff หรือไม่
joelb

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

ตัวอย่าง:

git diff commit1 commit2 config/routes.rb

มันแสดงให้เห็นถึงความแตกต่างในไฟล์นั้นระหว่างการกระทำเหล่านั้น


24

สำหรับการตรวจสอบการเปลี่ยนแปลงทั้งหมด:

  git diff <commit_Id_1> <commit_Id_2>

สำหรับการตรวจสอบเฉพาะไฟล์ที่มีการเปลี่ยนแปลง / เพิ่ม / ลบ:

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

หมายเหตุ : สำหรับการตรวจสอบความแตกต่างโดยไม่ต้องกระทำระหว่างคุณไม่จำเป็นต้องใส่รหัสการกระทำ


20

สมมติว่าคุณมีสิ่งนี้

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

และคุณต้องการเพื่อให้แน่ใจว่าเป็นเช่นเดียวกับAA0

นี่จะเป็นการหลอกลวง:

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

3
นอกจากนี้ยังสามารถตัดให้สั้นลงเป็นหนึ่งซับเช่นเดียวกับคำตอบโดย@plexoos : diff <(git diff B A) <(git diff D A0)(ผลเช่นเดียวกับการแสดงคอมไพล์)
pogosama

14

สมมติว่าคุณต้องการเห็นความแตกต่างระหว่างการยอมรับ 012345 และ abcdef ต่อไปนี้ควรทำสิ่งที่คุณต้องการ:

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

ขอขอบคุณเป็นความคิดที่ดีที่จะตรวจสอบผลลัพธ์ของคุณหลังจากการบีบอัดข้อมูล ตัวอย่างเช่นคุณสามารถเช็คเอาต์สาขาของคุณด้วยคอมมิทที่ไม่แบนและเชอร์รี่เลือกสควอชของคุณเพื่อดูว่าทุกอย่างราบรื่นด้วยการรีบูต นอกจากนี้เมื่ออาจารย์ได้ไปข้างหน้าของสาขา
akostadinov

10

เกี่ยวกับสิ่งนี้:

git diff abcdef 123456 | less

มันมีประโยชน์มากที่จะทำให้ท่อเล็กลงถ้าคุณต้องการเปรียบเทียบ diffs ที่แตกต่างกันมากมายในทันที


6

ตั้งแต่ Git 2.19 คุณสามารถใช้:

git range-diff rev1...rev2 - เปรียบเทียบต้นไม้สองต้นที่เริ่มโดยบรรพบุรุษร่วมกัน

หรือ git range-diff rev1~..rev1 rev2~..rev2 - เปรียบเทียบการเปลี่ยนแปลงที่แนะนำโดย 2 คอมมิทที่ให้ไว้


4

aliasการตั้งค่าของฉันใน~/.bashrcไฟล์สำหรับ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

aliasการตั้งค่าของฉันใน~/.zshrcไฟล์สำหรับgit diff:

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

ขอบคุณ @Jinmiao Luo


git diff HEAD~2 HEAD

การเปลี่ยนแปลงที่สมบูรณ์ระหว่างการส่งมอบครั้งที่ 2 ครั้งล่าสุดและปัจจุบัน

HEAD สะดวก


1

ฉันเขียนสคริปต์ที่แสดงความแตกต่างระหว่างสองคอมมิทใช้งานได้ดีบน 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)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.