¿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?

Vadim Kotov
fuente
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
Uno de uno
fuente
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.

bdonlan
fuente
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)
plexoos
fuente
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.

roadev
fuente
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.

bit_cracker007
fuente
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
Juanpa
fuente
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é
William Pursell
fuente
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.

Flow Overstack
fuente
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

Tomáš Diviš
fuente
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
Jinmiao Luo
fuente
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

dengST30
fuente
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)
Jacob Abraham
fuente