1) Есть ветка master с файлом, содержащим

1

2

3

4

5

2) A берет ветку от 'master' и редактирует как

1

2

100

3

4

5

3) B берет ветку от 'master' и редактирует как

1

2

3

4

200

5

4) Теперь толчок превращается в мастер. Затем B тоже пытается толкнуть.

Что будет с Б? Есть ли конфликты слияния или нет конфликтов слияния? Причины?

1
Supun Wijerathne 20 Апр 2016 в 08:36

4 ответа

Лучший ответ

Самый простой ответ: git будет считать, что существует конфликт, если два разработчика изменили одну и ту же строку кода в одном файле (и той же ветке).

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

4
Christoph-Tobias Schenke 28 Июл 2016 в 10:19

Ваш вопрос подразумевает то, что не так.

В частности, git push подталкивает только существующие коммиты . Он ничего не объединяет и даже не пытается что-либо объединять.

В вашем вопросе задействованы три объекта (люди и репозитории). Человек A (назовем ее Алисой), человек B (назовем его Боб) и человек C (назовем его Central-Server, на самом деле он просто машина, а не человек, хотя это не важно).

Алиса и Боб оба начинают с получения копии (клона) некоторого репозитория с Центрального сервера. Они получают точно такие же клоны (поэтому их называют «клонами»): клон Алисы совпадает с клоном Боба соответствует тому, что находится на Central-Server. Для этого они запускают git clone <url>, где <url> указывает на центральный сервер (github или что-то еще), а их git сохраняет URL-адрес под именем origin (мы увидим это имя скоро снова).

Давайте нарисуем (часть) графа фиксации git, который есть у всех трех сущностей прямо сейчас:

... - C7 - C8   <-- master

Теперь Алиса и Боб вносят изменения, но они вносят разные изменения. Алиса фиксирует свое изменение:

... - C7 - C8 - A   <-- master

Затем Алиса запускает git push origin, чтобы вернуть свою работу на центральный сервер. Central-Server просматривает ее запрос, в котором говорится: «добавить фиксацию A в конец цепочки в C8 и сделать так, чтобы master указывал на A». Эта операция добавляет новые коммиты в цепочку и поэтому разрешена, поэтому Central-Server отвечает «OK» Алисе, и она все завершает. Репозиторий Central-Server теперь выглядит так же, как и репозиторий Алисы, поскольку у них обоих есть новая фиксация A после старой фиксации C8, причем master указывает на фиксацию A (и commit A, указывающий на старый C8).

Тем временем Боб внес свои изменения, а также добавил новую фиксацию, и его график фиксации теперь выглядит так:

... - C7 - C8 - B   <-- master

Боб не знает, что Алиса совершила фиксацию A и не отправила его на центральный сервер. Он переходит к git push origin, но на этот раз Central-Server получает запрос, в котором говорится: "добавить фиксацию B в конец цепочки на C8, затем заставить master указывать на {{X4}" } ". Если бы Central-Server сделал это , эффект был бы следующим:

                A
              /
... - C7 - C8 - B   <-- master

То есть коммит A останется плавающим, и ничто не указывает на него. (Ветвь master будет указывать на B, а B будет указывать обратно на C8, при этом ничто не указывает на A.) Это обычно плохое положение вещей. и git отклоняет его, поэтому Central-Server сообщает Бобу:

rejected (non-fast-forward)

Обратите внимание, что не было ни слияния, ни перестановки.

Теперь задача Боба - выполнить слияние (или перебазировать). Он должен сделать это:

  1. Получение обновления от того, у кого оно есть. У кого это есть? Он есть у Алисы, но также у Central-Server. Он просто попросил отправить сообщение на центральный сервер, который сказал Бобу «нет», так что он может также получить его от центрального сервера.

  2. Выполните слияние (или перебазирование), разрешив любые конфликты.

  3. Повторите нажатие.

Если Боб выбирает «слияние» и выполняет правильную работу по слиянию, вот его новый график фиксации:

... - C7 - C8 - A - M   <-- master
              \   /
                B

Обратите внимание на новую фиксацию слияния M. Теперь Боб может повторить попытку отправки на центральный сервер, у которого в настоящее время цепочка заканчивается на A. На этот раз Central-Server увидит запрос, чтобы master указывал на M, а поскольку M указывает на A, этот запрос будет разрешен (теперь это " перемотка вперед").

Конечно, если Алиса (или Дэйв, или Эмили, или Фрэнк) опередит Боба, добавив новые коммиты после A и отправив их обратно на центральный сервер, Бобу придется снова объединить (или переустановить) Попробуйте снова.

Как Боб со всем этим справляется?

Слияние или перебазирование - выбор Боба. В любом случае Бобу придется разрешать любые конфликты слияния - и он получит те же конфликты слияния, какой бы метод он ни использовал. И в любом случае он должен начать с выполнения:

git fetch origin

(или просто git fetch, который будет использовать origin автоматически).

Давайте посмотрим на график фиксации Боба перед перебазированием или слиянием:

                A   <-- origin/master
              /
... - C7 - C8
              \
                B   <-- master

Обратите внимание, что master Боба указывает на фиксацию B, а у Боба есть еще одна вещь - это origin/master - указывающая на фиксацию Алисы A. Это то, что делает git fetch: он передает последнюю версию с Центрального сервера (или, если Боб получает напрямую от Алисы, передает ее от нее, поскольку у нее такая же фиксация), а затем указывает на эту метку совершить. Ярлык начинается с origin/..., потому что это имя, которое мы разрешили использовать git clone: он просто вставляет origin/ перед другим именем (в данном случае master), чтобы мы можем отличить их друг от друга.

Если Боб выберет перебазировать вместо слияния, он получит свой git скопировать свой коммит B в новый коммит B':

                A       <-- origin/master
              /   \
... - C7 - C8       B'  <-- master
              \
                B

Что происходит с оригинальным B Боба? Ответ: он заброшен. Он остается в репозитории некоторое время (по умолчанию 30 дней) на случай, если он понадобится Бобу, сохраненный в reflogs Боба, но если вы (или Боб) явно не попросите git посмотреть туда, вы не увидите эти коммиты, поэтому они кажутся утерянными.

Если Боб решит объединить , он получит следующее:

                A       <-- origin/master
              /   \
... - C7 - C8       M   <-- master
              \   /
                B

Это тот же график , который мы нарисовали выше, мы только что подняли узел A, чтобы появилась стрелка, указывающая на него (помеченная origin/master).

В любом случае Боб теперь может попробовать push, поскольку его новая фиксация - либо B', либо M - указывает на фиксацию A, так что он только просит Central-Server < em> добавить новые коммиты , а не забыть или отказаться от фиксации A.

А как насчет самого слияния?

Git попытается помочь Бобу, сравнивая изменения, сделанные Алисой (добавление строки с 100) с изменениями, внесенными Бобом (добавление строки с 200). Если git решит, что эти изменения не конфликтуют друг с другом, он сохранит оба изменения. Если он решит, что два изменения влияют на одну и ту же часть файла, это вызовет у Боба конфликт слияния , пометив измененные области файла, и заставит Боба решить, как объединить их.

Боб может использовать все, что ему нравится, для достижения комбинированного результата. Боб должен убедиться, что результат верен, а затем Боб должен сообщить своему git git add окончательную версию файла и git commit зафиксировать изменения. 1 Если команда, которую он использовал для объединения изменений, была git merge, это выполнит фиксацию слияния. Если это было git rebase, это сделает новую копию B'.


1 Если Боб выбрал git rebase, он может просто использовать git rebase --continue, и он выполнит фиксацию за него. Для Боба безопасно сначала выполнить git commit, а затем сделать git rebase --continue, хотя (и во времена git 1.5 или около того нужно было делать часть фиксации вручную, прежде чем продолжить перебазировать).

Последнее замечание о git pull

Я призываю новых пользователей git начинать с git fetch, а затем делать свои собственные git merge или git rebase. Многие документы советуют начинать с git pull, но я думаю, что это ошибка. Команда git pull предназначена для удобства : она запускает git fetch, а затем выполняет либо git merge, либо git rebase, но у нее есть несколько недостатков. Ни один из них больше не является ужасно серьезным, но он не подходит для новых пользователей:

  1. Он выбирает слияние или перебазирование перед , когда вы даже можете посмотреть изменения.

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

  3. Слияние, которое он производит, является «слиянием фокстрот»: родительский объект совершает фиксацию в неправильном направлении.

  4. Аргументы сбивают с толку по сравнению с ручным слиянием: git pull origin <branch> vs git merge origin/<branch>. (Конечно, если вы хотите избежать слияния фокстротов, вы также не можете использовать последнее, но вам, вероятно, все равно следует выполнить ребазинг.)

  5. Если вы укажете слишком много аргументов (git pull origin master develop), будет выполнено слияние осьминога , о чем новичкам даже не стоит думать. :-)

  6. Раньше в нем было несколько случаев разрушающих работу ошибок. Я считаю, что все они исправлены, но использование git fetch, за которым следует отдельный git merge или git rebase, всегда позволяло избежать этих ошибок.

  7. Наконец, здесь происходит слишком много магии. Предполагается, что это будет удобно (и для тех, кто уже знаком с git, это удобно), но в итоге получается непонятно.

6
Community 23 Май 2017 в 12:16

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

В этой ситуации вы не можете нажать B:

----M-----A
     \----B

Если вы перейдете к B и наберете и используете:

git rebase A

Ваш код в вашем локальном репозитории будет выглядеть следующим образом:

----M-----A-----B

И вы также сможете нажать фиксацию B

1
Bustikiller 20 Апр 2016 в 05:51

Когда B пытается нажать, будет возвращена ошибка, потому что A были отправлены изменения в мастер. Тогда тебе следует сделать,

git fetch
git rebase origin/master

При выполнении шага rebase конфликтов не возникает, потому что A изменяет строку 3 файла, B изменяет строку 5 файла. тогда ты можешь сделать,

git commit -m ""
git push
0
henryguo 20 Апр 2016 в 05:50