В качестве небольшого дополнительного объяснения обратите внимание, что git stash
выполняется либо две, либо три фиксации. По умолчанию два; вы получите три, если используете любое написание --all
или --include-untracked
.
Эти два, или три, совершающие специальные в одном важном пути: они находятся на нет отрасли. Git находит их по специальному имени stash
. 1 Однако самое важное - это то, что Git позволяет - и заставляет - делать с этими двумя или тремя коммитами. Чтобы понять это, нам нужно посмотреть, что находится в этих коммитах.
Что внутри тайника
Каждая фиксация может содержать одну или несколько родительских коммитов. Они образуют график, где более поздние фиксации указывают на более ранние. Тайник обычно содержит две фиксации, которые я люблю вызывать i
для содержимого индексной / промежуточной области и w
для содержимого рабочего дерева. Помните также, что каждый коммит содержит снимок. При обычной фиксации этот снимок создается из содержимого области индекса / промежуточной области. Таким образом, i
фиксация на самом деле является совершенно нормальной фиксацией! Его просто нет ни в одной ветке:
...--o--o--o <-- branch (HEAD)
|
i
Если вы создаете обычный тайник, git stash
код w
теперь копирует все ваши отслеживаемые файлы рабочего дерева (во временный вспомогательный индекс). Git устанавливает, что первый родительский элемент этой w
фиксации указывает на HEAD
фиксацию, а второй родительский элемент указывает на фиксацию i
. Наконец, он stash
указывает на этот w
коммит:
...--o--o--o <-- branch (HEAD)
|\
i-w <-- stash
Если вы добавите --include-untracked
или --all
, Git сделает дополнительную фиксацию, u
между созданием i
и w
. Содержимое моментального снимка u
- это файлы, которые не отслеживаются, но не игнорируются ( --include-untracked
), или файлы, которые не отслеживаются, даже если они игнорируются ( --all
). Это дополнительное u
обязательство не имеют нет родителей, а затем , когда git stash
марка w
, она устанавливает w
«s третьего родителя это u
совершить, так что вы получите:
...--o--o--o <-- branch (HEAD)
|\
i-w <-- stash
/
u
Git также, на этом этапе, удаляет все файлы рабочего дерева, которые были завершены в u
фиксации (используя git clean
для этого).
Восстановление тайника
Когда вы собираетесь восстановить тайник, у вас есть возможность использовать --index
или не использовать его. Это говорит git stash apply
(или какой - либо из команд , которые внутренне использовать apply
, например pop
) , что он должен использоватьi
совершить , чтобы попытаться изменить свой текущий индекс. Эта модификация выполняется с помощью:
git diff <hash-of-i> <hash-of-i's-parent> | git apply --index
(более или менее; здесь есть куча мельчайших деталей, которые мешают базовой идее).
Если вы опустите --index
, git stash apply
полностью игнорирует i
фиксацию.
Если в тайнике есть только две фиксации, git stash apply
теперь можно применить w
фиксацию. Он делает это, вызывая git merge
2 (не позволяя ему фиксировать или обрабатывать результат как обычное слияние), используя исходный коммит, на котором был создан тайник ( i
родительский элемент и w
первый родительский элемент) в качестве базы слияния, w
как --theirs
commit, а ваш текущий (HEAD) коммит в качестве цели слияния. Если слияние прошло успешно, все в порядке - ну, по крайней мере, Git так думает - и git stash apply
само успешно. Если раньше вы применяли git stash pop
тайник, код теперь отбрасывает тайник. 3 Если слияние не удалось, Git объявляет, что применение не удалось. Если вы использовалиgit stash pop
, код сохраняет тайник и выдает то же состояние отказа, что и для git stash apply
.
Но если у вас есть третья фиксация - если u
в тайнике, который вы применяете, есть фиксация - тогда все меняется! Невозможно сделать вид, что u
фиксации не существует. 4 Git настаивает на извлечении всех файлов из этого u
коммита в текущее дерево работы. Это означает, что файлы должны либо не существовать вовсе, либо иметь то же содержимое, что и в u
фиксации.
Для этого вы можете использовать git clean
себя, но помните, что неотслеживаемые файлы (игнорируемые или нет) больше не существуют в репозитории Git, поэтому убедитесь, что все эти файлы могут быть уничтожены! Или вы можете создать временный каталог и переместить туда файлы для безопасного хранения - или даже сделать другой git stash save -u
или git stash save -a
, поскольку они будут работать git clean
за вас. Но это просто оставляет вам u
тайник в другом стиле, с которым вы будете разбираться позже.
+1 Это на самом деле refs/stash
. Это имеет значение, если вы создаете ветку с именем stash
: полное имя ветки refs/heads/stash
, поэтому они не конфликтуют. Но не делайте этого: Git не будет возражать, но вы запутаетесь. :-)
2git stash
код на самом деле использует git merge-recursive
прямо здесь. Это необходимо по нескольким причинам, а также имеет побочный эффект, заключающийся в том, что Git не рассматривает это как слияние при разрешении конфликтов и фиксации.
3 Вот почему я рекомендую избегать git stash pop
в пользу git stash apply
. Вы получаете шанс рассмотреть то , что получил применены, и решить , был ли он на самом деле применяется правильно. В противном случае у вас все еще есть тайник, а это значит, что вы можете использовать его, git stash branch
чтобы полностью восстановить все. Ну, при условии отсутствия этой надоедливой u
фиксации.
+4 Там действительно должно быть: git stash apply --skip-untracked
что ли. Также должен быть вариант, который означает перетаскивание всех этих u
файлов фиксации в новый каталог , например git stash apply --untracked-into <dir>
, возможно.
git stash show -p | git apply --3