Предварительные заметки
Наблюдение здесь заключается в том, что после того, как вы начинаете работать branch1
(забывая или не понимая, что было бы хорошо branch2
сначала переключиться на другую ветку ), вы запускаете:
git checkout branch2
Иногда Git говорит: «Хорошо, ты сейчас на branch2!» Иногда Git говорит: «Я не могу этого сделать, я бы потерял некоторые из ваших изменений».
Если Git не позволит вам сделать это, вы должны зафиксировать свои изменения, чтобы сохранить их где-нибудь навсегда. Вы можете использовать их git stash
для сохранения; это одна из вещей, для которой он предназначен. Обратите внимание, что git stash save
или на git stash push
самом деле означает «Зафиксируйте все изменения, но ни на одной ветке, а затем удалите их с того места, где я сейчас». Это позволяет переключаться: у вас нет текущих изменений. Вы можете затем git stash apply
их после переключения.
Боковая панель: git stash save
старый синтаксис; git stash push
была введена в Git версии 2.13, чтобы исправить некоторые проблемы с аргументами git stash
и допустить новые опции. Оба делают то же самое, когда используются основными способами.
Вы можете перестать читать здесь, если хотите!
Если Git не позволит вам переключиться, у вас уже есть лекарство: используйте git stash
или git commit
; или, если ваши изменения тривиальны для воссоздания, используйте, git checkout -f
чтобы форсировать его. Этот ответ полностью о том, когда Git позволит вам, git checkout branch2
даже если вы начали вносить некоторые изменения. Почему это работает иногда , а не в другие времена?
Правило здесь простое с одной стороны и сложное / трудно объяснимое с другой:
Вы можете переключать ветки с незафиксированными изменениями в рабочем дереве, если и только если упомянутое переключение не требует сглаживания этих изменений.
То есть - и обратите внимание, что это все еще упрощено; Есть несколько очень сложных угловых случаев со ступенями git add
s, git rm
s и тому подобными branch1
. А git checkout branch2
придется сделать это:
- Для каждого файла, который находится в,
branch1
а не в branch2
, 1 удалите этот файл.
- Для каждого файла, который находится в,
branch2
а не в branch1
, создайте этот файл (с соответствующим содержимым).
- Для каждого файла, который находится в обеих ветвях, если версия в
branch2
отличается, обновите версию рабочего дерева.
Каждый из этих шагов может затормозить что-то в вашем рабочем дереве:
- Удаление файла является «безопасным», если версия в рабочем дереве совпадает с версией, зафиксированной в
branch1
; «небезопасно», если вы внесли изменения.
- Создание файла в том виде, в котором оно выглядит,
branch2
является «безопасным», если его сейчас нет. 2 «Небезопасно», если оно существует сейчас, но имеет «неправильное» содержание.
- И, конечно, замена версии файла рабочего дерева другой версией «безопасна», если версия рабочего дерева уже зафиксирована
branch1
.
Создание нового branch ( git checkout -b newbranch
) всегда считается «безопасным»: никакие файлы не будут добавлены, удалены или изменены в рабочем дереве как часть этого процесса, и область index / staging также не затронута. (Предостережение: это безопасно при создании новой ветки без изменения начальной точки новой ветки; но если вы добавите другой аргумент, например git checkout -b newbranch different-start-point
, это, возможно, придется изменить вещи, чтобы перейти к different-start-point
. Git затем будет применять правила безопасности проверки как обычно .)
1 Это требует, чтобы мы определили, что означает, что файл находится в ветви, что, в свою очередь, требует правильного определения слова ветвь . (Смотрите также Что именно мы подразумеваем под «ветвь»? ) Вот, что я на самом деле означает это коммит , к которому имя-ветви рассасывается: файл , чей путь является в случае производит хэш. Этот файл не в , если вы получите сообщение об ошибке. Существование пути в вашем индексе или рабочем дереве не имеет значения при ответе на этот конкретный вопрос. Таким образом, секрет здесь состоит в том, чтобы изучить результат на каждомP
branch1
git rev-parse branch1:P
branch1
P
git rev-parse
branch-name:path
, Это либо не удается, потому что файл находится "в" не более одной ветви, или дает нам два идентификатора хеша. Если два идентификатора хеша совпадают , файл одинаков в обеих ветвях. Никаких изменений не требуется. Если идентификаторы хеша различаются, файл отличается в двух ветвях и должен быть изменен для переключения ветвей.
Ключевым понятием здесь является то, что файлы в коммитах заморожены навсегда. Файлы, которые вы будете редактировать, явно не заморожены. Мы, по крайней мере изначально, смотрим только на несоответствия между двумя замороженными коммитами. К сожалению, мы или Git-же приходится иметь дело с файлами, которые не коммита вы собираетесь перейти от и находятся в коммит вы собираетесь перейти. Это приводит к остальным сложностям, поскольку файлы могут также существовать в индексе и / или в рабочем дереве, без необходимости существования этих двух конкретных замороженных коммитов, с которыми мы работаем.
2 Это может считаться «своего рода безопасным», если оно уже существует с «правильным содержимым», так что Git не должен его создавать. Я помню, по крайней мере, некоторые версии Git, позволяющие это сделать, но тестирование только сейчас показывает, что это считается «небезопасным» в Git 1.8.5.4. Тот же аргумент будет применяться к измененному файлу, который оказывается измененным в соответствии с ветвью, подлежащей переключению. Опять же, 1.8.5.4 просто говорит "будет перезаписано", хотя. См. Также конец технических заметок: моя память может быть неисправна, поскольку я не думаю, что правила дерева чтения изменились с тех пор, как я впервые начал использовать Git в версии 1.5.
Имеет ли значение, являются ли эти изменения поэтапными или нет?
Да, в некотором смысле. В частности, вы можете внести изменения, а затем «де-изменить» файл рабочего дерева. Вот файл в двух ветках, он отличается от branch1
и branch2
:
$ git show branch1:inboth
this file is in both branches
$ git show branch2:inboth
this file is in both branches
but it has more stuff in branch2 now
$ git checkout branch1
Switched to branch 'branch1'
$ echo 'but it has more stuff in branch2 now' >> inboth
На этом этапе рабочий файл дерева inboth
совпадает с файлом branch2
, даже если мы включены branch1
. Это изменение не предназначено для фиксации, что git status --short
показано здесь:
$ git status --short
M inboth
Пробел-то-М означает «измененный, но не поэтапный» (или, точнее, копия рабочего дерева отличается от поэтапной / индексной копии).
$ git checkout branch2
error: Your local changes ...
Хорошо, теперь давайте создадим копию рабочего дерева, которая, как мы уже знаем, также соответствует копии branch2
.
$ git add inboth
$ git status --short
M inboth
$ git checkout branch2
Switched to branch 'branch2'
Здесь готовые и рабочие копии совпадали с тем, что было branch2
, поэтому проверка была разрешена.
Давайте попробуем еще один шаг:
$ git checkout branch1
Switched to branch 'branch1'
$ cat inboth
this file is in both branches
Внесенное мною изменение теперь потеряно из области подготовки (потому что касса записывает через область подготовки). Это немного угловой случай. Изменение не было, но тот факт , что я поставил его, как исчез.
Давайте создадим третий вариант файла, отличный от ветки-копии, а затем установим рабочую копию в соответствии с текущей версией ветки:
$ echo 'staged version different from all' > inboth
$ git add inboth
$ git show branch1:inboth > inboth
$ git status --short
MM inboth
Два M
s здесь означают: промежуточный файл отличается от HEAD
файла, а файл рабочего дерева отличается от промежуточного файла. Версия рабочего дерева соответствует branch1
(ака HEAD
) версии:
$ git diff HEAD
$
Но git checkout
не позволят оформить заказ:
$ git checkout branch2
error: Your local changes ...
Давайте установим branch2
версию как рабочую версию:
$ git show branch2:inboth > inboth
$ git status --short
MM inboth
$ git diff HEAD
diff --git a/inboth b/inboth
index ecb07f7..aee20fb 100644
--- a/inboth
+++ b/inboth
@@ -1 +1,2 @@
this file is in both branches
+but it has more stuff in branch2 now
$ git diff branch2 -- inboth
$ git checkout branch2
error: Your local changes ...
Даже если текущая рабочая копия совпадает с текущей branch2
, промежуточный файл не соответствует, поэтому a git checkout
потеряет эту копию и git checkout
будет отклонена.
Технические заметки - только для безумно любопытных :-)
Основным механизмом реализации всего этого является индекс Git . Индекс, также называемый «промежуточной областью», - это место, где вы строите следующий коммит: он начинается с текущего коммита, т. Е. Независимо от того, что вы извлекли сейчас, а затем каждый раз, когда вы git add
файл, вы заменяете версию индекса. с тем, что у вас есть в вашем рабочем дереве.
Помните, что дерево работы - это то, где вы работаете со своими файлами. Здесь они имеют свою обычную форму, а не какую-то специальную форму, полезную только для Git, как в коммитах и в индексе. Таким образом, вы извлекаете файл из коммита через индекс и затем в рабочее дерево. После того, как вы git add
его измените, вы перейдете в индекс. Таким образом, на самом деле для каждого файла есть три места: текущий коммит, индекс и рабочее дерево.
Когда вы запускаете git checkout branch2
, то, что делает Git под прикрытием, это сравнивает коммит с коммитомbranch2
с тем, что находится в текущем коммите и в индексе сейчас. Любой файл, который соответствует тому, что там сейчас, Git может оставить в покое. Это все нетронутым. Любой файл, который одинаков в обеих фиксациях , Git также может оставить в покое - и это те, которые позволяют вам переключать ветки.
Большая часть Git, включая переключение коммитов, является относительно быстрой из-за этого индекса. На самом деле в индексе находится не каждый файл, а хеш каждого файла . Копия самого файла хранится в хранилище как то, что Git называет объектом BLOB-объекта . Это похоже на то, как файлы хранятся в коммитах: коммиты на самом деле не содержат файлов , они просто приводят Git к хеш-идентификатору каждого файла. Таким образом, Git может сравнивать хэш-идентификаторы - в настоящее время строки длиной 160 битов - чтобы определить, имеют ли коммиты X и Y один и тот же файл или нет. Затем он может сравнить эти хеш-идентификаторы с хеш-идентификатором в индексе.
Это то, что приводит ко всем вышеперечисленным угловым случаям. У нас есть коммиты X и Y, которые оба имеют файл path/to/name.txt
, и у нас есть индексная запись для path/to/name.txt
. Возможно, все три хэша совпадают. Может быть, два из них совпадают, а один нет. Может быть, все три разные. И у нас также может быть another/file.txt
это только в X или только в Y и есть или нет в индексе сейчас. Каждый из этих различных случаев требует отдельного рассмотрения: нужно ли Git копировать файл из коммита в индекс или удалять его из индекса, чтобы переключиться с X на Y ? Если так, то это также должноскопируйте файл в рабочее дерево или удалите его из рабочего дерева. И если это так, то версии индекса и рабочего дерева должны лучше соответствовать по крайней мере одной из зафиксированных версий; в противном случае Git будет забивать некоторые данные.
(Полные правила для всего этого описаны не в git checkout
документации, как вы могли бы ожидать, а в git read-tree
документации в разделе «Слияние двух деревьев» .)
git checkout -m
, который объединяет ваше рабочее дерево и изменения индекса в новой проверке.У вас есть два варианта: спрятать ваши изменения:
потом, чтобы вернуть их:
или поместите ваши изменения в ветку, чтобы вы могли получить удаленную ветку и затем объединить ваши изменения с ней. Это одна из величайших вещей в git: вы можете создать ветку, зафиксировать ее и затем внести другие изменения в ветку, в которой вы были.
Вы говорите, что это не имеет никакого смысла, но вы делаете это только для того, чтобы вы могли объединить их по своему желанию после выполнения тяги. Очевидно, что другой ваш выбор - зафиксировать копию вашей ветви, а затем выполнить ее. Предполагается, что вы либо не хотите этого делать (в этом случае я озадачен, что вам не нужна ветка), либо вы боитесь конфликтов.
источник
git stash apply
? здесь документы.git stash pop
его, и он удалит тайник из вашего списка, если он успешно применяется.git stash pop
, если только вы не собираетесь хранить журнал тайников в своей истории репоЕсли новая ветвь содержит правки, которые отличаются от текущей ветки для этого конкретного измененного файла, то она не позволит вам переключать ветки, пока изменение не будет зафиксировано или сохранено. Если измененный файл является одинаковым в обеих ветвях (то есть зафиксированная версия этого файла), вы можете свободно переключаться.
Пример:
Это касается как неотслеживаемых, так и отслеживаемых файлов. Вот пример для неотслеживаемого файла.
Пример:
Хороший пример того, почему вы хотели бы перемещаться между ветвями при внесении изменений, может быть, если вы выполняете некоторые эксперименты на мастере, хотите их зафиксировать, но пока не мастерите ...
источник
Правильный ответ
git checkout -m origin/master
Он объединяет изменения из основной ветки источника с локальными даже незафиксированными изменениями.
источник
Если вы не хотите, чтобы эти изменения были зафиксированы, сделайте это
git reset --hard
.Далее вы можете оформить заказ в нужную ветку, но помните, что незафиксированные изменения будут потеряны.
источник