Чому так багато проектів віддають перевагу "git rebase" перед "git merge"?


67

Однією з переваг використання DVCS є робочий процес редагування-фіксація-об'єднання (над редагуванням-злиттям-здійсненням, який часто виконується CVCS). Дозволяючи кожній унікальній зміні фіксуватись у сховищі незалежно від об'єднань, гарантується, що DAG точно відображає справжній родовід проекту.

Чому так багато веб-сайтів говорять про те, щоб прагнути "уникати об'єднань"? Чи не злиття попереднього введення чи скасування після злиття ускладнює ізоляцію регресій, повернення минулих змін тощо?

Пояснення : поведінка за замовчуванням для DVCS полягає у створенні об'єднань. Чому так багато місць говорять про бажання побачити лінійну історію розвитку, яка приховує ці злиття?


18
Інструменти не завжди підходять правильно. І коли вони помиляються, ой хлопче, чи не помиляються вони?
Одід

2
@Oded Ви посилаєтесь на автоматичні об'єднання (наприклад, створені git pull)? Я бачу, чому це може бути проблематично, але моє запитання стосується всіх об'єднань.
Джейс Браунінг

можливий повторник Чи часто ускладнені конфлікти злиття є ознакою проблем? "Злиття і відновлення повинні викликати абсолютно однакові конфлікти, для тих притаманних конфліктів, які має вирішити людина (тобто два розробники, що змінюють один і той же рядок коду) ..."
gnat

Після публікації своєї відповіді у мене з'явилася думка, що ІМО не відповідає саме як відповідь: git pull --rebase == svn up(
нерівні

У централізованому магазині, як SourceSafe, зміни продовжуватимуться перевірятись у постійних зусиллях, але як тільки буде досягнуто стабільного плато, ми би позначили всі файли на певну дату. Тоді принаймні ви можете співвідносити помилки до або після такої та такої версії, або rebase -line.
Ендіз Сміт

Відповіді:


64

Люди хочуть уникати об'єднань, оскільки це робить журнал красивішим. Серйозно. Схоже на централізовані колоди, з якими вони виросли, і локально вони можуть робити всі свої розробки в одній гілці. Крім цих естетичних переваг немає жодних переваг, а крім тих, про які ви згадали, є і деякі недоліки, як, наприклад, схильність до конфлікту тягнути безпосередньо від колеги, не проходячи через «центральний» сервер.


5
Ти єдиний, хто правильно зрозумів моє запитання. Як я можу зробити це більш зрозумілим?
Джейс Браунінг

12
Можливо, перефразуйте слово "Які переваги лінійної історії розвитку?"
Карл Білефельдт

11
"Часто ви будете робити це, щоб переконатися, що ваші зобов'язання чітко застосовуються на віддаленій гілці - можливо, у проекті, до якого ви намагаєтесь внести свій внесок, але якого ви не підтримуєте. У цьому випадку ви зробите свою роботу у філії, а потім перезавантажте свою роботу на origin / master, коли ви готові були надіслати свої патчі до основного проекту. Таким чином, технічному обслуговувачу не потрібно робити жодної інтеграційної роботи - лише швидку передачу вперед або чисте застосування. " git-scm.com/book/en/Git-Branching-Rebasing
Andyz Smith

12
Вам здається, що це просто косметично, але насправді це практично: зрозуміти, що відбувається в лінійній історії, набагато простіше, ніж зі складною DAG із перетином та злиттям ліній.
Данило Гершкович

20
"Ніяких переваг, окрім цієї естетики", - я не знаю про це. Дійсно важко зрозуміти, що відбувається у вашій історії, коли ваша історія виглядає як друкована плата. (Якщо припустити декількох членів команди, кожен зі своїми відділеннями та робочими
потоками

46

У двох словах: git bisect

Лінійна історія дозволяє точно визначити фактичне джерело помилки.


Приклад. Це наш початковий код:

def bar(arg=None):
    pass

def foo():
    bar()

Відділення 1 робить таке рефакторинг таким, що argбільше не діє:

def bar():
    pass

def foo():
    bar()

Відділення 2 має нову функцію, яку потрібно використовувати arg:

def bar(arg=None):
    pass

def foo():
    bar(arg=1)

Конфліктів злиття не буде, однак помилка була введена. На щастя, цей конкретний потрапить на етап складання, але нам не завжди так пощастило. Якщо помилка виявляється як несподівана поведінка, а не помилка компіляції, ми можемо не знайти її протягом тижня чи двох. У цей момент git bisectна допомогу!

Ось лайно. Це те, що він бачить:

(master: green)
|             \_________________________
|                \                      \
(master: green)  (branch 1: green)     (branch 2: green)
|                 |                     |
|                 |                     |
(master/merge commit: green)            |
|                         ______________/
|                        /
(master/merge commit: red)
|
...days pass...
|
(master: red)

Отже, коли ми відправляємо git bisectзнайти комісію, яка порушила збірку, це точно визначить об'єднання. Ну, це мало допомагає, але це, по суті, вказує на пакет комітетів, а не на один. Усі предки зелені. З іншого боку, за допомогою перезавантаження ви отримуєте лінійну історію:

(master: green)
|
(master: green)
|
(all branch 1 commits: green)
|
(some branch 2 commits: green)
|
(branch 2 commit: red)
|
(remaining branch 2 commits: red)
|
...days pass...
|
(master: still red)

Тепер, git bisectбуде вказати на точну фіксацію функції, яка порушила збірку. В ідеалі повідомлення комісії пояснить, що було призначено досить добре, щоб зробити інший рефактор та виправити помилку відразу.

Ефект посилюється лише у великих проектах, коли технічне обслуговування не написало весь код, тому вони не обов'язково пам’ятають, для чого виконувались будь-які зобов’язання / для чого кожна гілка. Тож чітке визначення точної фіксації (а потім можливість вивчення обрядів навколо неї) є чудовою підмогою.


Однак, я (зараз) все ще віддаю перевагу злиття. Перехід на гілку випуску надасть вам лінійну історію для використання git bisect, зберігаючи справжню історію для щоденної роботи.


2
Дякую за це пояснення. Чи можете ви розширити, чому ви все ще віддаєте перевагу злиття? Я знаю, що в більшості ситуацій багато людей вважають за краще об'єднатись до звільнення, але я ніколи не зміг зрозуміти, чому.
Філіп

@Philip Я трохи не вагаюся з цим, оскільки я використовую лише кілька місяців у невеликих особистих проектах, ніколи в команді. Але при відстеженні того, що я нещодавно робив, gitk --allнадзвичайно корисно бачити потік комітетів через гілки (тим більше, що у мене в будь-який час є 3-х гілки, і щодня перемикатися між ними залежно від мого настрою в час).
Ізката

16
Але проблема злиття - це проблема, тому ви хочете зробити результат злиття bisect. Досить легко знайти індивідуальні зобов’язання з одним blameчи іншим, bisectякщо ви хочете копати глибше. З лінійною історією злиття робиться поза книжкою, тому ви більше не можете сказати, що про злиття було проблемою. Це просто схоже на ідіот, використовуючи аргумент, який не існує, особливо якщо аргумент був видалений декількома комісіями попереднього. Намагаючись зрозуміти, чому він це зробить, це набагато складніше, коли ви знищуєте історію.
Карл Білефельдт

1
Дійсно не згоден з цією відповіддю. Повторне знищення корисності bisect часом. За винятком заголовків, які виконуються за допомогою ребаза, коміти з ребаза можуть бути недоступними. Приклад: Ви розгалужувались на A і робили B, C, D, хтось штовхав E. Ви B і C і працюєте, але переобладнаєте на E, ви B і C не піддаються виконанню або керуєте ними і не працюєте. Найкращий випадок, якщо ви здаєтеся, в гіршому випадку, на вашу думку, B чи C містить проблему, коли насправді це D.
Lan

4
@Izkata Чому ви використовуєте бісект? Тому що трапляються помилки / помилки. Це смирена реальність. Можливо, "перетягування миші ліворуч" раніше не було частиною списку автоматизованих / ручних тестувань, і тепер ми помічаємо, що ця функція порушена, але ми знаємо, що вона працювала.
Лан

16

Коротше кажучи, тому що злиття часто є іншим місцем, щоб щось пішло не так, і потрібно лише один раз піти не так, щоб люди дуже боялися з цим боротися знов (один раз покусали двічі сором'язливо, якщо хочете).

Скажімо, ми працюємо над новим екраном управління обліковими записами, і виявляється, що в робочому процесі нового облікового запису виявлена ​​помилка. Гаразд, ми беремо два окремі шляхи - ви закінчите Управління акаунтами, і я виправляю помилку за допомогою Нових облікових записів. Оскільки ми обидва маємо справу з обліковими записами, ми працювали з дуже схожим кодом - можливо, нам навіть довелося коригувати однакові фрагменти коду.

На даний момент у нас є дві різні, але повністю працюючі версії програмного забезпечення. Ми обидва взяли на себе зобов’язання щодо своїх змін, ми обидва старанно перевірили свій код, і незалежно ми впевнені, що ми виконали приголомшливу роботу. А тепер що?

Що ж, пора зливатися, але ... лайно, що зараз станеться? Ми могли б дуже добре перейти від двох робочих наборів програмного забезпечення до одного, єдиного, жахливо зламаного фрагмента нещодавно помилкового програмного забезпечення, де управління вашим обліковим записом не працює і нові облікові записи зламані, і я навіть не знаю, чи стара помилка все ще є .

Можливо, програмне забезпечення було розумним, і воно сказало, що виник конфлікт, і наполягало на тому, щоб ми давали йому вказівки. Ну лайно - я сідаю, щоб це зробити і бачу, що ти додав якийсь складний код, який я не відразу розумію. Я думаю, що це суперечить змінам, які я вніс ... Я прошу вас, і коли ви отримаєте хвилину, ви перевірите, і ви побачите мій код, який ви не розумієте. Один або обидва з нас повинні зайняти час, щоб сісти, витримати належне злиття та, можливо, повторно протестувати всю річ, щоб переконатися, що ми її не зламали.

Тим часом 8 інших хлопців все чинять код, як садисти, вони є, я зробив декілька невеликих виправлень помилок і подав їх, перш ніж я знав, що ми зіткнулися в конфлікті, і людина, напевно, здається, гарний час зробити перерву, і, можливо, ти виїжджають на полудень або застрягають на зустрічі чи що завгодно. Можливо, я повинен просто взяти відпустку. Або змінити кар’єру.

І тому, щоб уникнути цього кошмару, деякі люди дуже побоюються прихильності (що ще нового, аміре?). Ми, природно, не маємо ризику в подібних сценаріях - якщо не думаємо, що ми смокчемо і все одно зіб'ємо це, і в цьому випадку люди починають діяти з необачною відмовою. зітхати

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

Але це все більше роботи, і ми просто хочемо натиснути кнопку на мікрохвильовці і пообідати 4-блюдову їжу, перш ніж ми встигнемо знайти виделку, і все це дуже невиконано - код - це робота, це продуктивно, її осмислене, але витончено поводження з об'єднанням просто не зараховується.

Програмістам, як правило, доводиться розвивати велику робочу пам’ять, а потім мають тенденцію негайно забувати всі ці непотрібні і змінні імена та простежувати, як тільки вони закінчили проблему, і вирішуючи конфлікт злиття (або ще гірше, a неправильно оброблене злиття) - це запрошення нагадати про вашу смертність.


5
Дивовижно сформульовано, пане!
Саут-Лапа заєць

10
Це дає відповідь, чому люди бояться злиття, а не чому злиття комітетів вважають поганими багатьма.
Джей

23
Проблема з цією відповіддю полягає в тому, що це все ще стосується ребазу . Зрештою, ребаза все ще робить злиття, де ви змінили рядок коду, який вже був змінений. Я думаю, що це питання з тим, як це питання було викладено.
deworde

1
Я відповів на цю відповідь, оскільки, як це все ще красномовно, це поза річчю, імхо.
Євген

5
Схоже, це взагалі не потрапляє на rebase vs merge-merge.
Andyz Smith

5

Rebasing надає рухому точку відгалуження, яка спрощує процес витіснення змін назад до базової лінії. Це дозволяє ставитися до довго працюючої гілки так, ніби до місцевих змін. Без звільнення, гілки накопичують зміни від базової лінії, які будуть включені в зміни, об'єднані назад до базових.

Об'єднання залишає базову лінію в початковій точці відділення. Якщо ви з’єднаєте зміни, що варті кількох тижнів, у лінії, від якої ви розгалужувались, тепер у вас є багато змін із точки вашого відділення, багато з яких будуть у вашій базовій лінії. Це ускладнює ідентифікацію змін у вашій філії. Натискання змін до базової лінії може призвести до конфліктів, не пов’язаних із вашими змінами. У разі конфліктів можна просунути непослідовні зміни. Постійні злиття вимагають зусиль для управління, і порівняно легко втратити зміни.

Rebase переміщує точку вашої філії до останньої редакції на базовій лінії. Будь-які конфлікти, з якими ви зіткнетеся, стосуватимуться лише змін. Натискання змін набагато простіше. Конфлікти вирішуються в місцевому відділенні, роблячи додаткову базу даних. У випадку конфліктних поштовхів останнім, хто підштовхне їх зміни, потрібно буде вирішити проблему зі своєю зміною.


1
тому все, що він робить, - це заниження нових змін до рівня, який вони вважаються "неправильною річчю", оскільки вони є єдиною зміною . Якщо всі старі зміни включені в об'єднання, всі зміни знаходяться на рівній основі, щодо того, чи є вони сьогодні "правильною річчю".
Andyz Smith

@AndyzSmith Я б не вважав нові зміни неправильними . Коли ви намагаєтеся визначити, що змінюється, і що слід підштовхнути, вони є правильною справою . Після перезавантаження ви можете ігнорувати старі зміни, оскільки вони є частиною базової лінії.
BillThor

правильно, тому якщо ви не впевнені, що є вашою базовою лінією, тобто, те, що ви змінили, вже готові до об'єднання, - це "правильна річ", тоді не перезавантажуйте.
Ендіз Сміт

І якщо ви отримаєте поштовх змін від когось, і це порушує збірку, оскільки прототип їх функції не відповідає існуючому прототипу, миттєво знаєте, через ребазацію, що новий хлопець помиляється. вам не доведеться здогадуватися, що, можливо, новий хлопець має правильний прототип, і попередні, необгрунтовані, не застосовані зміни змінних - це ті, що мають неправильний прототип.
Ендіз Сміт

4

Автоматизовані інструменти стають кращими для забезпечення компіляції та запуску злиття коду, таким чином уникаючи синтаксичних конфліктів , але вони не можуть гарантувати відсутність логічних конфліктів, які можуть бути введені об'єднаннями. Таким чином, «успішне» злиття дає відчуття помилкової впевненості, коли насправді це нічого не гарантує, і вам доведеться повторно пройти все тестування.

Справжня проблема з розгалуженням і злиттям, як я це бачу, полягає в тому, що він виштовхує прислів’я може по дорозі. Це дозволяє вам сказати "Я просто працюю у своєму маленькому світі" протягом тижня, і вирішувати будь-які проблеми, які з’являться пізніше. Але виправлення помилок завжди швидше / дешевше, коли вони свіжі. До того моменту, коли всі гілки коду почнуть зливатися, ви вже можете забути деякі нюанси зроблених дій.

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


4
Це дає відповідь, чому люди бояться злиття, а не чому злиття комітетів вважають поганими багатьма.
Джей

Отже, цей стовбур, на який ви посилаєтесь, включає в себе перезавантаження? Поясніть, будь ласка.
Andyz Smith

@AndyzSmith "trunk" - це термін svn для його еквівалента
гітлі

@Izkata, отже, ця відповідь виступає за те, щоб потім не використовувати ні злиття, ні перезавантаження?
Andyz Smith

@AndyzSmith Так, я теж прочитав це. І я не згоден з цим; після роботи в команді з 7 іншими розробниками за запропонованим способом, я не пропоную цього. Ми повинні були використовувати гілки функцій більше, ніж ми, але більшість з них боїться злиття (як BrianDHall потрапляє у свою відповідь).
Ізката

0

Додатковим важливим моментом є наступне: за допомогою перезавантаження я легко зможу вибрати чи повернути функцію у своєму відділенні випуску.


1
Це докладно про це?
Акавал

Звичайно :-) У git потоці ми регулярно створюємо нові гілки випуску. Якщо після створення помилка виправлена ​​у гілці розвитку, ми використовуємо її, git cherry-pick -x <commit>щоб застосувати її до гілки випуску. Або ми можемо захотіти скасувати щось для випуску, за допомогою git revert <commit>. Якщо <commit>є злиття, воно стає волохатим. Це є можливим, наскільки я знаю, але не так легко зробити. При використанні ребаза, кожне "злиття" - це один гігантський, але регулярний фіксатор, легко вибирається і повертається.
Бруно Шепер

0

Три речі не були сказані в жодній з відповідей:

  • Відмінність всередині гілки:

    • Різниця між довільною парою комітетів у гілці стає надзвичайно важкою, коли є об'єднання комітетів.
  • Об’єднання по одному предмету:

    • Коли ви вирішите різницю між двома гілками, злиття, як правило, відбувається одразу, і будь-які конфлікти злиття вам залишаються робити без контексту, який конкретний комітет відповідав за конфлікт. Якщо ви перезавантажуєтесь, то відновлення зупиняється в момент виникнення конфлікту і дозволить вирішити його в тому контексті.
  • Очищення перед натисканням:

    • Якщо ви допустили помилку, яку потрібно пізніше виправити, якщо інтерактивна база даних не натиснута, ви зможете комбінувати / розділити / змінити / перемістити комітети. Хоча ви все ще можете це зробити, якщо ви об'єдналися, це стає дуже важко, якщо ви хочете об'єднати / розділити / змінити / перейти через межу злиття.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.