Git merge із перезаписом сили


101

У мене є гілка під назвою, demoяку потрібно об’єднати з masterгілкою. Я можу отримати бажаний результат за допомогою наступних команд:

git pull origin demo
git checkout master
git pull origin master
git merge demo
git push origin master

Моє єдине занепокоєння полягає в тому, що якщо є якісь проблеми злиття , я хочу сказати, gitщоб перезаписати зміни у masterгілці, не даючи мені запиту на злиття. Отже, в основному зміни у demoгілці повинні автоматично перезаписувати зміни у masterгілці.

Я озирнувся, є кілька варіантів, але я не хочу ризикувати зі злиттям.


git push -f origin master
MD XF,

4
@MDXF: Можливо, я помиляюся, але чи не слід використовувати -fопцію з mergeкомандою, а не з pushкомандою
OpenStack

Ви можете спробувати обидва і подивитися, що вам
MD XF,

Нижче посилання для вирішення сили перезапис: stackoverflow.com/a/42454737/984471
Манохар Редді Poreddy

Відповіді:


102

Насправді це не пов'язано з цією відповіддю, але я б відмовився git pull, який просто біжить git fetchза ним git merge. Ви виконуєте три злиття, які змусять ваш Git запустити три операції отримання, коли вам потрібно лише одне завантаження. Звідси:

git fetch origin   # update all our origin/* remote-tracking branches

git checkout demo         # if needed -- your example assumes you're on it
git merge origin/demo     # if needed -- see below

git checkout master
git merge origin/master

git merge -X theirs demo   # but see below

git push origin master     # again, see below

Керування найскладнішим злиттям

Найцікавіша частина тут git merge -X theirs. Як зазначив root545 ,-X параметри передаються стратегії злиття, причому як recursiveстратегія за замовчуванням, так і альтернативна resolveстратегія приймають -X oursабо -X theirs(одну чи іншу, але не обидві). Однак, щоб зрозуміти, що вони роблять, вам потрібно знати, як Git знаходить та обробляє конфлікти та обробляє їх .

Конфлікт злиття може статися в якомусь файлі 1, коли базова версія відрізняється від поточної (також називається локальною, HEAD, або --ours) версії, так і від іншої (також званої віддаленою або --theirs) версії того самого файлу. Тобто злиття визначило три ревізії (три коміти): базову, нашу та їхню. Версія "base" - це основа злиття між нашим комітом та їхнім комітом, як це знайдено на графіку коміту (докладніше про це див. Інші публікації StackOverflow). Потім Git знайшов два набори змін: "що ми зробили" і "що вони зробили". Ці зміни (загалом) знаходяться у рядку за рядком, суто текстовіосновою. Git не має реального розуміння вмісту файлу; це просто порівняння кожного рядка тексту.

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

Якщо зміни відбуваються на різних лініях, наприклад, ми змінюємо colorдо colourрядку 17 і вони змінюються fredв barneyна лінії 71-те немає ніякого конфлікту: Git просто приймає обидва зміни. Якщо зміни відбуваються в одних і тих же рядках, але є однаковими , Git бере одну копію зміни. Тільки якщо зміни знаходяться в одних рядках, але це різні зміни, або той особливий випадок, що заважає контексту, ви отримуєте конфлікт модифікації / модифікації.

Параметри -X oursand і -X theirsпідказують Git, як вирішити цей конфлікт, вибравши лише одну з двох змін: нашу чи їхню. Оскільки ви сказали, що об’єднуєте demo(їхнє) у master(наше) і хочете зміни від demo, ви хотіли б -X theirs.

-XОднак сліпе застосування небезпечно. Те, що наші зміни не суперечили окремо, не означає, що наші зміни насправді не суперечать! Один класичний приклад зустрічається в мовах із оголошеннями змінних. Базова версія може оголосити невикористовувану змінну:

int i;

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

Якщо у вас є автоматизований набір тестів, найголовніше - це запустити тести після об’єднання. Ви можете зробити це після фіксації та за потреби виправити ситуацію пізніше; або ви можете зробити це перед фіксацією, додавши --no-commitдо git mergeкоманди. Подробиці про все це ми залишимо для інших публікацій.


1 Ви також можете отримати конфлікти щодо "загальнофайлових" операцій, наприклад, можливо, ми виправляємо написання слова у файлі (щоб ми мали зміни), і вони видаляють весь файл (щоб вони мали видалити). Git не вирішить ці конфлікти самостійно, незалежно від -Xаргументів.


Роблячи менше злиттів та / або розумніші злиття та / або використовуючи перебазування

Існує три злиття в обох наших послідовностях команд. Перший - приєднати origin/demoдо локального demo(ваше використання, git pullяке, якщо ваш Git дуже старий, не зможе оновитись, origin/demoале дасть той самий кінцевий результат). Під - друге , щоб принести origin/masterв master.

Мені незрозуміло, хто оновлює demoта / або master. Якщо ви пишете свій власний код у своїй demoгілці, а інші пишуть код і штовхають його до demoгілки origin, тоді це перше крокове об’єднання може мати конфлікти або створити справжнє об’єднання. Найчастіше для поєднання роботи краще використовувати перебазування, а не об’єднання (правда, це питання смаку та думки). Якщо так, ви можете git rebaseзамість цього скористатися . З іншого боку, якщо ви ніколи не робите жодної власної комісії demo, ви навіть не робите : це перемотує вашу сторінку вперед, щоб вона відповідала оновленим потрібно в demoгалузі. Крім того, якщо ви хочете багато чого автоматизувати, але зможете ретельно перевірити, чи є коміти, зроблені вами та іншими, ви можете використовуватиgit merge --ff-only origin/demodemoorigin/demo якщо це можливо, і просто відмовити, якщо ні (тоді ви зможете перевірити два набори змін і вибрати реальне злиття або перебазування відповідно до ситуації).

Ця ж логіка застосовна і до master, хоча ви робите злиття на master , так що ви напевне потрібні master. Однак, навіть більше ймовірно, що ви хотіли б, щоб злиття не вдалося, якщо це неможливо зробити як швидке незлиття, тому це, мабуть, теж повинно бути git merge --ff-only origin/master.

Скажімо, ти ніколи не робиш власних зобов’язань demo. У цьому випадку ми можемо demoповністю відмовитись від назви :

git fetch origin   # update origin/*

git checkout master
git merge --ff-only origin/master || die "cannot fast-forward our master"

git merge -X theirs origin/demo || die "complex merge conflict"

git push origin master

Якщо будуть робити свої власні demoгілки фіксацій, це не корисно; Ви також можете зберегти існуюче злиття (але, можливо, додати, --ff-onlyзалежно від того, яку поведінку ви хочете), або переключити його на перебазування. Зверніть увагу, що всі три методи можуть зазнати збою: злиття може призвести до збою в результаті конфлікту, злиття з --ff-onlyне зможе перемотати вперед, а перебазування може призвести до збою в результаті конфлікту (перебазування працює, по суті, коммітування вибору вишні , яке використовує злиття машини, а отже, може виникнути конфлікт злиття).


21

У мене була подібна проблема, коли мені потрібно було ефективно замінити будь-який файл, який мав зміни / конфлікти, іншою гілкою.

Рішенням, яке я знайшов, було використання git merge -s ours branch.

Зверніть увагу, що варіант є -sі ні -X. -sпозначає використання oursв якості стратегії злиття верхнього рівня, -Xзастосовуватиме oursопцію до recursiveстратегії злиття, що не те, що я (або ми) хочемо в цьому випадку.

Кроки, де oldbranchзнаходиться гілка, яку ви хочете перезаписати newbranch.

  • git checkout newbranch перевіряє гілку, яку ви хочете зберегти
  • git merge -s ours oldbranch зливається у старій гілці, але зберігає всі наші файли.
  • git checkout oldbranch перевіряє гілку, яку потрібно замінити
  • get merge newbranch зливається в новій гілці, перезаписуючи стару гілку

Примітка: жодне з інших вирішень тут не спрацювало для мене, тому я публікую свою відповідь.
Найл Маккормак

1
У мене це не спрацювало. Він вирішив конфлікт (вирішив конфліктні файли), але файл не об’єднав. І не може злити ні те, ні інше.
c-an

Якщо хтось застряє там, де вам буде запропоновано "Введіть повідомлення про коміт, щоб пояснити, чому це об’єднання потрібно": Введіть своє повідомлення, а потім натисніть клавішу ESC на клавіатурі, введіть: wq і натисніть ENTER, щоб вийти з запиту.
topherPedersen

19

Цей підхід злиття додасть один коміт, поверх masterякого вставляється все, що є feature, без нарікань на конфлікти чи інше лайно.

введіть тут опис зображення

Перш ніж щось торкнутися

git stash
git status # if anything shows up here, move it to your desktop

Тепер підготуй майстра

git checkout master
git pull # if there is a problem in this step, it is outside the scope of this answer

Отримати featureвсі одягнені

git checkout feature
git merge --strategy=ours master

Йди на вбивство

git checkout master
git merge --no-ff feature

1
git push до кінця
Кохчі

git-scm.com/docs/git-merge#Documentation/git-merge.txt-ours. Це спрацьовувало, коли коміти не зливались чисто. Але такий підхід не завжди спрацює, цитуючи джерело Changes from the other tree that do not conflict with our side are reflected in the merge result. Це не спрацювало для мене, оскільки в моїй гілці були чисто об’єднані коміти, які перезаписувались. Чи є інший спосіб?
NiharGht

11

Ви можете спробувати "наш" варіант у git merge,

гілка злиття git -X наша

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


3
Це було б назад, оскільки ОП заявив, що хоче, щоб demoверсія була кращою, хоча він зливається master. Є -X theirsтакож, але незрозуміло, що цього насправді хоче ОП.
Торек

Ви не прочитали цілком. У вашій примітці описується, що oursвідбувається при використанні у серверній системі з -sопцією. При використанні oursз -X: Він відкидає все, що робило інше дерево, декларуючи, що наша історія містить усе, що в ньому відбувалося. джерело
jimasun

2

Коли я намагався використовувати -X theirsта інші пов'язані командні перемикачі, я продовжував отримувати коміт злиття. Я, мабуть, не розумів цього правильно. Одна з простих для розуміння альтернатива - це просто видалити гілку, а потім відстежити її знову.

git branch -D <branch-name>
git branch --track <branch-name> origin/<branch-name>

Це не зовсім "злиття", але це те, що я шукав, коли натрапив на це питання. У моєму випадку я хотів отримати зміни з віддаленої гілки, які були примусово натиснуті.

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