Як бачити зміни між двома комітами без комірок між ними?


642

Як зробити так, щоб git diffвідображатись лише різниця між двома комітами, за винятком інших комітетів між ними?


15
"git diff" завжди показує різницю між двома комітами (або довідкою і робочим каталогом тощо).
Якуб Нарбський

21
@ JakubNarębski, він запитує, як побачити різницю між змінами, внесеними однією командою, та змінами, внесеними іншим комітом. Іншими словами, різниця різниться або змінюється.
psusi

1
і якщо ви додасте параметр --dirstat = файли до команди diff, ви зробите дуже приємний знімок екрана на точні проекти та файли, які змінюються, разом із відсотком зміни. Ось так: git diff [номер комісії] [номер комісії] - dirstat = файли
Óscar Ibáñez Fernández

Відповіді:


605

ви можете просто передати 2 коміти на git diff, як:

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

1
Це працювало для мене, але тепер, як я можу подати заявку my.patchв іншу галузь?
nacho4d

2
@ nacho4d: git checkout іншої галузі && git застосувати my.patch && git add. && git commit -am "Повідомлення"
Фелікс Рабе

1
Перевагою використання git apply та patch є те, що ви можете включити перейменування та деякі інші зміни, характерні для git. Мені подобається використовувати git format-patch та git am.
Рассел

58
Ця відповідь цілком не дозволяє вирішити це питання, тому я не маю уявлення, чому в ньому так багато відгуків. ОП спеціально запитує, як НЕ отримати першу команду, яку ви віддаєте, а друга не має нічого спільного.
psusi

3
Ця відповідь абсолютно не дає нічого відповісти. Це прекрасно працює. Якщо ви відключите пізніший з двох спірних комітетів, а потім застосуйте цю різницю до цієї нової гілки, ви побачите зміни між двома комітетами без головного болю переривчастих комітетів.
Крейг Лабенц

142

Просити різницю / між / двома елементами без включення комітетів між ними має мало сенсу. Коміти - це лише знімки вмісту сховища; прохання про різницю між двома обов'язково включає їх. Тож питання полягає в тому, що ви насправді шукаєте?

Як запропонував Вільям, збирання вишні може надати вам дельту одного комітету, переобладнаного на інший. Це є:

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

Це приймає "abcdef", порівнює його з його безпосереднім предком, а потім застосовує цю різницю поверх '012345'. Потім з'являється ця нова різниця - єдиною зміною є контекст, що походить від "012345", а не від безпосереднього предка abcdef. Звичайно, у вас можуть виникнути конфлікти тощо, тому в більшості випадків це не дуже корисний процес.

Якщо вас просто цікавить сам abcdef, ви можете зробити:

$ git log -u -1 abcdef

Це порівнює abcdef з його безпосереднім предком, один, і, як правило, те, що ви хочете.

І звичайно

$ git diff 012345..abcdef

дає всі відмінності між цими двома комітами.

Це допомогло б краще зрозуміти, що ви намагаєтесь досягти - як я вже згадував, прохання про різницю між двома комітами без того, що між ними, насправді не має сенсу.


41
Погодьтесь, що взагалі не має сенсу порівнювати два коміти. Але git насправді хороший тим, що не говорить тобі, як ти повинен думати. Припустимо, у вас є дві гілки, кожна з чітко виражених комірок, схожих на те, що вони вносять однакові зміни в ті самі набори файлів. Я хотів би мати можливість використовувати git, щоб сказати мені, чи ці два патчі однакові, не довіряючи очам. Я думаю, що в цьому є корисність.
Кріс Кліленд

9
@ChrisCleeland, утиліта interdiff може стати в нагоді в цьому випадку. Використовуйте git diff, щоб отримати різницю кожного комітету проти його безпосереднього батьків, тоді використовуйте interdiff для порівняння diff.
bdonlan

3
@ChrisCleeland, git не зберігає патчі. У ньому зберігається вміст файлу. У нього є схема стиснення, яка використовує дельти, але джерела дельти не обов'язково співвідносяться з фактичною історією файлів.
bdonlan

11
Різниця між двома комісіями, за винятком інших комітетів на відповідних гілках, має ідеальний сенс: одна фіксація вишня була обрана від іншої, але може мати певні відмінності. Ви хочете побачити, якими вони є, не захаращуючись усіма іншими незв’язаними лайнами, різними між двома гілками.
psusi

2
Або скажіть, що ви перезавантажуєте майстер на гілку функції та повинні вирішувати конфлікти. Після порівняння origin/featurebranch#HEADз local/featurebranch#HEADможе допомогти вам переконатися , що ви не зробили нічого гидоту під час врегулювання конфліктів.
lefnire

91

Для порівняння двох git виконує 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 git diff 123456 abcdef.
OneOfOne

15
@OneOfOne Це не робить те саме. Те, що ви запропонували, порівняло б дерева кожного коду, показуючи один патч . Те, що я роблю (і @plexoos), порівнює два виправлення , кожен з яких введений окремими коміксами - іншими словами, diffз результатами двох diffс. Це включає читання та порівняння двох вхідних потоків. diff(GNU або Unix diff) може це зробити, хоча git diffне може. Деякі можуть задатися питанням, чому хтось хотів би це зробити. Я зараз в середині займаюся цим, прибираючи злиття, яке пішло погано.
Менахем

1
не включатиме це gnu diff всіх метаданих у git 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

ПРИМІТКА . Для перевірки відмінності між комісіями між ними, вам не потрібно ставити ідентифікатори комісій.


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 замовлення 012345
$ git cherry-pick -n abcdef
$ git diff - кешована

Дякую, це гарна ідея, щоб перевірити результат після скоєння комбайна. Наприклад, ви можете перевірити свою філію з необрізаними фірмами, а вишневе - вибирати, що бачили, щоб побачити, чи все пройшло з інтерактивною базою даних. Крім того, коли майстер пішов попереду гілки.
акостадінов

10

Як що до цього:

git diff abcdef 123456 | less

Зручно просто передавати його менше, якщо ви хочете порівнювати багато різних відмінностей.


6

Оскільки Git 2.19, ви можете просто використовувати:

git range-diff rev1...rev2 - порівняйте два дерева фіксації, починаючи з їх спільного предка

або git range-diff rev1~..rev1 rev2~..rev2 - порівняти зміни, внесені двома даними комітами


4

Мої aliasналаштування у ~/.bashrcфайлі для 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

Мої aliasналаштування у ~/.zshrcфайлі для git diff:

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

Дякую @Jinmiao Luo


git diff HEAD~2 HEAD

повна зміна між останнім 2-м комітом і поточним.

HEAD зручно


1

Я написав сценарій, який відображає різницю між двома комітами, добре працює на 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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.