Шкатулки
Вещь, сохраненная git stash
, - это то, что я назвал "сумкой для хранения". Он состоит из двух1 отдельных коммитов: коммита "index" (промежуточная область) и коммита "дерева работы". Коммит рабочего дерева — это забавный вид коммита слияния.
Позвольте мне снова нарисовать это здесь (см. Ссылочный ответ для более длинной версии), просто чтобы проиллюстрировать это должным образом. Давайте для простоты предположим, что у вас есть небольшое репо с одной веткой и тремя фиксациями в ней, с 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
.
Что происходит со старой заначкой? "Имя ссылки"2 stash
теперь указывает на новый тайник. Но старые коммиты из заначки все еще там. Только сейчас им требуется имя стиля "reflog", stash@{1}
.3
В любом случае, то, что у вас есть сейчас, это:
A - B - C - D - E <-- HEAD=master
|\ |\
i-w i-w <-- stash
.
-------------- stash@{1}
(Когда вы используете git stash drop
, скрипт stash просто манипулирует reflog для stash
ref, чтобы удалить идентификатор сброшенного stash-bag. Вот почему все «более высокие» перенумеровываются. Фактический stash-bag сам по себе является мусором, собранным на следующий git gc
.)
Следующий фрагмент является ключом к пониманию того, что происходит.
Каждый раз, когда git нужно, чтобы вы назвали конкретный коммит, вы можете сделать это любым из многих способов.
У каждого коммита есть «настоящее имя», которое представляет собой большой уродливый хэш SHA-1, который вы видите, такие значения, как 676699a0e0cdfd97521f3524c763222f1c30a094
. Вы можете написать это. Это всегда означает один и тот же коммит. Коммиты никогда не могут быть изменены, и это криптографический хэш всего содержимого коммита, поэтому, если этот конкретный коммит вообще существует, это значение всегда является его именем.
Однако это не очень хорошее имя для людей. Итак, у нас есть псевдонимы: такие вещи, как имена веток и тегов, и относительные имена, такие как HEAD
и HEAD~2
, и имена в стиле журнала ссылок, такие как HEAD@{yesterday}
или master@{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 stash show, git show и git diff
Хорошо, наконец-то мы можем перейти к вашим git show
против git stash show
, и git diff
, и так далее. Давайте сначала займемся git stash show
, так как это тот, который вы должны использовать с тайниками. Более того, подкоманды git stash
проверят, что указанный вами коммит — или, если вы не назвали коммит, найденный по ссылке stash
— «выглядит как» тайник, т. е. является одним из этих забавных коммитов слияния.
Если вы запустите git stash show -p
, git покажет вам разницу (-p
atch). Но что именно он показывает?
Вернитесь к схеме с заначками. Каждая заначка связана с конкретным коммитом. Выше «основной» тайник теперь висит на коммите E
, а более ранний тайник stash@{1}
висит на C
.
Что делает git stash show -p
, так это сравнивает фиксацию этого тайника в рабочем дереве, w
, с фиксацией, от которой висит тайник.4
Вы, конечно, можете сделать это сами. Допустим, вы хотите сравнить w
в stash
, который зависает от фиксации E
, с фиксацией E
, которая может быть названа именем ветки master
. Итак, вы можете запустить: git diff master stash
. Здесь имя stash
относится к (текущей) фиксации тайника w
, а master
относится к фиксации E
, так что получается точно такой же патч, что и git stash show -p stash
. (И если вы хотите сравнить w
в stash@{1}
с фиксацией C
, вам просто нужно запустить git diff
так, чтобы вы назвали эти две фиксации. Конечно, проще просто git stash show -p stash@{1}
.)5
Как насчет простого git show
? Это немного сложнее. git show
рад показать коммит, а вы дали ему ссылку stash
(либо сам stash
, либо один из вариантов reflog). Это допустимый идентификатор коммита, и он разрешается в один из w
коммитов рабочего дерева в одном из тайников. Но git show
ведет себя по-другому, когда видит фиксацию слияния. Как говорится в документации:
Он также представляет фиксацию слияния в специальном формате, созданном git diff-tree --cc
.
Таким образом, git show stash@{1}
показывает вам "комбинированный diff", предполагая, что коммит w
представляет собой обычное слияние коммитов C
и i
, производящее w
. В конце концов, это не обычное слияние, хотя комбинированный diff может быть действительно полезным, если вы знаете, на что смотрите. Прочтите документацию по --cc
под git diff-tree
, чтобы узнать, как это работает в деталях, но я отмечу, что --cc
подразумевает -c
, который включает этот бит:
... перечисляет только файлы, которые были изменены от всех родителей.
В случае stash
, если вы git add
отредактировали файлы до запуска git stash
, так что разница i
-vs-w
пуста, вы не увидите эти файлы в выводе здесь.
Наконец, если вы git diff stash@{M} stash@{N}
: это просто просьба git diff
сравнить разные коммиты w
ork-tree. Насколько это важно, зависит от того, что вы сравниваете, что, как правило, зависит от того, где прикреплены сумки для хранения.
1На самом деле два или три, но я нарисую их двумя. Вы получаете два коммита с git stash save
(или простым git stash
, что означает git stash save
). Вы получите три коммита, если добавите параметры -u
или -a
для сохранения неотслеживаемых или всех файлов. Это влияет на восстановление тайника, но не на вывод команды git stash show
.
2"Ссылочное имя" — это просто имя, похожее на имя ветви или тега. Существует много возможных форм ссылочного имени. Ветки и теги — это просто имена специального назначения. "Удаленные ветки" - это еще одна форма этих ссылок, и "тайник" также является ссылкой.
На самом деле HEAD
— это просто еще одна ссылка, хотя и очень особенная. Я настолько важен, что если вы удалите файл HEAD
, git решит, что ваш репозиторий в конце концов больше не является репозиторием.
За исключением некоторых особых случаев — HEAD
, ORIG_HEAD
, MERGE_HEAD
и т. д. — все ссылки начинаются со строки refs/
. Ветки начинаются с refs/heads/
, теги начинаются с refs/tags/
, а "удаленные ветки" начинаются с refs/remotes/
. Другими словами, у ссылок есть «пространство имен», обычно начинающееся с refs/
, а затем под ним следует еще одно слово, чтобы определить, где они находятся.
Ссылка на тайник пишется refs/stash
(и останавливается на этом, нет refs/stash/jimmy_kimmel
или чего-то подобного).
3На самом деле, это действительно действительно использует журнал ссылок. Это означает, помимо прочего, что тайники, кроме "основного", refs/stash
, будут могут истечь. (К счастью, как музыкальные заметки, по умолчанию, начиная с git 1.6.0, они не истекают; вы должны настроить время истечения срока их действия, чтобы это произошло - что, вероятно, не то, что вам нужно в любом случае.)
4Умный способ сделать это с использованием нотации суффикса ^
описан в моем другой ответ.
5Что, если вы хотите посмотреть на i
ndex-коммиты в этих тайниках? А, хороший вопрос! :-) Скрипт тайника не имеет хорошего ответа. Самый простой способ увидеть это — использовать суффикс ^2
для имени второго родителя каждого тайника, который является коммитом i
. И, если у вас есть тайник с третьим коммитом, содержащим неотслеживаемые или все файлы, это третий родитель: коммит w
выглядит как слияние трех родителей, а stash^3
получает третий. Но опять же, w
не является обычным слиянием, так что это сложно. Вероятно, лучший простой способ просмотреть все части тайника — превратить его в отдельную ветку, используя git stash branch
.
person
torek
schedule
13.02.2014