Como ver as alterações entre duas confirmações sem confirmações intermediárias?

643

Como você git diffmostra apenas a diferença entre dois commits, excluindo os outros commits no meio?

Vadim Kotov
fonte
15
"git diff" sempre mostra a diferença entre dois commit (ou commit e diretório de trabalho, etc.).
Jakub Narębski
21
@ JakubNarębski, ele está perguntando como ver a diferença entre as alterações introduzidas por um comando e as alterações introduzidas por outro commit. Em outras palavras, o diff de diffs ou interdiff.
psusi 4/09/2015
1
e se você adicionar o parâmetro --dirstat = files ao comando diff, você fará uma captura de tela muito boa dos projetos e arquivos exatos que foram alterados, juntamente com uma porcentagem de alteração. Assim: git diff [número do commit] [número do commit] --dirstat = files
Óscar Ibáñez Fernández

Respostas:

606

você pode simplesmente passar os 2 commits para o git diff como:

-> git diff 0da94be  59ff30c > my.patch
-> git apply my.patch
OneOfOne
fonte
1
Isso funcionou para mim, mas agora, como posso me inscrever my.patchem outro ramo?
N
2
@ nacho4d: git check-out de outros ramos && git aplicam my.patch && git add. && git commit -am "Message"
Felix Rabe
1
A vantagem de usar o git apply vs. patch é que você pode incluir renomeações e outras alterações específicas do git. Eu gosto de usar o git format-patch e o git am.
Russell
58
Essa resposta falha totalmente em abordar a questão, então não tenho idéia do porquê de tantas votações. O OP está perguntando especificamente como NÃO obter o primeiro comando que você dá, e o segundo não tem nada a ver com nada.
psusi 4/09/2015
3
Esta resposta não deixa de responder absolutamente nada. Funciona perfeitamente. Se você ramificar o mais tardar dos dois commits em questão, aplicar essa diferença nesse novo branch, você verá as alterações entre os dois commits sem dor de cabeça dos commits intermitentes.
Craig Labenz
142

Pedir a diferença / entre / dois commits sem incluir os commits entre faz pouco sentido. As confirmações são apenas instantâneos do conteúdo do repositório; pedir a diferença entre dois necessariamente os inclui. Então a questão é: o que você realmente está procurando?

Como William sugeriu, a escolha da cereja pode fornecer o delta de um único commit refeito em cima de outro. Isso é:

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

Isso leva o commit 'abcdef', o compara ao seu ancestral imediato e aplica essa diferença sobre '012345'. Essa nova diferença é mostrada - a única mudança é que o contexto vem do '012345' e não do ancestral imediato do abcdef. Claro, você pode ter conflitos e etc, portanto, não é um processo muito útil na maioria dos casos.

Se você está interessado apenas no abcdef, pode:

$ git log -u -1 abcdef

Isso compara o abcdef ao seu ancestral imediato, sozinho, e geralmente é o que você deseja.

E claro

$ git diff 012345..abcdef

fornece todas as diferenças entre esses dois commits.

Seria bom ter uma idéia melhor do que você está tentando alcançar - como mencionei, pedir a diferença entre dois commits sem o que está no meio não faz sentido.

bdonlan
fonte
41
Concordo que, em geral, não faz muito sentido comparar dois commits. Mas o git é realmente bom em não dizer como você deve pensar. Suponha que você tenha duas ramificações, cada uma com confirmações distintas que parecem estar fazendo as mesmas alterações nos mesmos conjuntos de arquivos. Eu gostaria de poder usar o git para me dizer se esses dois patches são iguais sem ter que confiar nos meus olhos. Eu acho que existe utilidade nisso.
Chris Cleeland
9
@ChrisCleeland, o utilitário interdiff pode ser útil nesse caso. Use git diff para obter o diff de cada commit contra seu pai imediato e, em seguida, use interdiff para comparar os diffs.
bdonlan
3
@ChrisCleeland, o git não armazena patches. Ele armazena o conteúdo do arquivo. Ele possui um esquema de compactação que usa deltas, mas as fontes delta não estão necessariamente correlacionadas com o histórico real dos arquivos.
bdonlan
11
A diferença entre os dois commits, excluindo outros commits em seus respectivos ramos, faz todo o sentido: um commit foi escolhido de maneira diferente do outro, mas pode ter algumas diferenças sutis. Você deseja ver o que eles são, sem ser desordenado com todas as outras porcarias não relacionadas que são diferentes entre os dois ramos.
psusi 4/09/2015
2
Ou diga que você rebase o master em uma ramificação de recurso e deve resolver conflitos. Depois, comparar origin/featurebranch#HEADcom local/featurebranch#HEADpode ajudar a garantir que você não tenha estragado nada durante a resolução de conflitos.
Lefnire
91

Para comparar dois commits git 12345 e abcdef como patches, pode-se usar o comando diff como

diff <(git show 123456) <(git show abcdef)
plexoos
fonte
8
Por que você usaria o GNU diff com git?
OneOfOne 13/08/14
7
@OneOfOne git diff <(git show 123456) <(git show abcdef)não funciona; diff <(...) <(...)faz. (Eu apenas tentei).
Menachem
@Menachem git diff 123456 abcdef.
OneOfOne
15
@OneOfOne Isso não faz a mesma coisa. O que você sugeriu compararia as árvores de cada confirmação, mostrando um único patch . O que eu (e @plexoos) estamos fazendo é comparar dois patches , cada um tendo sido introduzidos por commits separados - em outras palavras, diffobtendo a saída de dois diffs. Isso envolve ler e comparar dois fluxos de entrada. diff(GNU ou Unix diff) pode fazer isso, enquanto git diffnão pode. Alguns podem se perguntar por que alguém iria querer fazer isso. Estou fazendo isso agora, limpando uma mesclagem que deu errado.
Menachem
1
isso não inclui o diff gnu de todos os metadados no diff git?
joelb
61
git diff <a-commit> <another-commit> path

Exemplo:

git diff commit1 commit2 config/routes.rb

Ele mostra a diferença nesse arquivo entre essas confirmações.

roadev
fonte
24

Para verificar alterações completas:

  git diff <commit_Id_1> <commit_Id_2>

Para verificar apenas os arquivos alterados / adicionados / excluídos:

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

NOTA : Para verificar o diff sem confirmar no meio, você não precisa colocar os IDs de confirmação.

bit_cracker007
fonte
20

Digamos que você tenha isso

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

E você quer ter certeza de que Aé o mesmo que A0.

Isto irá fazer o truque:

$ git diff B A > B-A.diff
$ git diff D A0 > D-A0.diff
$ diff B-A.diff D-A0.diff
Juanpa
fonte
3
Também pode ser abreviado como um one-liner, assim como a resposta por @plexoos : diff <(git diff B A) <(git diff D A0)(mesmo resultado que com espectáculo de git)
pogosama
14

Suponha que você queira ver a diferença entre confirmações 012345 e abcdef. A seguir, faça o que você deseja:

$ git checkout 012345
$ git cherry-pick -n abcdef
$ git diff --cached
William Pursell
fonte
Obrigado, é uma boa ideia verificar seu resultado após a compactação. Por exemplo, você pode fazer checkout do seu ramo com confirmações não esmagadas e escolher sua confirmação esmagada para ver se tudo correu bem com o rebase interativo. Além disso, quando o mestre foi à frente da filial.
akostadinov
10

Que tal isso:

git diff abcdef 123456 | less

É útil reduzi-lo a menos se você quiser comparar muitas diferenças diferentes rapidamente.

Flow Overstack
fonte
6

Desde o Git 2.19, você pode simplesmente usar:

git range-diff rev1...rev2 - compare duas árvores de confirmação, começando pelo ancestral comum

ou git range-diff rev1~..rev1 rev2~..rev2 - comparar as alterações introduzidas por 2 confirmados

Tomáš Diviš
fonte
4

Minhas aliasconfigurações no ~/.bashrcarquivo para 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
fonte
2

Minhas aliasconfigurações no ~/.zshrcarquivo para git diff:

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

Obrigado @Jinmiao Luo


git diff HEAD~2 HEAD

mudança completa entre o último 2º commit e o atual.

HEAD é conveniente

dengST30
fonte
1

Eu escrevi um script que exibe diff entre dois commits, funciona bem no 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)
Jacob Abraham
fonte