Переместить незафиксированные изменения в ветку master в ветку dev и вернуть мастер - PullRequest
0 голосов
/ 08 января 2019

Я пытаюсь начать использовать git ветки, но есть кое-что, чего я не понимаю.

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

Я перепробовал все виды методов, но я беру этот в качестве примера , потому что у меня неожиданное поведение.

Таким образом, с git stash все незафиксированные изменения перемещаются в тайник, и мастер возвращается к своему последнему состоянию фиксации. Затем создается новая ветка «dev» и переключается на нее. с stash apply сохраненный тайник применяется обратно к этой ветви, и все изменения возвращаются. Теперь, когда я checkout master вижу, все измененные файлы снова присутствуют. Я ожидал, что эти изменения были только в ветке dev, и при переключении обратно на master в последнем коммите будет состояние. Я не уверен, что это нормальное поведение, и мои ожидания неверны (с большим количеством вопросов) или ветка master осталась в состоянии последнего коммита?

Ответы [ 4 ]

0 голосов
/ 08 января 2019

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

Может помочь, если вы не думаете, что 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 во многих случаях позволяет вам переключаться между коммитами. См. Оформление другой ветки, когда есть незафиксированные изменения в текущей ветке для всех подробностей крови.)

0 голосов
/ 08 января 2019

Я вообще второй анализ Филиппа.

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

# just after your stash apply on the new branch
git commit -a -m"temporary message"
git checkout master

Тогда, если вы хотите продолжить работу над новой веткой, отмените этот временный коммит, работайте до тех пор, пока не закончите, и зафиксируйте «по-настоящему»:

git checkout dev
# the following line will undo the commit but keep your changes in working tree
git reset --soft HEAD^
# work some more
git commit -a -m "real final message"
0 голосов
/ 08 января 2019

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

Git очень чувствителен к потере работы. Когда вы попросили git проверить мастер, он заметил, что у вас есть незафиксированные изменения. Он сделал быструю проверку в фоновом режиме, чтобы увидеть, потеряете ли вы какую-либо работу при переходе от dev к master. Поскольку обе ветви имели одинаковую историю (и, следовательно, не было различий в файлах между двумя ветвями), git знал, что не будет никаких конфликтов, и поэтому ваши незафиксированные изменения не были перезаписаны или «потеряны». Это просто сохранило их. Это удобная функция, когда вы понимаете, что работаете не с той веткой ... вы можете попытаться переключить ветки и забрать всю свою незафиксированную работу с собой, пока нет конфликтов. Еще раз обратите внимание, что незафиксированные изменения не сохраняются ни с одной ветвью, пока вы не подтвердите их.

0 голосов
/ 08 января 2019

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

Когда вы создаете ветку, она указывает на коммит, на котором вы сейчас находитесь. Так что и dev, и master указывают на один и тот же коммит. В этом особом случае git позволит вам свободно переключать ветки, потому что checkout не изменит никаких файлов.

Это наиболее полезно для вашего варианта использования ... Вы начинаете вносить изменения, но забыли начать новую ветку. В этом случае вы можете просто создать новую ветку и переключиться на нее, не используя git stash.


Edit:

Если после git-stash изменений не будет, их не будет после фиксации в другой ветви. Если вы действительно хотели это проверить, ничто не мешает вам сделать это:

git checkout dev
git add *
git commit -m 'Test commit'
git checkout master

# look around

git checkout dev
git reset HEAD~

Последняя строка выскакивает из вашего последнего коммита и оставляет файлы там как незафиксированные изменения. Только не нажимайте test commit.

...