Подтвердить что ты не робот

Оформить другую ветку, если в текущей ветке есть незафиксированные изменения

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

Однако иногда Git позволяет мне проверять другую ветку, не совершая или не сбрасывая эти изменения, и переносит эти изменения на проверку ящика.

Какое здесь правило? Имеет ли значение, являются ли изменения поставленными или неустановленными? Проведение изменений в другой ветке не имеет для меня никакого смысла, почему Git позволяет это иногда? То есть, полезно ли в некоторых ситуациях?

4b9b3361

Ответ 1

Предварительные примечания

Наблюдение заключается в том, что после того, как вы начнете работать в branch1 (забыв или не осознав, что было бы хорошо сначала переключиться на другую ветвь branch2), вы запустите:

git checkout branch2

Иногда Git говорит: "Хорошо, теперь вы на ветке2". Иногда Git говорит: "Я не могу этого сделать, я потеряю некоторые из ваших изменений".

Если Git не позволит вам это сделать, вы должны зафиксировать свои изменения, чтобы сохранить их где-то постоянным. Вы можете использовать git stash для их сохранения; это одна из целей, которые она предназначена для. Обратите внимание, что git stash save на самом деле означает "Зафиксировать все изменения, но ни в какой ветки вообще, а затем удалить их с того места, где я сейчас". Это позволяет переключаться: теперь у вас нет изменений в процессе. После этого вы можете git stash apply после них.

Вы можете перестать читать здесь, если хотите!

Если Git не позволит вам переключаться, у вас уже есть исправление: используйте git stash или git commit; или, если ваши изменения тривиальны для воссоздания, используйте git checkout -f, чтобы заставить его. Этот ответ о том, когда Git позволит вам git checkout branch2, даже если вы начали делать некоторые изменения. Почему это работает иногда, а не в другое время?

Правило здесь прост одним способом и сложным/трудно объяснимым в другом:

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

Это - и, пожалуйста, обратите внимание, что это все еще упрощено; есть некоторые сложнейшие угловые случаи с поэтапными git add s, git rm и такие - предположим, что вы находитесь на branch1. A git checkout branch2 должен был бы сделать это:

  • Для каждого файла, находящегося в branch1, а не в branch2, удалите этот файл.
  • Для каждого файла, находящегося в branch2, а не в branch1, создайте этот файл (с соответствующим содержимым).
  • Для каждого файла, который находится в обеих ветвях, если версия в branch2 отличается, обновите рабочую версию дерева.

Каждый из этих шагов может сбивать что-то в вашем дереве:

  • Удаление файла является "безопасным" , если версия в дереве дерева такая же, как в версии branch1; это "небезопасно", если вы внесли изменения.
  • Создание файла так, как оно отображается в branch2, является "безопасным" , если оно не существует. 1 Это "небезопасно", если оно существует сейчас, но имеет "неправильное" содержимое.
  • И, конечно же, замена версии дерева файлов другой версией "безопасна", если версия дерева рабочих файлов уже привязана к branch1.

Создание новой ветки (git checkout -b newbranch) всегда считается "безопасным" : никакие файлы не будут добавлены, удалены или изменены в дереве дерева как часть этого процесса, а индекс/промежуточная область также не затронута, (Предостережение: безопасно создавать новую ветку без изменения начальной точки ветвления, но если вы добавите другой аргумент, например git checkout -b newbranch different-start-point, это может измениться, чтобы перейти на different-start-point. Git будет затем примените правила безопасности выписки, как обычно.)

Имеет ли значение, являются ли изменения поставленными или неустановленными?

Да, в некотором роде. В частности, вы можете выполнить изменение, а затем "де-изменить" файл дерева работ. Здесь файл в двух ветвях, который отличается от 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

Пространство-then-M означает "измененный, но не поставленный" (или, точнее, копирование рабочего дерева отличается от поэтапной/индексной копии).

$ 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 здесь означают: поэтапный файл отличается от файла HEAD, а рабочий файл дерева отличается от поэтапного файла. Версия рабочего дерева соответствует версии branch1 (aka 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, поэтапный файл не работает, поэтому git checkout потеряет эту копию, а git checkout будет отклонен.


1 Он может считаться "сортировочным", если он уже существует с "правильным содержимым", так что Git не нужно создавать его в конце концов. Я помню хотя бы некоторые версии Git, позволяющие это, но сейчас тестирование показывает, что это считается "небезопасным" в Git 1.8.5.4. Тот же аргумент применим к модифицированному файлу, который будет изменен, чтобы соответствовать ветке to-be-switch-to. Опять же, 1.8.5.4 просто говорит, что "будет перезаписано". См. Также конец технических заметок: моя память может быть неисправной, поскольку я не думаю, что правила чтения-дерева изменились с тех пор, как я впервые начал использовать Git в версии 1.5.something.


Технические примечания - только для безумно любопытных: -)

Основным механизмом реализации для всего этого является индекс Git. Индекс, также называемый "промежуточной областью", - это то место, где вы создаете следующую фиксацию: она начинается с соответствия текущей фиксации, т.е. Независимо от того, что вы проверили сейчас, а затем каждый раз, когда вы git add файл, вы заменяете версию индекса с тем, что у вас есть в рабочем дереве.

Помните, что дерево работы - это то, где вы работаете с вашими файлами. Здесь у них есть их нормальная форма, а не какая-то специальная форма только-полезная-to- Git, как в коммитах и ​​в индексе. Таким образом, вы извлекаете файл из фиксации через индекс, а затем в дерево работы. После его изменения вы git add к индексу. Таким образом, для каждого файла есть три места: текущий фиксатор, индекс и дерево работы.

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

Большая часть Git, включая commit-switching, относительно быстро из-за этого индекса. Что фактически в индексе не каждый файл, а скорее каждый хэш файла. Копия самого файла сохраняется как то, что 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, в соответствии с раздел "Два слияния деревьев" .)

Ответ 2

У вас есть два варианта: запишите свои изменения:

git stash

а затем, чтобы вернуть их:

git stash apply

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

Вы говорите, что это не имеет никакого смысла, но вы делаете это только для того, чтобы вы могли объединить их по своему усмотрению после выполнения упражнения. Очевидно, ваш другой выбор состоит в том, чтобы зафиксировать на своей копии ветки, а затем сделать тягу. Презумпция заключается в том, что вы либо не хотите этого делать (в этом случае я озадачен тем, что вам не нужна ветка), либо вы боитесь конфликтов.

Ответ 3

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

Пример:

$ echo 'hello world' > file.txt
$ git add file.txt
$ git commit -m "adding file.txt"

$ git checkout -b experiment
$ echo 'goodbye world' >> file.txt
$ git add file.txt
$ git commit -m "added text"
     # experiment now contains changes that master doesn't have
     # any future changes to this file will keep you from changing branches
     # until the changes are stashed or committed

$ echo "and we're back" >> file.txt  # making additional changes
$ git checkout master
error: Your local changes to the following files would be overwritten by checkout:
    file.txt
Please, commit your changes or stash them before you can switch branches.
Aborting

Это относится к файлам без следа, а также к отслеживаемым файлам. Вот пример для файла без следа.

Пример:

$ git checkout -b experimental  # creates new branch 'experimental'
$ echo 'hello world' > file.txt
$ git add file.txt
$ git commit -m "added file.txt"

$ git checkout master # master does not have file.txt
$ echo 'goodbye world' > file.txt
$ git checkout experimental
error: The following untracked working tree files would be overwritten by checkout:
    file.txt
Please move or remove them before you can switch branches.
Aborting

Хорошим примером того, почему вы хотите двигаться между ветвями при внесении изменений, было бы, если бы вы выполняли некоторые эксперименты над мастером, хотели их совершить, но еще не освоили...

$ echo 'experimental change' >> file.txt # change to existing tracked file
   # I want to save these, but not on master

$ git checkout -b experiment
M       file.txt
Switched to branch 'experiment'
$ git add file.txt
$ git commit -m "possible modification for file.txt"

Ответ 4

Полностью согласен с @totrek как более упрощенным объяснением. Скажем, у нас есть файл, который уже был зафиксирован и перенесен на git на ветку A, которая не существует в ветки b, поэтому вы не можете перенести изменение на контейнер, который еще выходит из другой ветки

Ответ 5

Если обе ветки указывают на одну и ту же фиксацию, git разрешает проверку иначе.