Я использую Git уже пару месяцев в проекте с одним другим разработчиком. У меня есть несколько лет опыта работы с SVN , поэтому я полагаю, что я привнес много багажа в отношения.
Я слышал, что Git отлично подходит для ветвления и слияния, и пока я просто не вижу этого. Конечно, ветвление просто невероятно, но когда я пытаюсь слиться, все идет к черту. Теперь я привык к этому из SVN, но мне кажется, что я просто обменял одну систему контроля версий на другую.
Мой партнер говорит мне, что мои проблемы проистекают из моего желания слиться волей-неволей, и что во многих ситуациях мне следует использовать rebase вместо слияния. Например, вот рабочий процесс, который он заложил:
clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature
git checkout master
git merge my_new_feature
По сути, создайте ветвь объектов, ВСЕГДА перебирайте с мастера на ветку и сливайте с ветки обратно на ветку. Важно отметить, что филиал всегда остается локальным.
Вот рабочий процесс, с которого я начал
clone remote repository
create my_new_feature branch on remote repository
git checkout -b --track my_new_feature origin/my_new_feature
..work, commit, push to origin/my_new_feature
git merge master (to get some changes that my partner added)
..work, commit, push to origin/my_new_feature
git merge master
..finish my_new_feature, push to origin/my_new_feature
git checkout master
git merge my_new_feature
delete remote branch
delete local branch
Есть два существенных различия (я думаю): я использую слияние всегда, а не перебазирование, и я помещаю свою ветвь функций (и мои коммиты ветви функций) в удаленный репозиторий.
Я рассуждаю об удаленной ветке так, что я хочу, чтобы моя работа поддерживалась во время работы. Наш репозиторий автоматически резервируется и может быть восстановлен, если что-то пойдет не так. Мой ноутбук не, или не так тщательно. Поэтому я ненавижу иметь код на моем ноутбуке, который не отражен где-то еще.
Мое рассуждение о слиянии вместо rebase заключается в том, что слияние кажется стандартным, а rebase - расширенной функцией. У меня такое ощущение, что я пытаюсь не продвинутая настройка, поэтому ребаз должен быть ненужным. Я даже просмотрел новую книгу «Прагматическое программирование» по Git, и в ней подробно рассматриваются слияния и едва упоминается ребазинг.
Во всяком случае, я следил за своим рабочим процессом в недавней ветке, и когда я попытался объединить его с мастером, все пошло к черту. Было множество конфликтов с вещами, которые не должны были иметь значения. Конфликты просто не имели смысла для меня. Мне потребовался день, чтобы все уладить, и в итоге я вынужден был принудительно подтолкнуть удаленного мастера, поскольку мой локальный мастер разрешил все конфликты, но удаленный все еще не был доволен.
Каков «правильный» рабочий процесс для чего-то подобного? Git должен сделать ветвление и слияние очень легким, и я просто не вижу этого.
Обновление 2011-04-15
Это, кажется, очень популярный вопрос, поэтому я подумала, что обновлю свой двухлетний опыт с тех пор, как я его впервые спросила.
Оказывается, исходный рабочий процесс корректен, по крайней мере, в нашем случае. Другими словами, это то, что мы делаем, и это работает:
clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git checkout master
git merge my_new_feature
На самом деле, наш рабочий процесс немного отличается, так как мы склонны делать сквош-слияния вместо сырых слияний. ( Примечание: это противоречиво, см. Ниже. ) Это позволяет нам превратить всю нашу ветку объектов в один коммит на master. Затем мы удаляем нашу ветку функций. Это позволяет нам логически структурировать наши коммиты на master, даже если они немного запутаны в наших ветках. Итак, вот что мы делаем:
clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git checkout master
git merge --squash my_new_feature
git commit -m "added my_new_feature"
git branch -D my_new_feature
Противоречие слияния сквоша - Как указали несколько комментаторов, слияние сквоша отбросит всю историю вашей ветки. Как следует из названия, он сводит все коммиты в один. Для небольших функций это имеет смысл, поскольку сводит их в единый пакет. Для более крупных функций это, вероятно, не очень хорошая идея, особенно если ваши отдельные коммиты уже атомарны. Это действительно сводится к личным предпочтениям.
Github и Bitbucket (другие?) Запросы на извлечение - Если вам интересно, как слияние / перебазирование связано с запросами на извлечение, я рекомендую выполнить все вышеописанные шаги до тех пор, пока вы не будете готовы к слиянию с мастером. Вместо ручного слияния с git, вы просто принимаете пиар. Обратите внимание, что это не будет выполнять слияние с помощью сквоша (по крайней мере, по умолчанию), но без сквоша, без ускоренной перемотки является принятым соглашением о слиянии в сообществе запросов на извлечение (насколько я знаю). В частности, это работает так:
clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git push # May need to force push
...submit PR, wait for a review, make any changes requested for the PR
git rebase master
git push # Will probably need to force push (-f), due to previous rebases from master
...accept the PR, most likely also deleting the feature branch in the process
git checkout master
git branch -d my_new_feature
git remote prune origin
Я полюбил Git и больше не хочу возвращаться в SVN. Если вы боретесь, просто придерживайтесь этого, и в конце концов вы увидите свет в конце туннеля.
rebase
пониманиемОтветы:
«Конфликты» означают «параллельные эволюции одного и того же контента». Так что, если во время слияния все идет к черту, это означает, что у вас есть массивные изменения в одном и том же наборе файлов.
Причина, по которой ребазинг тогда лучше, чем слияние, заключается в том, что:
Я подтверждаю, что правильный рабочий процесс в этом случае (эволюции на общем наборе файлов) сначала перезагружается, затем объединяется .
Однако это означает, что, если вы нажмете на свою локальную ветвь (по причине резервного копирования), эта ветвь не должна быть извлечена (или, по крайней мере, использована) кем-либо еще (так как история коммитов будет перезаписана последовательным ребазом).
На эту тему (перебазирование, а затем слияние рабочего процесса) Баррапонто упоминает в комментариях два интересных поста, оба из randyfay.com :
(похожая техника существует для базара )
git push --force
(например, вместоgit pull --rebase
)источник
TL; DR
Рабочий процесс git rebase не защищает вас от людей, которые плохо разбираются в разрешении конфликтов, или от людей, привыкших к рабочему процессу SVN, как это предлагается в статье «Избежание Git Disasters: История Гори» . Это только делает разрешение конфликтов более утомительным для них и затрудняет восстановление после плохого разрешения конфликтов. Вместо этого используйте diff3, чтобы это не было так сложно в первую очередь.
Перебазировать рабочий процесс не лучше для разрешения конфликтов!
Я очень сторонник перебазирования за очистку истории. Однако, если я когда-нибудь столкнусь с конфликтом, я немедленно прерву ребаз и вместо этого сделаю слияние! Меня действительно убивает, что люди рекомендуют рабочий процесс rebase как лучшую альтернативу рабочему процессу слияния для разрешения конфликтов (именно об этом и был этот вопрос).
Если во время слияния он пойдет «все в ад», то во время перебазировки он пойдет «все в ад», и, возможно, намного больше в ад! Вот почему:
Причина № 1: Разрешать конфликты один раз, а не один раз для каждого коммита
Когда вы перебазируете вместо слияния, вам придется выполнять разрешение конфликтов столько раз, сколько вы выполняете перебазирование, для одного и того же конфликта!
Реальный сценарий
Я разветвляюсь от мастера, чтобы провести рефакторинг сложного метода в ветке. Моя работа по рефакторингу состоит из 15 коммитов, так как я работаю над ее рефакторингом и получаю обзоры кода. Часть моего рефакторинга включает исправление смешанных вкладок и пробелов, которые присутствовали в master раньше. Это необходимо, но, к сожалению, оно будет конфликтовать с любыми изменениями, внесенными впоследствии в этот метод в master. Конечно же, пока я работаю над этим методом, кто-то вносит простое, законное изменение в тот же метод в основной ветке, который должен быть объединен с моими изменениями.
Когда пришло время объединить мою ветку с master, у меня есть два варианта:
Git Merge: я получаю конфликт. Я вижу изменения, которые они сделали, чтобы освоить и объединить их с (конечным продуктом) моей ветки. Выполнено.
git rebase: у меня конфликт с первым коммитом. Я разрешаю конфликт и продолжаю перебазирование. У меня конфликт со вторым коммитом. Я разрешаю конфликт и продолжаю перебазирование. У меня конфликт с моим третьим коммитом. Я разрешаю конфликт и продолжаю перебазирование. У меня конфликт с моим четвертым коммитом. Я разрешаю конфликт и продолжаю перебазирование. У меня конфликт с моим пятым коммитом. Я разрешаю конфликт и продолжаю перебазирование. У меня конфликт с шестым коммитом. Я разрешаю конфликт и продолжаю перебазирование. У меня конфликт с седьмымсовершить. Я разрешаю конфликт и продолжаю перебазирование. У меня конфликт с моим восьмым коммитом. Я разрешаю конфликт и продолжаю перебазирование. У меня конфликт с моим девятым коммитом. Я разрешаю конфликт и продолжаю перебазирование. У меня конфликт с моим десятым коммитом. Я разрешаю конфликт и продолжаю перебазирование. У меня конфликт с моим одиннадцатым коммитом. Я разрешаю конфликт и продолжаю перебазирование. У меня конфликт с моим двенадцатым коммитом. Я разрешаю конфликт и продолжаю перебазирование. У меня конфликт с моим тринадцатым коммитом. Я разрешаю конфликт и продолжаю перебазирование. У меня конфликт с моим четырнадцатымсовершить. Я разрешаю конфликт и продолжаю перебазирование. У меня конфликт с пятнадцатым коммитом. Я разрешаю конфликт и продолжаю перебазирование.
Ты, должно быть, шутишь, если это твой любимый рабочий процесс. Все, что требуется, - это исправление пробелов, которое конфликтует с одним изменением, внесенным в master, и каждый коммит будет конфликтовать и должен быть разрешен. И это простой сценарий с конфликтом только пробелов. Не дай бог у вас есть реальный конфликт с участием основных изменений кода в файлах и должны решить , что несколько раз.
Со всем дополнительным разрешением конфликтов, которое вам нужно сделать, это просто увеличивает вероятность того, что вы допустите ошибку . Но ошибки в git хороши, так как вы можете отменить, верно? За исключением, конечно ...
Причина № 2: с rebase, нет отмены!
Я думаю, что мы все можем согласиться с тем, что разрешение конфликтов может быть трудным, а также что некоторые люди очень плохо в этом. Он может быть очень подвержен ошибкам, поэтому он так хорош, что git позволяет легко его отменить!
Когда вы объединяете ветку, git создает коммит слияния, который можно отменить или изменить, если разрешение конфликта идет плохо. Даже если вы уже отправили неудачный коммит слияния в публичное / авторитетное репо, вы можете использовать его,
git revert
чтобы отменить изменения, внесенные слиянием, и правильно повторить слияние в новом коммите слияния.Когда вы перебазируете ветку, в вероятном случае, если разрешение конфликта сделано неправильно, вы облажались. Каждый коммит теперь содержит неверное слияние, и вы не можете просто повторить ребаз *. В лучшем случае вы должны вернуться и внести поправки в каждый из затронутых коммитов. Не весело.
После перебазирования невозможно определить, что изначально было частью коммитов, а что было введено в результате плохого разрешения конфликта.
* Может быть возможно отменить ребазинг, если вы можете выкопать старые ссылки из внутренних журналов git, или если вы создадите третью ветку, которая указывает на последний коммит перед ребазингом.
Избавьтесь от разрешения конфликта: используйте diff3
Возьмите этот конфликт, например:
Глядя на конфликт, невозможно сказать, что изменилось в каждой ветви или каково ее намерение. По моему мнению, это самая главная причина, по которой разрешение конфликтов является запутанным и трудным делом.
на помощь diff3!
Когда вы используете diff3, каждый новый конфликт будет иметь 3-й раздел, объединенный общий предок.
Сначала изучите объединенного общего предка. Затем сравните каждую сторону, чтобы определить намерение каждой ветви. Вы можете видеть, что HEAD изменил EmailMessage на TextMessage. Его цель - изменить используемый класс на TextMessage, передавая те же параметры. Вы также можете видеть, что намерение ветви функции - передать false вместо true для опции: include_timestamp. Чтобы объединить эти изменения, объедините намерение обоих:
В основном:
Альтернатива: разрешить вручную, применяя изменения ветви
Наконец, некоторые конфликты ужасны для понимания даже с diff3. Это происходит, особенно когда diff находит общие линии, которые не являются семантически общими (например, обе ветви имели пустую строку в одном и том же месте!). Например, одна ветвь изменяет отступ тела класса или изменяет порядок похожих методов. В этих случаях лучшей стратегией разрешения может быть изучение изменения с любой стороны слияния и ручное применение diff к другому файлу.
Давайте посмотрим, как мы можем разрешить конфликт в сценарии, где происходит слияние,
origin/feature1
гдеlib/message.rb
конфликты.Решите, является ли наш измененный филиал (
HEAD
или--ours
) или филиалом, который мы объединяем (origin/feature1
или--theirs
), более простым изменением, чтобы применить его. Использование diff с тройной точкой (git diff a...b
) показывает изменения, произошедшиеb
с момента последнего расхожденияa
, или, другими словами, сравнивает общего предка a и b с b.Проверьте более сложную версию файла. Это удалит все маркеры конфликта и будет использовать ту сторону, которую вы выберете.
Сложное изменение проверено, найдите различие более простого изменения (см. Шаг 1). Примените каждое изменение из этого diff к конфликтующему файлу.
источник
В моем рабочем процессе я перезагружаюсь настолько, насколько это возможно (и я стараюсь делать это часто. Не позволяя накопленным несоответствиям резко сократить количество и серьезность столкновений между ветвями).
Однако даже в рабочем процессе, основанном на ребазе, есть место для слияний.
Напомним, что слияние фактически создает узел, у которого есть два родителя. Теперь рассмотрим следующую ситуацию: у меня есть две независимые ветви функций A и B, и теперь я хочу разработать материал для ветви функций C, который зависит как от A, так и от B, в то время как A и B рассматриваются.
Что я тогда делаю, так это следующее:
Теперь ветвь C включает в себя изменения как от A, так и от B, и я могу продолжать развивать ее. Если я сделаю какие-либо изменения в A, то я восстановлю график ветвей следующим образом:
Таким образом, я могу фактически поддерживать произвольные графы ветвей, но делать что-то более сложное, чем ситуация, описанная выше, уже слишком сложно, учитывая, что не существует автоматического инструмента для перебазирования при смене родителя.
источник
НЕ ИСПОЛЬЗУЙТЕ git push origin --mirror В СООТВЕТСТВИИ С ЛЮБОЙ ЛЮБОЙ ОБСТОЯТЕЛЬНОСТЬЮ.
Он не спрашивает, уверены ли вы, что хотите это сделать, и вам лучше быть уверенным, потому что он удалит все ваши удаленные ветви, которые не находятся в вашем локальном ящике.
http://twitter.com/dysinger/status/1273652486
источник
Instructions to this machine may lead to unintended consequences, loss of work/data, or even death (at the hands of the sysad). Remember that you are solely responsible for the consequences of your actions
в MOTD.У меня есть один вопрос после прочтения вашего объяснения: может быть, вы никогда не делали
перед выполнением 'git rebase / merge master' в вашей ветке функций?
Потому что ваша основная ветка не будет обновляться автоматически из репозитория вашего друга. Вы должны сделать это с
git pull origin
. Т.е., может быть, вы всегда сделали бы ребаз из неизменной локальной ветки master? И затем наступает время push, вы запускаете репозиторий, в котором есть (локальные) коммиты, которые вы никогда не видели, и, следовательно, push не выполняется.источник
В вашей ситуации я думаю, что ваш партнер прав. Что приятно в перебазировании, так это то, что для постороннего ваши изменения выглядят так, как будто все они произошли в чистой последовательности. Это означает
Вы все равно можете продолжать отправлять свою частную ветку разработки в удаленный репозиторий ради резервного копирования, но другие не должны воспринимать это как "публичную" ветку, так как вы будете перебазировать. Кстати, простая команда для этого
git push --mirror origin
.Статья « Упаковка программного обеспечения с использованием Git» довольно неплохо объясняет компромиссы между слиянием и перебазированием. Это немного другой контекст, но принципы одинаковы - в основном все зависит от того, являются ли ваши филиалы общедоступными или частными, и как вы планируете интегрировать их в основную ветку.
источник
origin
, вы должны зеркально отображать третий хранилище выделенных резервных копий.Ни в вашем партнёре, ни в предложенных вами рабочих процессах вы не должны сталкиваться с конфликтами, которые не имеют смысла. Даже если у вас есть, если вы следуете предлагаемым рабочим процессам, после разрешения «принудительный» толчок не требуется. Это говорит о том, что вы на самом деле не слили ветку, в которую вы толкали, но вам пришлось толкнуть ветку, которая не была потомком удаленного кончика.
Я думаю, вам нужно внимательно посмотреть на то, что произошло. Может ли кто-то другой (намеренно или нет) перемотать удаленную главную ветку между созданием локальной ветки и точкой, в которой вы попытались объединить ее с локальной веткой?
По сравнению со многими другими системами контроля версий, я обнаружил, что использование Git требует меньше борьбы с этим инструментом и позволяет вам приступить к решению проблем, которые являются фундаментальными для ваших исходных потоков. Git не выполняет магию, поэтому конфликтующие изменения вызывают конфликты, но он должен упростить процесс записи, отслеживая происхождение коммитов.
источник
«Даже если вы один разработчик с несколькими ветвями, стоит привыкнуть использовать ребаз и слияние должным образом. Основной шаблон работы будет выглядеть так:
Создать новую ветку B из существующей ветки A
Добавить / зафиксировать изменения в ветке B
Перебазировать обновления из ветки А
Объединить изменения из ветви B в ветку A "
https://www.atlassian.com/git/tutorials/merging-vs-rebasing/
источник
Из того, что я наблюдал, git merge стремится отделить ветви даже после слияния, тогда как rebase затем объединяет их в одну ветвь. Последнее получается намного чище, тогда как в первом было бы легче выяснить, какие коммиты принадлежат какой ветви, даже после слияния.
источник
В Git нет «правильного» рабочего процесса. Используйте все, что плавает на вашей лодке. Однако, если вы постоянно сталкиваетесь с конфликтами при объединении веток, возможно, вам следует лучше координировать свои усилия с коллегами-разработчиками? Похоже, вы двое продолжаете редактировать одни и те же файлы. Кроме того, следите за пробелами и подрывными ключевыми словами (например, «$ Id $» и другие).
источник
Я использую только рабочий процесс rebase, потому что он визуально более понятен (не только в GitKraken, но и в Intellij и в
gitk
, но я рекомендую самый первый): у вас есть ветвь, она исходит от мастера и возвращается к мастеру , Когда диаграмма будет чистой и красивой, вы будете знать, что ничто не идет в ад, никогда .Мой рабочий процесс почти такой же, как у вас, но с одним небольшим отличием: я
squash
фиксирую один в своем локальном филиале перед тем, какrebase
мой филиал включит последние измененияmaster
, потому что:Это означает, что если у вас есть 15 коммитов, меняющих ту же строку, что
master
и вы, вы должны 15 раз проверить, не сквошите ли вы, но какое значение имеет конечный результат, верно?Итак, весь рабочий процесс:
Проверьте
master
и потяните, чтобы убедиться, что у вас последняя версияОттуда создайте новую ветку
Делайте свою работу там, вы можете свободно фиксировать несколько раз и передавать удаленно, не беспокойтесь, потому что это ваша ветвь.
Если кто-то скажет вам: «Эй, мой PR / MR одобрен, теперь он объединен с мастером», вы можете получить их / вытащить их. Вы можете сделать это в любое время или на шаге 6.
Выполнив всю свою работу, зафиксируйте их, а если у вас есть несколько коммитов, раздавите их (это все ваша работа, и сколько раз вы меняете строку кода, значения не имеет; единственная важная вещь - это финальная версия). Нажми на это или нет, это не имеет значения.
Проверьте
master
,pull
снова, чтобы убедиться, что у вас есть последниеmaster
в местном. Ваша диаграмма должна быть похожа на это:Как видите, вы находитесь в своем местном филиале, который имеет устаревшее состояние
master
, в то время какmaster
(как локальный, так и удаленный) продвинулся вперед с изменениями вашего коллеги.master
и выбрать «Rebase»; еще одна причина, по которой мне это нравится). После этого вы будете подобно:Итак, теперь у вас есть все последние изменения в
master
сочетании с изменениями в вашей ветке. Теперь вы можете нажать на пульт, и, если вы нажали раньше, вам придется принудительно нажать; Git скажет вам, что вы не можете просто перемотать вперед. Это нормально, из-за перебазировки вы изменили начальную точку своей ветки. Но вы не должны бояться: используйте силу, с умом . В конце концов, пульт дистанционного управления также является вашей веткой, поэтому вы не оказываете влияния,master
даже если делаете что-то не так.Создайте PR / MR и подождите, пока он не будет одобрен, поэтому
master
внесу свой вклад. Congrats! Так что теперь вы можете оформить заказmaster
, вытащить свои изменения и удалить свою локальную ветку, чтобы очистить диаграмму. Удаленная ветвь тоже должна быть удалена, если этого не сделать, когда вы объедините ее с master.Окончательная диаграмма снова чиста и ясна:
источник