사이에 커밋없이 두 커밋 사이의 변경 사항을 보는 방법은 무엇입니까?


642

git diff사이의 다른 커밋을 제외하고 두 커밋의 차이점 만 어떻게 표시합니까?


15
"git diff"는 항상 두 커밋 (또는 커밋 및 작업 디렉토리 등)의 차이점을 보여줍니다.
Jakub Narębski

21
@ JakubNarębski, 그는 한 명령으로 인한 변경 사항과 다른 커밋으로 인한 변경 사항의 차이점을 확인하는 방법을 묻고 있습니다. 즉, diff 또는 interdiff의 diff입니다.
psusi

1
diff 명령에 --dirstat = files 매개 변수를 추가하면 변경 비율과 함께 변경된 정확한 프로젝트와 파일에 대한 멋진 스크린 샷이 생성됩니다. git diff [commit-number] [commit-number] --dirstat = files
Óscar Ibáñez Fernández

답변:


605

다음과 같이 git diff에 2 개의 커밋을 전달할 수 있습니다.

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

1
그것은 나를 위해 일했지만 지금 my.patch은 다른 지점에 어떻게 신청할 수 있습니까?
nacho4d

2
@ nacho4d : git checkout other-branch && git apply my.patch && git add. && git commit -am "Message"
Felix Rabe

1
git apply와 patch를 사용하면 git에 고유 한 이름 변경 및 기타 변경 사항을 포함시킬 수 있다는 장점이 있습니다. 나는 git format-patch와 git am을 사용하는 것을 좋아합니다.
Russell

58
이 답변은 질문을 완전히 다루지 못하기 때문에 왜 그렇게 많은 투표가 있는지 전혀 알 수 없습니다. OP는 구체적으로 첫 번째 명령을 내리지 않는 방법을 묻고 두 번째 명령은 아무런 관련이 없습니다.
psusi

3
이 답변은 아무것도 대답하지 못했습니다. 완벽하게 작동합니다. 문제의 두 커밋 중 나중에 커밋 한 경우이 차이점을 새 브랜치에 적용하면 간헐적 커밋에 대한 두통없이 두 커밋 사이의 변경 사항을 볼 수 있습니다.
Craig Labenz 2016 년

142

사이에 커밋을 포함시키지 않고 두 커밋 사이의 차이를 묻는 것은 의미가 없습니다. 커밋은 저장소 내용의 스냅 샷일뿐입니다. 둘 사이의 차이를 요구하는 것은 반드시 그것들을 포함합니다. 그렇다면 문제는 무엇을 찾고 있습니까?

윌리엄이 제안했듯이 체리 피킹은 다른 커밋을 기반으로 단일 커밋의 델타를 제공 할 수 있습니다. 그건:

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

커밋 'abcdef'를 가져 와서 직계 조상과 비교 한 다음 그 차이 를 적용 합니다 '012345'위에 . 이 새로운 차이점은 다음과 같습니다. 유일한 변화는 컨텍스트가 'abcdef'의 직계 조상이 아닌 '012345'에서 비롯된 것입니다. 물론 충돌 등이 발생할 수 있으므로 대부분의 경우 유용한 프로세스가 아닙니다.

abcdef 자체에 관심이 있다면 다음을 수행하십시오.

$ git log -u -1 abcdef

이것은 abcdef를 바로 직계 조상과 비교하며 일반적으로 원하는 것입니다.

그리고 물론

$ git diff 012345..abcdef

두 커밋 사이의 모든 차이점을 알려줍니다.

앞서 언급했듯이 두 커밋 사이의 차이점을 묻는 것이 실제로 의미가있는 것은 아닙니다.


41
나는 일반적으로 두 커밋을 비교하는 것이 의미가 없다는 것에 동의합니다. 그러나 git은 어떻게 생각 해야하는지 말하지 않는 것이 좋습니다. 동일한 파일 세트에 대해 동일한 변경을 수행하는 것처럼 보이는 고유 한 커밋이있는 두 개의 분기가 있다고 가정하십시오. git을 사용 하여이 두 패치가 내 눈을 믿지 않고 동일한 지 알려주고 싶습니다. 나는 이것에 유용성이 있다고 생각합니다.
Chris Cleeland

9
이 경우 interdiff 유틸리티 인 @ChrisCleeland가 유용 할 수 있습니다. git diff를 사용하여 직계 부모에 대한 각 커밋의 diff를 얻은 다음 interdiff를 사용하여 diff를 비교하십시오.
bdonlan

3
@ChrisCleeland, git은 패치를 저장하지 않습니다. 파일 내용을 저장합니다. 델타를 사용하는 압축 체계가 있지만 델타 소스가 파일의 실제 히스토리와 상관 관계가있는 것은 아닙니다.
bdonlan

11
각각의 브랜치에서 다른 커밋을 제외하고 두 커밋 사이의 차이점은 완벽하게 이해됩니다. 하나의 커밋은 다른 체리에서 선택되었지만 약간의 차이가있을 수 있습니다. 두 가지 사이에 다른 다른 관련되지 않은 쓰레기와 혼잡하지 않고 그들이 무엇인지보고 싶습니다.
psusi

2
또는 기능 분기에 마스터를 리베이스하고 충돌을 해결해야한다고 가정하십시오. 이후에 비교 origin/featurebranch#HEAD하면 local/featurebranch#HEAD충돌 해결 중에 아무것도 뭉치지 않도록 할 수 있습니다.
lefnire

91

두 개의 git commit 12345와 abcdef를 패치로 비교하려면 diff 명령을 다음과 같이 사용할 수 있습니다.

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

8
왜 GNU diff를 git과 함께 사용 하시겠습니까?
OneOfOne

7
@OneOfOne git diff <(git show 123456) <(git show abcdef)이 작동하지 않습니다. diff <(...) <(...)그렇습니다. (방금 시도했습니다).
Menachem

@Menachem git diff 123456 abcdef.
OneOfOne

15
@OneOfOne 똑같은 일을하지 않습니다. 제안한 내용은 각 커밋 의 트리 를 비교하여 단일 패치를 보여줍니다 . 내가 (그리고 @plexoos)하고있는 것은 두 개의 패치를 비교하는 것입니다 . 각 패치 는 별도의 커밋에 의해 도입되었습니다. 즉, 두 개의 패치diff 를 출력 diff합니다. 여기에는 두 개의 입력 스트림을 읽고 비교하는 작업이 포함됩니다. diff(GNU 또는 Unix diff)는 그렇게 git diff할 수 있지만 할 수는 없습니다. 어떤 사람들은 왜 그렇게하고 싶어하는지 궁금 할 것입니다. 나는 지금 그 일을하는 중입니다. 나쁜 병합을 정리하십시오.
Menachem

1
이것은 git diff에있는 모든 메타 데이터의 gnu 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

참고 : 사이에 커밋없이 diff를 확인하기 위해 커밋 ID를 넣을 필요는 없습니다.


20

당신이 이것을 가지고 있다고 가정 해 봅시다

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

그리고 당신은이 있는지 확인하려면 A과 동일A0 .

이것은 트릭을 할 것입니다 :

$ 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)(git show와 동일한 결과)
pogosama

14

커밋 012345와 abcdef의 차이점을보고 싶다고 가정합니다. 다음은 원하는 것을 수행해야합니다.

$ git checkout 012345
$ git cherry-pick -n abcdef
$ git diff-캐시

감사합니다. 커밋을 스쿼시 한 ​​후 결과를 확인하는 것이 좋습니다. 예를 들어, 스쿼시되지 않은 커밋으로 지점을 체크 아웃하고 체리는 스쿼시 커밋을 선택하여 대화 형 리베이스로 모든 것이 원활하게 진행되는지 확인할 수 있습니다. 또한 마스터가 지점을 앞질 때.
akostadinov

10

이건 어때?

git diff abcdef 123456 | less

많은 다른 diff를 즉석에서 비교하고 싶을 때 파이프를 줄이면 편리합니다.


6

Git 2.19부터 다음을 간단히 사용할 수 있습니다 :

git range-diff rev1...rev2 -공통 조상으로 시작하는 두 커밋 트리 비교

또는 git range-diff rev1~..rev1 rev2~..rev2 -주어진 커밋 2 개에 의해 도입 된 변경 사항 비교


4

파일 내 alias설정 :~/.bashrcgit 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설정 :~/.zshrcgit diff

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

감사합니다 @Jinmiao Luo


git diff HEAD~2 HEAD

최신 2 차 커밋과 현재 사이의 완전한 변경.

HEAD 편리하다


1

두 커밋 사이의 차이점을 표시하고 우분투에서 잘 작동하는 스크립트를 작성했습니다.

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.