Мерзавец и применить

Я новичок в Git и не совсем понятно, как работает копирование.

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

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

3 ответа

Решение

Быстрая версия на вынос "TL;DR", так что можно вернуться позже и изучить больше

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

Когда вы закончите с изменениями, вы должны использовать git stash drop чтобы освободить шкатулку от коммита, на котором она была "спрятана". (А также, git stash pop это просто сокращение для "применить, затем автоматически отбросить". Тем не менее, я рекомендую оставить эти два шага раздельными, если вам не нравится результат "применить" и вы хотите повторить попытку позже.)

Длинная версия

git stash на самом деле довольно сложный.

Говорят, что "git имеет гораздо больше смысла, когда вы понимаете X", для многих различных значений "X", что обобщает на "git имеет гораздо больше смысла, когда вы понимаете git".:-)

В этом случае, чтобы действительно понять stash, вам нужно понять, как работают коммиты, ветки, область index/staging, пространство имен ссылок git и объединяет, потому что git stash создает очень специфический коммит слияния, на который ссылается имя вне обычных пространств имен - странный вид слияния, который вообще не "на ветке" - и git stash apply использует механизм слияния git, чтобы попытаться "повторно применить" изменения, сохраненные, когда была сделана особая фиксация слияния, при необходимости сохраняя различие между поэтапными и неустановленными изменениями.

К счастью, вам не нужно понимать все это, чтобы использовать git stash,

Здесь вы работаете над какой-то веткой (master) и у вас есть некоторые изменения, которые еще не готовы, поэтому вы не хотите фиксировать их в ветке. 1 Тем временем кто-то положил что-то хорошее - или, по крайней мере, вы надеетесь, что это хорошо - origin/master на удаленном репо, так что вы хотите забрать их.

Допустим, вы и они оба начали с коммитов, которые заканчиваются в - A - B - C т.е. C последний коммит, который у вас был в репо, когда вы начали работать над веткой master, Новое "что-то хорошее" фиксирует, мы позвоним D а также E,

В твоем случае ты бежишь git pull и это терпит неудачу с проблемой "рабочий каталог не чист". Итак, вы бежите git stash, Это фиксирует ваши вещи за вас, в особой, странной манере, так что ваш рабочий каталог теперь чистый. Теперь вы можете git pull,

С точки зрения рисования коммитов (график, как вы получаете с gitk или же git log --graph), теперь у вас есть что-то вроде этого. Тайник - это маленькая сумка i-w свисать с коммита вы были "на", в вашем master ветка, когда ты побежал git stash, (Причина имен i а также w является то, что это - "i"ndex / staging-area и "w" части тайника.)

- A - B - C - D - E      <-- HEAD=master, origin/master
          |\
          i-w            <-- the "stash"

Этот рисунок - то, что вы получите, если начали работать над master и никогда не делал никаких коммитов. Таким образом, ваш последний коммит был C, После изготовления тайника git pull смог добавить коммиты D а также E в ваше местное отделение master, Спрятанный мешок с работами все еще висит C,

Если вы сделали несколько своих коммитов - мы им позвоним Y, за ваш коммит и Z просто для двух коммитов - результат "stash затем pull" выглядит так:

                   .-------- origin/master
- A - B - C - D - E - M  <-- HEAD=master
            \       /
              Y - Z
                  |\
                  i-w    <-- the "stash"

На этот раз после stash повесил сумку Z, pull - что просто fetch затем merge - пришлось сделать реальное слияние, а не просто "перемотка вперед". Так что это делает коммит M Слияние совершают. origin/master ярлык по-прежнему относится к коммиту E не M, Ты сейчас на master при совершении M, который является объединением E а также Z, Ты "на шаг впереди" origin/master,

В любом случае, если вы сейчас запускаете git stash apply Сценарий stash (это сценарий оболочки, который использует множество низкоуровневых команд git "plumbing") эффективно 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, он запускает различие с --stat, Так что если вы хотите увидеть подробно, что git stash apply слиться, использовать git stash show -p, (Это не покажет вам, что git stash apply может попытаться применить из индексной части тайника; это небольшая ошибка, которую я имею со сценарием тайника.)


В любом случае, после того, как тайник применяется чисто, вы можете использовать git stash drop удалить ссылку на шкатулку, чтобы ее можно было собрать мусором. Пока не уронишь, у него есть имя (refs/stash ака stash@{0}) так что он держится "навсегда"... за исключением того факта, что если вы делаете новый тайник, stash Скрипт "выталкивает" текущий тайник в рефлог (так что его имя становится stash@{1}) и заставляет новый тайник использовать refs/stash название. Большинство записей reflog остаются на 90 дней (вы можете настроить это как-то иначе), а затем истекает. Срок действия таймеров по умолчанию не истекает, но если вы настроите это иначе, "толкаемый" тайник может потеряться, поэтому будьте осторожны с зависимостью от "сохранить навсегда", если вы начнете настраивать git по своему вкусу.

Обратите внимание, что git stash drop "выталкивает" сюда стэкинг, перенумеровывая stash@{2} в stash@{1} и делая stash@{1} стать простым stash, использование git stash list чтобы увидеть тайник


1 В любом случае, неплохо идти вперед и совершать их, а потом делать git rebase -i сдавить или исправить дальнейшие вторые, третьи, четвертые,..., nth коммиты и / или переписать временный коммит "контрольной точки". Но это не зависит от этого.

2 Это немного сложнее, потому что вы можете использовать --index пытаться держать поэтапные изменения поэтапно, но на самом деле, если вы посмотрите в сценарий, вы увидите фактическую последовательность команд git diff ... | git apply --index, Это действительно просто применить diff, в этом случае! В конце концов это вызывает git merge-recursive однако, непосредственно, чтобы слиться в рабочем дереве, позволяя вносить те же самые изменения из другого места. Равнина git apply потерпит неудачу, если ваш патч сделает что-то "хорошее" D а также E также делает.

3 Это использует магический синтаксис именования в git с небольшим предварительным планированием внутри stash скрипт. Потому что тайник - это смехотворный коммит, w имеет двух или даже трех родителей, но сценарий stash устанавливает его так, чтобы "первым родителем" был исходный коммит, C или же Z в зависимости от обстоятельств. "Второй родитель" stash^2 является индексным состоянием на момент принятия, показанным как i в маленькой висящей шкатулке, и "третий родитель", если он существует, - это файлы без возможности обработки и, возможно, игнорирования, из git stash save -u или же git stash save -a,

Обратите внимание, что в этом ответе я предполагаю, что вы не тщательно подготовили часть своего рабочего дерева и не используете git stash apply --index восстановить поэтапный указатель. Не делая ничего из этого, вы делаете i совершать в значительной степени избыточно, так что нам не нужно беспокоиться об этом во время apply шаг. Если вы используете apply --index или эквивалентные, и с постановочными предметами, вы можете попасть в гораздо большее количество угловых случаев, где тайник не будет применяться чисто.

Эти же предостережения применяются, с еще большим количеством угловых случаев, к тайникам, сохраненным с -u или же -a Третий коммит.

Для этих особо сложных случаев, git stash предоставляет способ превратить тайник в полноценную ветку - но я оставлю все это другому ответу.

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

   git stash list

выход

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

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

   git checkout 35669fb
   git stash apply

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

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

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

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

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

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

Другие вопросы по тегам