¿Cómo ver los cambios entre dos confirmaciones sin confirmaciones intermedias?


643

¿Cómo hacer que git diffsolo muestre la diferencia entre dos confirmaciones, excluyendo las otras confirmaciones intermedias?


15
"git diff" siempre muestra la diferencia entre dos commits (o commit y directorio de trabajo, etc.).
Jakub Narębski

21
@ JakubNarębski, pregunta cómo ver la diferencia entre los cambios introducidos por un comando y los cambios introducidos por otro commit. En otras palabras, la diferencia de diffs o interdiff.
psusi

1
y si agrega el parámetro --dirstat = files al comando diff, tomará una muy buena captura de pantalla de los proyectos y archivos exactos que se cambian, junto con un porcentaje de cambio. De esta manera: git diff [commit-number] [commit-number] --dirstat = files
Óscar Ibáñez Fernández

Respuestas:


606

simplemente puede pasar los 2 commits a git diff como:

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

1
Eso funcionó para mí, pero ahora, ¿cómo puedo aplicar my.patcha otra sucursal?
nacho4d

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

1
La ventaja de usar git apply vs. patch es que puede incluir cambios de nombre y algunos otros cambios que son específicos de git. Me gusta usar git format-patch y git am.
Russell

58
Esta respuesta no responde la pregunta, por lo que no tengo idea de por qué tiene tantos votos positivos. El OP pregunta específicamente cómo NO obtener el primer comando que das, y el segundo no tiene nada que ver con nada.
psusi

3
Esta respuesta no deja de responder nada. Funciona perfectamente Si ramifica la última de las dos confirmaciones en cuestión, luego aplique esta diferencia a esa nueva ramificación, verá los cambios entre las dos confirmaciones sin dolor de cabeza de las confirmaciones intermitentes.
Craig Labenz

142

Pedir la diferencia / entre / dos confirmaciones sin incluir las confirmaciones intermedias tiene poco sentido. Los commits son solo instantáneas del contenido del repositorio; preguntar por la diferencia entre dos necesariamente los incluye. Entonces la pregunta es, ¿qué estás buscando realmente?

Como sugirió William, la selección de cerezas puede darte el delta de un solo commit rebasado sobre otro. Es decir:

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

Esto toma commit 'abcdef', lo compara con su antepasado inmediato, luego aplica esa diferencia sobre '012345'. Luego se muestra esta nueva diferencia: el único cambio es que el contexto proviene de '012345' en lugar del antepasado inmediato de 'abcdef. Por supuesto, puede obtener conflictos, etc., por lo que no es un proceso muy útil en la mayoría de los casos.

Si solo está interesado en abcdef, puede hacer lo siguiente:

$ git log -u -1 abcdef

Esto compara abcdef con su antepasado inmediato, solo, y generalmente es lo que desea.

Y por supuesto

$ git diff 012345..abcdef

te da todas las diferencias entre esos dos commits.

Sería útil tener una mejor idea de lo que está tratando de lograr; como mencioné, pedir la diferencia entre dos confirmaciones sin lo que hay entre ellas no tiene sentido.


41
Estoy de acuerdo en que, en general, no tiene mucho sentido comparar dos commits. Pero git es realmente bueno para no decirte cómo deberías pensar. Suponga que tiene dos ramas, cada una con confirmaciones distintas que parecen estar haciendo los mismos cambios en los mismos conjuntos de archivos. Me gustaría poder usar git para decirme si estos dos parches son iguales sin tener que confiar en mis ojos. Creo que hay utilidad en esto.
Chris Cleeland

99
@ChrisCleeland, la utilidad interdiff puede ser útil en ese caso. Use git diff para obtener el diff de cada commit contra su padre inmediato, luego use interdiff para comparar los diffs.
bdonlan

3
@ChrisCleeland, git no almacena parches. Almacena el contenido del archivo. Tiene un esquema de compresión que usa deltas, pero las fuentes delta no están necesariamente correlacionadas con el historial real de los archivos.
bdonlan

11
La diferencia entre las dos confirmaciones excluyendo otras confirmaciones en sus respectivas ramas tiene mucho sentido: una confirmación fue seleccionada de la otra, pero puede tener algunas diferencias sutiles. Desea ver cuáles son sin estar abarrotados con todas las demás basura no relacionada que es diferente entre las dos ramas.
psusi

2
O digamos que reescribe el maestro en una rama de características y debe resolver los conflictos. Después comparando origin/featurebranch#HEADa local/featurebranch#HEADpuede ayudar a asegurarse de que no hizo nada lodo durante la resolución de conflictos.
lefnire

91

Para comparar dos git commits 12345 y abcdef como parches, se puede usar el comando diff como

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

8
¿Por qué usarías GNU diff con git?
OneOfOne

77
@OneOfOne git diff <(git show 123456) <(git show abcdef)no funciona; diff <(...) <(...)hace. (Solo lo intenté).
Menachem

@Menachem git diff 123456 abcdef.
OneOfOne

15
@OneOfOne Eso no hace lo mismo. Lo que sugirió compararía los árboles de cada confirmación, mostrando un solo parche . Lo que yo (y @plexoos) estamos haciendo es comparar dos parches , cada uno de ellos introducido por commits separados, en otras palabras, diffobteniendo la salida de dos diffs. Esto implica leer y comparar dos flujos de entrada. diff(GNU o Unix diff) puede hacer eso, mientras git diffque no puede. Algunos pueden preguntarse por qué uno querría hacer eso. Estoy en el medio de hacer eso ahora, limpiando una fusión que salió mal.
Menachem

1
¿No incluirá esto el gnu diff de todos los metadatos en el git diff?
joelb

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

Ejemplo:

git diff commit1 commit2 config/routes.rb

Muestra la diferencia en ese archivo entre esas confirmaciones.


24

Para verificar los cambios completos:

  git diff <commit_Id_1> <commit_Id_2>

Para verificar solo los archivos modificados / agregados / eliminados:

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

NOTA : Para verificar diff sin commit en el medio, no necesita poner los identificadores de commit.


20

Digamos que tienes esto

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

Y quieres asegurarte de que Asea ​​lo mismo que A0.

Esto hará el truco:

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

3
También se puede acortar como una línea como la respuesta de @plexoos : diff <(git diff B A) <(git diff D A0)(el mismo resultado que con git show)
pogosama

14

Suponga que quiere ver la diferencia entre commits 012345 y abcdef. Lo siguiente debe hacer lo que quieras:

$ git pago 012345
$ git cherry-pick -n abcdef
$ git diff - caché

Gracias, es una buena idea verificar su resultado después de aplastar las confirmaciones. Por ejemplo, puede verificar su rama con confirmaciones no aplastadas y elegir su confirmación aplastada para ver si todo salió bien con la nueva versión interactiva. Además, cuando el maestro se adelantó a la rama.
akostadinov

10

¿Qué hay de esto?

git diff abcdef 123456 | less

Es útil simplemente canalizarlo a menos si desea comparar muchas diferencias diferentes sobre la marcha.


6

Desde Git 2.19, simplemente puede usar:

git range-diff rev1...rev2 - compara dos árboles de compromiso, comenzando por su ancestro común

o git range-diff rev1~..rev1 rev2~..rev2 - compare los cambios introducidos por 2 commits dados


4

Mi aliasconfiguración en el ~/.bashrcarchivo 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

2

Mi aliasconfiguración en el ~/.zshrcarchivo para git diff:

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

Gracias @Jinmiao Luo


git diff HEAD~2 HEAD

cambio completo entre la última segunda confirmación y la actual.

HEAD es conveniente


1

Escribí un script que muestra la diferencia entre dos confirmaciones, funciona bien en 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)
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.