Разница между `git stash show -p stash @{N}` и `git show stash @{N}`?

Я думал, что они должны быть в основном одинаковыми, но когда я попробовал

$ git stash show -p [email protected]{N}

и

$ git show [email protected]{N}

последний показывает некоторую дополнительную информацию о фиксации, но фактический разброс был намного, намного короче. (Первый показывает около дюжины файлов, но последний только один.)

Итак, что же такое разница между двумя и почему они разные?

Могу ли я также полагаться на такие вещи, как git diff [email protected]{M} [email protected]{N}?

Ответ 1

Шкатулки

Вещь, сохраненная git stash, - это то, что я сделал, чтобы называть "сумку для хранения" . Он состоит из двух 1 отдельных коммитов: фиксация "индекса" (промежуточная область) и "рабочее дерево". Компоновка рабочего дерева - забавный вид фиксации слияния.

Позвольте мне нарисовать это снова здесь (см. ответ с ссылкой для более длинной версии), чтобы просто проиллюстрировать его. Предположим для простоты, что у вас есть небольшое репо с одной ветвью и три фиксации на нем, A через C. Вы находитесь в одной ветке и делаете несколько изменений, а затем запускаете git stash save (или просто просто git stash). Это то, что вы получаете:

A - B - C     <-- HEAD=master
        |\
        i-w   <-- the "stash"

Теперь вы можете сделать (или переключиться на) другую ветвь, но, на иллюстрации, просто скажем, что вы оставите этот кошелек там и сделаете более "регулярные" фиксации на master:

A - B - C - D - E    <-- HEAD=master
        |\
        i-w   <-- stash

Дело в том, что "суммарная сумка", пара i ndex и w ork-tree фиксируется, по-прежнему ведется с той же фиксации, что и раньше. Конец не может быть изменен, и это справедливо и для кошельков закладок.

Но теперь вы делаете новый тайник, делая некоторые изменения (пока все еще на master) и снова запускаете git stash save.

Что происходит со старой сумкой? "Ссылочное имя" 2stash теперь указывает на новую сумку. Но старые кошельки все еще там. Теперь им требуется имя стиля "reflog", [email protected]{1}. 3

В любом случае, у вас есть следующее:

A - B - C - D - E     <-- HEAD=master
        |\      |\
        i-w     i-w   <-- stash
          .
           -------------- [email protected]{1}

(Когда вы используете git stash drop, stash script просто манипулирует reflog для stash ref, чтобы удалить идентификатор упавшего сумка-пакета. Поэтому все "высшие" перенумеруются. сама сумка-корзина - это сбор мусора на следующем git gc.)

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

В любое время git вам нужно указать конкретную фиксацию, вы можете сделать это любым из множества способов.

Каждая фиксация имеет "истинное имя", которое представляет собой большой уродливый хэш SHA-1, который вы видите, значения, такие как 676699a0e0cdfd97521f3524c763222f1c30a094. Вы можете это написать. Это всегда означает одно и то же совершение. Конец никогда не может быть изменен и что криптографический хэш всего содержимого коммита, поэтому, если эта конкретная фиксация вообще существует, это значение всегда является его именем.

Это нехорошее имя для людей. Таким образом, у нас есть псевдонимы: такие, как имена ветвей и тегов, и относительные имена, такие как HEAD и HEAD~2, и имена типа reflog, такие как [email protected]{yesterday} или [email protected]{1}. (Там есть команда git rev-parse), которая превращает такие строки в хеш-значения. Попробуйте: запустите git rev-parse HEAD, git rev-parse stash и т.д. Большинство вещей в git используют либо git rev-parse, либо его старший брат что делает намного больше, git rev-list, чтобы превращать имена в значения SHA-1.)

(Полное описание того, как назвать ревизию, см. gitrevisions. git использует SHA-1 для более чем просто коммиты тоже, но здесь давайте просто подумаем о коммитах.)

Git show show, git show и git diff

ОК, наконец, мы можем перейти к вашим git show vs git stash show и git diff и так далее. Давайте сначала рассмотрим git stash show, как тот, который вы должны использовать с записями. Кроме того, подкоманды git stash будут проверять, что имя фиксации вы или, если вы не назовёте фиксацию, тот, который найден с помощью ссылки stash, - выглядит как "кошелек", т.е. Является одним из этих смешных слияний совершает.

Если вы запустите git stash show -p, git показывает вам diff (-p atch). Но что именно он показывает?

Вернитесь к диаграмме с сумками. Каждая сумка-штабель вешается с определенной фиксацией. Выше "главный" тайник теперь висит от commit E, а предыдущий тайник [email protected]{1} висит от C.

То, что git stash show -p делает, сравнивает, что фиксация рабочего дерева stash, w, против фиксации, из которой висит шкаф. 4

Вы можете, конечно, сделать это сами. Скажем, вы хотите сравнить w в stash, который отцепляет commit E, от commit E, который может быть назван именем ветки master. Таким образом, вы можете запустить: git diff master stash. Здесь имя stash относится к (текущему) фиксации stash w, а master относится к commit E, поэтому это дает то же самое патч, что и git stash show -p stash. (И, если вы хотите сравнить w в [email protected]{1} с commit C, вам просто нужно запустить git diff, чтобы вы назвали эти две коммиты. Конечно, проще просто git stash show -p [email protected]{1}.) 5

Как насчет простой git show? Это немного сложнее. git show счастлив показать фиксацию, и вы дали ей ссылку stash (либо stash, либо один из вариантов reflog). То, что действительный идентификатор фиксации, и он разрешает одно из рабочих деревьев w, фиксируется в одном из пакетов. Но git show действует по-разному, когда видит слияние. Как говорится в документации:

Он также представляет фиксацию слияния в специальном формате, создаваемом git diff-tree --cc.

Итак, git show [email protected]{1} показывает вам "комбинированный diff", предполагая, что commit w является нормальным слиянием commits C и i, создавая w. В конце концов, это не нормальное слияние, хотя комбинация различий может быть действительно полезной, если вы знаете, что вы смотрите. Прочитайте документацию --cc в разделе git diff-tree, чтобы увидеть, как это работает подробно, но я должен заметить, что --cc подразумевает -c, который включает этот бит:

... перечислены только файлы, которые были изменены из всех родителей.

В случае stash, если у вас есть git add -ed файлы перед запуском git stash, так что diff i -vs- w пуст, вы не увидите эти файлы на выходе здесь.

Наконец, если вы git diff [email protected]{M} [email protected]{N}: это просто запрашивает git diff, чтобы сравнить разные теги w ork-tree. Сколько значения, которое имеет, зависит от того, что вы сравниваете, что обычно зависит от того, где прикреплены сумочки.


1 Два или три, действительно, но я собираюсь сделать это как два. Вы получаете две коммиты с git stash save (или простой git stash, что означает git stash save). Вы получаете три фиксации, если вы добавляете параметры -u или -a для сохранения невоспроизводимых или всех файлов. Это влияет на восстановление косой черты, но не на результат команды git stash show.

2 "Имя ссылки" - это просто имя, скорее как имя ветки или тега. Существует много возможных форм ссылочного названия. Филиалы и теги - это просто имена с особыми целями. "Удаленные ветки" - это еще одна форма этих ссылок, а "stash" также является ссылкой.

Фактически, HEAD - это еще одна ссылка, хотя это очень специальная ссылка. Мне так важно, что если вы удалите файл HEAD, git решит, что ваш репозиторий больше не является репозиторием.

С некоторыми исключениями для особых случаев - HEAD, ORIG_HEAD, MERGE_HEAD, и так далее-ссылки все начинаются со строки refs/. Филиалы начинаются с refs/heads/, теги начинаются с refs/tags/, а "удаленные ветки" начинаются с refs/remotes/. Другими словами, ссылки имеют "пространство имен", обычно начиная с refs/, а затем подбирают под ним другое слово, чтобы определить, где они живут.

Ссылка на stash написана refs/stash (и останавливается там, нет refs/stash/jimmy_kimmel или что-то в этом роде).

3 На самом деле это действительно использует reflog. Это означает, среди прочего, что задерживается, кроме "основного", refs/stash, будет может истечь. (К счастью, как примечания musiphil, значение по умолчанию с git 1.6.0 заключается в том, что они не истекают, вы должны настроить время истечения для них, чтобы это произошло, вероятно, это не то, что вы хотите.)

4 Умный способ сделать это, используя нотацию суффикса ^, изложено в моем другом ответе.

5 Что делать, если вы хотите посмотреть на i ndex-коммиты в этих сумках? Ах, хороший вопрос!:-) Начертание script не имеет хорошего ответа. Легкий способ увидеть это - использовать суффикс ^2, чтобы назвать второго родителя каждого тайника, который является фиксацией i. И если у вас есть тайник с третьей фиксацией, содержащей неотслеживаемые или все файлы, то третий родитель: commit w выглядит как слияние с тремя родителями, а stash^3 - третьим. Но опять же, w не является нормальным слиянием, поэтому это сложно. Вероятно, лучший простой способ взглянуть на все части тайника - превратить его в свою отдельную ветвь, используя git stash branch.

Ответ 2

Я полагаю, что это связано с причудой, где git помещает рабочий каталог и индекс отдельно. git stash show -p [email protected]{N} отобразит все изменения в кошельке, включая те, которые добавлены на сцену. Однако git show [email protected]{N} не будет включать в себя изменения, которые были поставлены до списания. Кажется, команда git stash достаточно умна, чтобы объединить их вместе, тогда как git show просто показывает вам содержимое blob [email protected]{0}

И да, git diff [email protected]{M} [email protected]{N} будет работать так, как вы ожидаете.