Ответ 1
Быстрая версия "TL; DR" для вытаскивания, поэтому можно вернуться позже и изучить больше
git stash
висит сумка - это своеобразная форма фиксации слияния, которая не находится ни на одной ветке - на текущем фиксаторе HEAD
. Позднее git stash apply
, когда вы находитесь в любом коммите, возможно, в другом коммите, затем пытается восстановить изменения, которые git вычисляет, просматривая как висячий сумка, так и фиксацию, из которой она зависает.
Когда вы закончите с изменениями, вы должны использовать git stash drop
, чтобы отпустить сумку с фиксации, которая была "спрятана". (И, git stash pop
является просто сокращением для "apply", затем автоматически отбрасывается ". Я рекомендую сохранить два шага отдельно, однако, если вам не нравится результат" применить ", и вы хотите попробовать позже.)
Длинная версия
git stash
на самом деле довольно сложный.
Было сказано, что "git имеет гораздо больше смысла, как только вы понимаете X" , для многих разных значений "X", который обобщается на "git, имеет смысл, если вы понимаете git".: -)
В этом случае, чтобы действительно понять stash
, вам нужно понять, как коммиты, ветки, пространство имен указателей/промежуточных областей, git и слияние всей работы, потому что git stash
создает очень своеобразное merge commit, на которое ссылается имя за пределами обычных именных пространств - странный вид слияния, который вообще не находится на "ветке", и git stash apply
использует git механизм слияния, чтобы попытаться "повторно применить" "изменения, сохраненные при выполнении специфического слияния, при необходимости сохраняя различие между поэтапными и неустановленными изменениями.
К счастью, вам действительно не нужно понимать все это, чтобы использовать git stash
.
Здесь вы работаете над какой-либо веткой (master
), и у вас есть некоторые изменения, которые еще не готовы, поэтому вы не хотите их фиксировать на ветке. 1 Тем временем кто-то еще положил что-то хорошее - или, по крайней мере, вы надеетесь, что это хорошо - на origin/master
на удаленном репо, поэтому вы хотите выбрать их.
Скажем, что вы и они оба начали с коммитов, заканчивающихся на - A - B - C
, т.е. C
- это окончательная фиксация, которую вы имели в своем репо, когда вы начали работать с веткой master
. Новое "что-то хорошее" совершит, мы назовем D
и E
.
В вашем случае вы используете git pull
, и он не работает с проблемой "рабочий каталог не чист". Итак, вы запустите git stash
. Это фиксирует ваши вещи для вас, в его особой странной форме stash-y, так что ваш рабочий каталог теперь чист. Теперь вы можете git pull
.
С точки зрения рисования коммитов (график, который вы получаете с помощью gitk
или git log --graph
), теперь у вас есть что-то вроде этого. Стойка - это маленькая сумка из i-w
, зависящая от того, что вы были "on", в вашей ветке master
, когда вы запустили git stash
. (Причиной имен i
и w
является то, что это элементы "i" ndex/staging-area и "w" ork-tree в stash.)
- A - B - C - D - E <-- HEAD=master, origin/master
|\
i-w <-- the "stash"
Этот рисунок - это то, что вы получаете, если вы начали работать над master
и никогда не совершали никаких коммитов. Таким образом, последнее совершение было таким образом C
. После создания тайника git pull
смог добавить коммиты D
и E
в локальную ветвь master
. Спрятанный мешок работы все еще свисает C
.
Если вы сделали несколько своих собственных коммитов, мы назовем их Y
, для вашей фиксации и Z
просто для двух коммитов - результат "stash then pull" выглядит следующим образом:
.-------- origin/master
- A - B - C - D - E - M <-- HEAD=master
\ /
Y - Z
|\
i-w <-- the "stash"
На этот раз, после stash
поместил свою сумку с Z
, pull
, которая просто fetch
, а затем merge
- должна была выполнить реальное слияние, а не просто "быстрая перемотка вперед" ". Таким образом, он делает commit M
, фиксацию слияния. Метка origin/master
по-прежнему относится к commit E
, а не M
. Теперь вы находитесь в master
при фиксации M
, который является слиянием E
и Z
. Вы" один впереди" от origin/master
.
В любом случае, если вы сейчас запустите git stash apply
, то stash script (это оболочка script, которая использует множество низкоуровневых команд git ") эффективно 2 делает следующее:
git diff stash^ stash > /tmp/patch
git apply /tmp/patch
Это разграничивает stash
, который называет w
-рабочее дерево "частью трюка - против правильного родителя 3. Другими словами, он обнаруживает" то, что вы изменили" между надлежащим фиксацией родителя (C
или Z
, если это необходимо) и спрятанным деревом. Затем он применяет изменения к текущей версии, которая является либо E
, либо M
, снова в зависимости от того, где вы начали.
Кстати, git stash show -p
действительно просто запускает ту же самую команду git diff
(без курса > /tmp/patch
). Без -p
он запускает diff с --stat
. Поэтому, если вы хотите увидеть, что git stash apply
будет сливаться, используйте git stash show -p
. (Это не покажет вам, что git stash apply
может попытаться применить из индексной части кошелька, хотя это небольшая проблема с ящиком script.)
В любом случае, как только прикрытие будет применяться чисто, вы можете использовать git stash drop
, чтобы удалить ссылку на сумку, чтобы ее можно было собрать в мусор. До тех пор, пока вы его не упадете, у него есть имя (refs/stash
, aka [email protected]{0}
), так что он остается "вечно"... за исключением того факта, что если вы создаете новый тайник, то кнопки stash
script "msgstr "" "текущий трюк в stash reflog (так, чтобы его имя становилось [email protected]{1}
) и заставляет новый stash использовать имя refs/stash
. Большинство записей в loglog хранятся в течение 90 дней (вы можете настроить это, чтобы они были разными), а затем истекает. Штампы не истекают по умолчанию, но если вы настроите это в противном случае, "толкаемый" тайник может потеряться, поэтому будьте осторожны в зависимости от "сохранить навсегда", если вы начнете настраивать git по своему усмотрению.
Обратите внимание, что git stash drop
"всплывает" стек стека, перенумеровывая [email protected]{2}
на [email protected]{1}
и делая [email protected]{1}
равным stash
. Используйте git stash list
, чтобы увидеть стоп файл.
1 Нехорошо идти вперед и фиксировать их в любом случае, а затем сделать более позднюю git rebase -i
для сквоша или исправления второй, третьей, четвертой,..., n-й коммитов и/или переписать временную "контрольную точку". Но это не зависит от этого.
2 Это немного сложнее, потому что вы можете использовать --index
, чтобы попытаться сохранить поэтапные изменения, но на самом деле, если вы посмотрите в script, вы увидите фактическая последовательность команд git diff ... | git apply --index
. В этом случае действительно применяется diff. В конце концов, он вызывает git merge-recursive
напрямую, однако, для объединения в дереве работ, позволяя внести те же изменения из других источников. Простой git apply
не удастся, если ваш патч сделает что-то, что "хороший материал" совершает D
и E
также.
3 В этом случае используется git мажоритарный синтаксис родительского именования с небольшим предварительным планированием внутри stash
script. Поскольку stash - это напуганное объединение слиянием, w
имеет двух или даже трех родителей, но stash script устанавливает его таким образом, чтобы "первым родителем" был исходный commit, C
или Z
, в зависимости от ситуации, "Второй родитель" stash^2
- это состояние индекса во время фиксации, показанное как i
в маленькой висячей сумке, а "третий родитель", если он существует, является нестационарным и, возможно, проигнорированные файлы, от git stash save -u
или git stash save -a
.
Обратите внимание, что в этом ответе я предполагаю, что вы не тщательно выполнили часть своего рабочего дерева и что вы не используете git stash apply --index
для восстановления поэтапного индекса. Не делая этого, вы оказываете i
commit довольно избыточным, так что нам не нужно беспокоиться об этом во время шага apply
. Если вы используете apply --index
или эквивалент, и у вас есть поставленные элементы, вы можете получить гораздо больше угловых случаев, где тайник не будет применяться чисто.
Эти же оговорки применяются, с еще большим количеством угловых случаев, к записям, сохраненным с помощью -u
или -a
, которые имеют третью фиксацию.
Для этих сверхжестких случаев git stash
предоставляет способ превратить stash в полноценную ветку, но я оставлю все это на другой ответ.