Git: Что происходит, когда я "нажимаю" другой код с разных компьютеров на одну и ту же удаленную ветвь?

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

Что происходит в случае push/pull? Если я нажму на компьютеры, у меня получится две ветки? Как решить конфликт?

1 ответ

В этом случае это точно так же, как если бы вы и Боб (или Кэрол или кто-то-не-вы) оба работали в репозиториях, которые вы клонировали из общего места, и один из вас подталкивает изменения назад к другому. Первый, кто подталкивает "победы".

Гит не знает и не заботится о том, кто ты. 1 Как видно, нажимайте, является ли нажатие "быстрой перемоткой" операции. Если это так, нажатие по умолчанию разрешено. 2 Когда кто-то сначала скажет, что Боб побеждает - когда Боб делает свой толчок, новые коммиты, которые он укладывает, находятся непосредственно на вершине ветки репозитория, получающего толчок. Это быстрая перемотка, поэтому это разрешено. Затем вы пытаетесь нажать, но ваши новые коммиты сложены рядом с некоторыми коммитами в этом репо:

D-E <-- branch (updated by Bob)
 /
...-A-B-C
 \
 F-G <-- what you're trying to push

[Примечание: это то, что видит удаленный, т. E Пульт имеет branch указывающую на E У вас есть branch указывающая на G и у вас нет D и E в вашем репозитории. Сделав толчок, вы дадите удаленным F и G а затем попросите его изменить свое представление о точках branch.]

Вы просите пульт установить branch на G Если пульт дистанционного управления делает это, происходит замирание D и E, поэтому он просто говорит вам "нет", если только вы не нажмете нажатие.

Это верно, даже если вы тоже Боб (на другом компьютере).

Решение вещей: два пути

В этой ситуации вам нужно взять на себя обязательства, которые Боб толкнул:

$ git fetch

Это приведет к совершению коммитов D и E в данном конкретном случае. Как только у вас есть это, вам необходимо изменить то, что вы хотите нажать, чтобы каким-то образом включить D и E в свои F и G

Есть два простых способа сделать это, используя git merge и git rebase.

сливаться

На этом этапе вы можете просто запустить:

$ git merge

(так как будет вверх по течению, о котором знает git). Что git merge делает, это попытка объединить разветвленную разработку и создать новый "слияние", который указывает на обе цепи:

D-E
 / \
...-A-B-C M <-- branch
 \ /
 F-G

[Примечание: здесь и ниже, это то, что у вас есть в вашем локальном репозитории.]

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

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

Преимущество фактического слияния состоит в том, что он показывает, что на самом деле произошло: Боб выиграл гонку толчков, а затем вы скорректировали свои вещи для соответствия. Недостатком является то, что трудно скомпоновать материал путем слияния: если ошибка в Боб изменяется или ошибка в вашем, сложнее сказать, где именно ошибка; история "выглядит сложной", и осложнения часто не очень полезны. Вот почему люди используют вторую альтернативу.

перебазировать

Вместо git merge вы обычно можете просто запустить:

$ git rebase

(по той же причине, что вы можете запустить git merge без дополнительных аргументов: git обычно знает о восходящем потоке и может выяснить, что нужно переупаковать). Это делается попытка "скопировать" ваши коммиты, в данном случае F и G, в несколько разные версии, которые просто складываются в конце последнего извлеченного фиксации, в этом случае E Если все пойдет хорошо, результат будет выглядеть следующим образом:

D-E
 / \
...-A-B-C F'-G' <-- branch
 \
 F-G [to be abandoned]

Ваши оригинальные F и G все еще там (и доступны под названием ORIG_HEAD пока другая перезагрузка или что-то не перезаписывает ORIG_HEAD; они также остаются в логах в течение 30 дней по умолчанию, если вам нужно их восстановить), но в значительной степени забыты если перебалансировка идет хорошо.

Как и в случае слияния, вам нужно убедиться, что rebase действительно сработала: даже если git считает, что все работает нормально, это просто выполняется по простым правилам, как и для слияния. 3 Но обычно это просто работает, и исходные F и G можно игнорировать, поэтому мы можем перерисовать граф фиксации следующим образом, называя копии F и G сейчас:

...-A-B-C-D-E-F-G <-- branch

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

Как насчет git pull?

Сценарий pull - это просто удобная вещь для выполнения git fetch за которой следует git merge или git rebase. Он буквально вызывает git fetch (слегка запутанным способом, который в более старых версиях git предотвращает появление некоторых полезных вещей, хотя детали здесь не очень важны). Когда фишка заканчивается, она буквально вызывает git merge если вы не сказали ей использовать git rebase. Если вы сказали ему использовать rebase вы также можете сказать, следует ли вызывать git rebase --preserve-merges с --preserve-merges. Во всех случаях он вызывает слияние или перестановку слегка запутанным способом. Однако, если и когда использовать --preserve-merges выходит за рамки этого ответа.

В более старых (но не слишком старых) версиях git, если у вас есть git pull do rebase, он также может восстанавливаться с восходящей ребазы, где регулярная git fetch; git rebase git fetch; git rebase sequence не может. В более новых версиях git, rebase использует новый материал для --fork-point чтобы понять это самостоятельно, так что больше нет никакого преимущества для сценария pull.

ugh, TL; DR-so, когда следует использовать git pull?

Раньше я избегал этого, потому что в нем было множество ошибок. Я думаю, что они в основном исправлены, поэтому я избегаю этого в основном по привычке. Не стесняйтесь использовать его, просто помните, что он просто кратковременный для fetch затем merge или- rebase. Решите, предпочитаете ли вы слияния или пересоздания, и соответствующим образом настройте свои настройки "rebase on pull". Как обычно с git, это слишком сложно. Или просто избегайте git pull, как и я, так что вам не нужно branch.autosetuprebase означают все значения branch.autosetuprebase.

1 Метод, по которому вы нажимаете, обычно заботится, например, используя ssh и ключи. Но эта проверка подлинности происходит до того, как git задействован; как только git работает, это не волнует, потому что ему не нужно заботиться.

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

2 Точные правила для того, что есть и не разрешены, зависят от того, кто управляет системой, которую вы нажимаете. Правила "по умолчанию по умолчанию", т.е. То, что вы получаете, если вы настраиваете вещи и ничего не меняете, заключается в том, что нажатие на ветку разрешено, если оно является быстрым или если установлен флаг силы. Ток в тег разрешен, если тег не существует или установлен флаг силы. (Некоторые более старые версии git применяют правила ветвления к тегам, но более новые - более строгие.)

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

licensed under cc by-sa 3.0 with attribution.