Git скрыть и применить

Я новичок в git и не совсем понимаю, как работает stashing.

Скажем, я работаю над мастером ветки и стараюсь git pull и получать сообщение об ошибке, что мои локальные изменения будут перезаписаны и должны быть спрятаны или зафиксированы. Если я не выполнил какие-либо изменения и не выполнил git stash, тогда выполните git pull и успешно обновите, что произойдет, когда я git stash apply?

В общем случае, если кто-то еще модифицирует файлы, и я запускаю git pull, что происходит, когда я run git stash apply? он перезаписывает файлы, которые были только что обновлены, независимо от того, были ли они поставлены, когда я их спрятал? Переписывает ли он каждый файл, который я только что обновил с помощью git pull, с файлами, которые были спрятаны?

Ответы

Ответ 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 в полноценную ветку, но я оставлю все это на другой ответ.

Ответ 2

Вообще нефиксированные изменения всегда плохие. Либо ваши изменения хороши, а затем совершают их, или они плохи, чем отбрасывают их. Выполнение любых операций git при наличии незафиксированных изменений может вызвать проблемы, а git не сможет вам помочь, так как git не знает ничего, что вы не совершали.

Сказав это, вернемся к вашему вопросу.;)

Git, как правило, довольно умный. Когда вы применяете свой тайник, он пытается объединить ваши изменения с другими изменениями. В большинстве случаев это работает.

Если изменения действительно конфликтуют, потому что вы изменили те же строки по-другому, git сообщит вам, и вам придется самостоятельно разрешить конфликт. - Даже в этом случае git поможет вам с помощью git mergetool, который запустит подходящую команду, чтобы отобразить конфликты и разрешить их по одному.

Ответ 3

команда stash git запоминает, откуда появляется штамп:

   git stash list

out put

   [email protected]{0}: WIP on master.color-rules.0: 35669fb [NEW] another step toward initial cube

Где вы можете видеть, на какой SHA1 он был включен. Поэтому, если вы используете git stash, git pull, git stash применить, и у вас возник конфликт, он не будет отбрасываться (это будет только если вы уроните или если приложение было успешным). Таким образом, вы всегда можете получить SHA1 из списка git и

   git checkout 35669fb
   git stash apply

и он гарантирован для работы. Я рекомендую использовать параметр -b и указать имя ветки для этого восстановления.

Как я уже сказал, мой любимый рабочий процесс заключается в том, чтобы ВСЕГДА проверять под новым "персонализированным" именем, чтобы избежать таких проблем.