Вы видите, что существует очень большая разница между вашим рабочим деревом - областью, где вы выполняете свою работу - и фактическим коммитами .
Может помочь, если вы не думаете, что Git хранит изменений , потому что это не так. Он хранит снимки . Каждый коммит - это полная, полная копия всех ваших файлов - ну, все те, которые вы зафиксировали, но это отчасти избыточно, поэтому просто подумайте «все файлы». Сохраненные моментальные снимки замораживаются навсегда - ну, они живут так же долго, как и сам коммит, но, по крайней мере, это навсегда.
Эти снимки сохраняются в специальном сжатом замороженном формате только для Git. Они бесполезны ни для чего, кроме Git, поэтому для работы с ними - на git checkout
- Git должен разложить их в нормальную форму, где они незамерзшие и полезные, и, конечно, вы можете изменить их вокруг , Это ваше рабочее дерево , где вы будете выполнять свою работу.
Итак, есть две «активные» копии каждого файла: замороженная в коммите и та, над которой вы работаете. Когда вы просматриваете вещи как изменения , вы сравниваете эти разные копии.
Git добавляет сюда дополнительную складку, потому что на самом деле есть третья копия каждого файла. Когда Git впервые извлекает коммит, он копирует замороженные файлы только Git в то, что Git вызывает, по-разному: index , область подготовки или кэш, Здесь файлы по-прежнему предназначены только для Git, но теперь они уже не совсем заморожены. Они более слякотные, готовые к замораживанию, и, в частности, вы можете заменить индексную копию любого файла новой копией. Вот что делает git add
.
Когда вы используете git stash
, Git на самом деле делает коммит или, точнее, два коммита. Затем он очищает индекс и рабочее дерево, чтобы они соответствовали текущей или HEAD
фиксации. Главная особенность этого stash commit заключается в том, что он не находится в любой ветви. Таким образом, вы можете переключиться на другую ветку и использовать git apply
для ее повторного извлечения независимо от того, на какую ветку вы переключились.
Переключение на другую ветвь означает выбор коммита tip этой ветки в качестве текущей фиксации, а этой ветки - текущей ветки . Git скопирует все эти файлы, из этого снимка, в индекс и рабочее дерево. Если вы были чисты раньше, вы снова окажетесь в чистой ситуации, но теперь коммит HEAD
- это другой коммит. Теперь вы можете снова изменить рабочее дерево.
Как заметил Филип Коулинг , существует особый случай, когда две ветви имена идентифицируют одинаковый коммит. Если мы рисуем коммиты в виде цепочки с двумя именами ветвей, указывающими на последний коммит:
...--o--o--* <-- master, develop
тогда, в некотором смысле, не имеет значения, будем ли мы git checkout master
или git checkout develop
. Оба идентифицируют тот самый последний коммит. Активный снимок в HEAD
, индекс и фиксация будут иметь одинаковое содержимое . Git не нужно ничего делать с индексом и рабочим деревом, если вы переключаетесь с коммита *
на коммит *
, поскольку вы на самом деле ничего не переключаете в них!
Но это еще не все, что git checkout
делает! В частности, проверка master
говорит Git присоединить имя HEAD
к имени master
, а проверка develop
говорит Git вместо этого присоединить его к develop
. Это влияет на то, какое имя ветви обновляется обновляется , когда вы делаете новый коммит. Если вы настроены так:
...--o--o--* <-- master, develop (HEAD)
, а затем сделать новый коммит, Git переместит имя develop
, чтобы указать на новый коммит:
...--o--o--o <-- master
\
* <-- develop (HEAD)
Обратите внимание, что HEAD
все еще только что присоединен к develop
. Хитрость в том, что develop
теперь идентифицирует этот новый коммит.
Гит маkes новый коммит из того, что находится в index , а не из того, что находится в рабочем дереве. Вы используете git add
, чтобы сообщить Git: скопировать файл рабочего дерева поверх индексного файла. Это подготовит файл к замораживанию, сжав его до специального формата Git, используя все, что находится в версия рабочего дерева прямо тогда. Затем git commit
просто нужно мгновенно заморозить копии индекса, чтобы сделать замороженные копии всех файлов нового коммита.
Итак, для этого конкретного случая, git stash
сделал коммит, очистил ваше рабочее дерево, затем вы заново прикрепили HEAD
к тому же коммиту, но с другим именем ветви, а затем повторно применил изменения вашего рабочего дерева. Копить и применять было совершенно не нужно. Однако если бы develop
и master
указывали на разные коммиты, вам часто приходилось бы делать это stash-and-apply. (Даже в этом случае Git во многих случаях позволяет вам переключаться между коммитами. См. Оформление другой ветки, когда есть незафиксированные изменения в текущей ветке для всех подробностей крови.)