Comment voir les changements entre deux commits sans commits entre les deux?

643

Comment faites-vous pour git diffafficher uniquement la différence entre deux validations, à l'exclusion des autres validations intermédiaires?

Vadim Kotov
la source
15
"git diff" montre toujours la différence entre deux commits (ou commit et répertoire de travail, etc.).
Jakub Narębski
21
@ JakubNarębski, il demande comment voir la différence entre les changements introduits par une commande et les changements introduits par un autre commit. En d'autres termes, le diff de diffs ou interdiff.
psusi
1
et si vous ajoutez le paramètre --dirstat = files à la commande diff, vous prendrez une très belle capture d'écran sur les projets et fichiers exacts qui sont modifiés, ainsi qu'un pourcentage de changement. Comme ceci: git diff [commit-number] [commit-number] --dirstat = files
Óscar Ibáñez Fernández

Réponses:

606

vous pouvez simplement passer les 2 commits à git diff comme:

-> git diff 0da94be  59ff30c > my.patch
-> git apply my.patch
OneOfOne
la source
1
Cela a fonctionné pour moi, mais maintenant, comment puis-je postuler my.patchdans une autre branche?
nacho4d
2
@ nacho4d: git checkout other-branch && git apply my.patch && git add. && git commit -am "Message"
Felix Rabe
1
L'avantage d'utiliser git apply vs patch est que vous pouvez inclure des renommages et d'autres modifications spécifiques à git. J'aime utiliser git format-patch et git am.
Russell
58
Cette réponse ne répond absolument pas à la question, donc je n'ai aucune idée pourquoi elle a autant de votes positifs. L'OP demande spécifiquement comment NE PAS obtenir la première commande que vous donnez, et la seconde n'a rien à voir avec quoi que ce soit.
psusi
3
Cette réponse ne manque absolument de rien. Cela fonctionne parfaitement. Si vous dérivez la dernière des deux validations en question, puis appliquez ce diff à cette nouvelle branche, vous verrez les changements entre les deux validations sans maux de tête des validations intermittentes.
Craig Labenz du
142

Demander la différence / entre / deux commits sans inclure les commits entre les deux n'a guère de sens. Les validations ne sont que des instantanés du contenu du référentiel; demander la différence entre deux les inclut nécessairement. La question est donc: que cherchez-vous vraiment?

Comme William l'a suggéré, la sélection des cerises peut vous donner le delta d'un seul commit rebasé au-dessus d'un autre. C'est:

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

Cela prend le commit 'abcdef', le compare à son ancêtre immédiat, puis applique cette différence au-dessus de '012345'. Cette nouvelle différence est alors montrée - le seul changement est que le contexte vient de '012345' plutôt que de 'l'ancêtre immédiat d'abcdef. Bien sûr, vous pouvez avoir des conflits, etc., donc ce n'est pas un processus très utile dans la plupart des cas.

Si vous êtes juste intéressé par abcdef lui-même, vous pouvez faire:

$ git log -u -1 abcdef

Cela compare abcdef à son ancêtre immédiat, seul, et c'est généralement ce que vous voulez.

Et bien sûr

$ git diff 012345..abcdef

vous donne toutes les différences entre ces deux commits.

Cela aiderait à avoir une meilleure idée de ce que vous essayez de réaliser - comme je l'ai mentionné, demander la différence entre deux commits sans ce qui se trouve entre les deux n'a pas de sens.

bdonlan
la source
41
Je conviens qu'en général, il n'est pas très logique de comparer deux commits. Mais git est vraiment bon pour ne pas vous dire comment vous devriez penser. Supposons que vous ayez deux branches, chacune avec des validations distinctes qui semblent apporter les mêmes modifications aux mêmes ensembles de fichiers. J'aimerais pouvoir utiliser git pour me dire si ces deux patchs sont les mêmes sans avoir à me faire confiance. Je pense qu'il y a de l'utilité dans tout cela.
Chris Cleeland
9
@ChrisCleeland, l'utilitaire interdiff peut être utile dans ce cas. Utilisez git diff pour obtenir le diff de chaque commit par rapport à son parent immédiat, puis utilisez interdiff pour comparer les diff.
bdonlan
3
@ChrisCleeland, git ne stocke pas les correctifs. Il stocke le contenu du fichier. Il a un schéma de compression qui utilise des deltas, mais les sources delta ne sont pas nécessairement corrélées avec l'historique réel des fichiers.
bdonlan
11
La différence entre les deux validations, à l'exclusion des autres validations sur leurs branches respectives, est parfaitement logique: une validation a été sélectionnée à partir de l'autre, mais peut présenter quelques différences subtiles. Vous voulez voir ce qu'ils sont sans être encombré par toutes les autres conneries non liées qui sont différentes entre les deux branches.
psusi
2
Ou dites que vous rebasiez le maître sur une branche de fonctionnalité et devez résoudre les conflits. Ensuite comparer origin/featurebranch#HEADà local/featurebranch#HEADpeut vous aider à vous assurer avez rien Muck lors de la résolution des conflits.
lefnire
91

Pour comparer deux git commits 12345 et abcdef en tant que patchs, on peut utiliser la commande diff comme

diff <(git show 123456) <(git show abcdef)
plexoos
la source
8
Pourquoi utiliseriez-vous GNU diff avec git?
OneOfOne
7
@OneOfOne git diff <(git show 123456) <(git show abcdef)ne fonctionne pas; diff <(...) <(...)Est-ce que. (Je viens de l'essayer).
Menachem
@Menachem git diff 123456 abcdef.
OneOfOne
15
@OneOfOne Cela ne fait pas la même chose. Ce que vous avez suggéré serait de comparer les arbres de chaque commit, montrant un seul patch . Ce que je fais (et @plexoos) est en train de comparer deux patchs , chacun ayant été introduit par des validations distinctes - en d'autres termes, diffla sortie de deux diffs. Cela implique la lecture et la comparaison de deux flux d'entrée. diff(GNU ou Unix diff) peut le faire, mais git diffpas. Certains peuvent se demander pourquoi on voudrait faire cela. Je suis en train de faire ça en ce moment, de nettoyer une fusion qui a mal tourné.
Menachem
1
cela n'inclura-t-il pas le diff gnu de toutes les métadonnées dans le diff git?
joelb
61
git diff <a-commit> <another-commit> path

Exemple:

git diff commit1 commit2 config/routes.rb

Il montre la différence sur ce fichier entre ces validations.

roadev
la source
24

Pour vérifier les modifications complètes:

  git diff <commit_Id_1> <commit_Id_2>

Pour vérifier uniquement les fichiers modifiés / ajoutés / supprimés:

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

REMARQUE : pour vérifier les différences sans validation entre les deux, vous n'avez pas besoin de mettre les ID de validation.

bit_cracker007
la source
20

Disons que vous avez ceci

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

Et vous voulez vous assurer que Ac'est la même chose que A0.

Ça fera l'affaire:

$ git diff B A > B-A.diff
$ git diff D A0 > D-A0.diff
$ diff B-A.diff D-A0.diff
Juanpa
la source
3
Peut également être abrégé en one-liner tout comme la réponse de @plexoos : diff <(git diff B A) <(git diff D A0)(même résultat qu'avec
Git
14

Supposons que vous souhaitiez voir la différence entre les validations 012345 et abcdef. Ce qui suit devrait faire ce que vous voulez:

$ git checkout 012345
$ git cherry-pick -n abcdef
$ git diff --cached
William Pursell
la source
Merci, c'est une bonne idée de vérifier votre résultat après un commit de squash. Par exemple, vous pouvez extraire votre branche avec des validations non écrasées et choisir votre validation écrasée pour voir si tout s'est bien passé avec le rebase interactif. De plus, lorsque le maître a devancé la succursale.
akostadinov
10

Et ça:

git diff abcdef 123456 | less

Il est pratique de simplement le rediriger vers moins si vous souhaitez comparer de nombreux diff différents à la volée.

Flow Overerstack
la source
6

Depuis Git 2.19, vous pouvez simplement utiliser:

git range-diff rev1...rev2 - comparer deux arbres de commit, en commençant par leur ancêtre commun

ou git range-diff rev1~..rev1 rev2~..rev2 - comparer les changements introduits par 2 commits donnés

Tomáš Diviš
la source
4

Mes aliasparamètres dans le ~/.bashrcfichier pour 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
la source
2

Mes aliasparamètres dans le ~/.zshrcfichier pour git diff:

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

Merci @Jinmiao Luo


git diff HEAD~2 HEAD

changement complet entre le dernier 2e commit et le dernier.

HEAD est pratique

dengST30
la source
1

J'ai écrit un script qui affiche les différences entre deux commits, fonctionne bien sur 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
la source