Чи існує "їхня" версія "git merge -s our"?


876

При об'єднанні тематичної гілки "B" в "A" за допомогою git mergeя отримую деякі конфлікти. Я знаю, що всі конфлікти можна вирішити за допомогою версії в "В".

Мені відомо git merge -s ours. Але я хочу щось подібне git merge -s theirs.

Чому його не існує? Як я можу досягти такого ж результату після суперечливого злиття з існуючими gitкомандами? ( git checkoutкожен неперероблений файл із B)

ОНОВЛЕННЯ: "Рішення" просто відкинути що-небудь із гілки A (точка злиття комісії в B-версію дерева) - це не те, що я шукаю.


1
Також дивіться цю відповідь: stackoverflow.com/questions/928646/… - банально змінити приклад, щоб використовувати версію B замість А.
Робі Басак

8
Дивіться команду git SO answer для створення однієї гілки як іншої для всіх поточних можливих способів моделюванняgit merge -s their .
VonC

4
Таким чином, ви не дуже шукаєте git merge -s theirs(чого можна легко досягти git merge -s oursі тимчасовою гілкою), оскільки -наші повністю ігнорують зміни злиття-з гілки ...
Nicolò Martini

21
@Torek - зробіть Git УБС дійсно знайти , що наступ , щоб забезпечити theirsна додаток до ours??? Це симптом однієї з інженерно-конструкторських проблем високого рівня в Git: непослідовність.
jww

3
@jww Проблема тут полягає не в тому, що "git merge -s ours" є образливим, а в тому, щоб бути контр-інтуїтивно зрозумілим. З питання ОП видно, що якщо така функція буде додана, він використає її помилково, коли те, що він насправді хоче зробити, - це "git merge -s recursive -X njihovo". Зазвичай потрібно об'єднати іншу гілку, яка переважає конфлікти з версією на іншій гілці, але повністю перезаписати поточну гілку з іншою гілкою, повністю відкинувши поточні зміни - це справді виняток.
ThomazMoura

Відповіді:


1019

Додати -Xопцію в theirs. Наприклад:

git checkout branchA
git merge -X theirs branchB

Все злиється бажаним чином.

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

Виправити це легко. Просто запустіть git rmз ім'ям усіх видалених файлів:

git rm {DELETED-FILE-NAME}

Після цього -X theirsслід працювати так, як очікувалося.

Звичайно, виконання фактичного видалення за допомогою git rmкоманди запобіжить виникненню конфлікту в першу чергу.


Примітка : Існує також довший варіант форми.

Щоб використовувати його, замініть:

-X theirs

з:

--strategy-option=theirs

6
Дивіться іншу відповідь нижче для кращого вирішення оригінального питання (Пол Пладійс).
Малькольм

204
Варто зазначити, що це не те саме, що "стратегія злиття theirs". -Xtheirs - це варіант стратегії, застосовуваний до рекурсивної стратегії . Це означає, що рекурсивна стратегія все одно зливатиме все, що може, і лише в разі конфліктів перейде до "їхньої" логіки. Хоча це те, що потрібно в більшості випадків, як вище, це не те саме, що "просто взяти все з гілки B, як є". Натомість це справжнє злиття.
Тимур

2
Він працює і з іншими командами. Я щойно зробив "git cherry-pick sha1 -X їх". Дякую!
Макс Хохенггер

48
Це жахлива відповідь, адже це виглядає правильно, але насправді неправильно. "git merge -X їхнє" не робить те, що зробить "git merge -s njihovo". Він не замінює поточну гілку змістом об'єднаної гілки. Він віддає перевагу "їхнім" змінам, але лише у випадку конфлікту. Отже, отриманий комітет може бути зовсім іншим від "їхньої" галузі. Це не те, про що мав на увазі плакат з питань.
Іван Крив’яков

7
@ user3338098: так, ти маєш рацію. Я перечитував питання, і це теж не так добре. На жаль, першопричиною плутанини є не відповідь і навіть не питання. Вибір дизайну git авторів полягає в тому, щоб дати однойменному «нашому» стратегію злиття та варіанту стратегії злиття «рекурсивну». Плутанина в цьому випадку практично неминуча.
Іван Кривяков

218

Можливе і перевірене рішення для об'єднання відділенняB у нашу перевірену галузьA:

# in case branchA is not our current branch
git checkout branchA

# make merge commit but without conflicts!!
# the contents of 'ours' will be discarded later
git merge -s ours branchB    

# make temporary branch to merged commit
git branch branchTEMP         

# get contents of working tree and index to the one of branchB
git reset --hard branchB

# reset to our merged commit but 
# keep contents of working tree and index
git reset --soft branchTEMP

# change the contents of the merged commit
# with the contents of branchB
git commit --amend

# get rid off our temporary branch
git branch -D branchTEMP

# verify that the merge commit contains only contents of branchB
git diff HEAD branchB

Щоб автоматизувати його, ви можете зафіксувати його в сценарій, використовуючи в якості аргументів branchA і branchB.

Це рішення зберігає перше і друге батьківське об'єднання злиття так, як ви і очікували git merge -s theirs branchB.


З: Для чого вам потрібна гілкаTEMP? Чи не могли ви просто git reset --soft branchA?
cdunn2001

4
@ cdunn2001: Якось я думав те саме, але ні. Зауважте, що git reset --hardзміни, на які branchAвказується, вказують
Tsuyoshi Ito

5
вибачте, що задаєте дурне запитання, але чому цей метод кращий -послідок? які переваги / недоліки
chrispepper1989

9
@ chrispepper1989 -x їх впливає лише на конфлікти , якщо наша частина може бути застосована чисто, вона проникне в результаті злиття. У цьому випадку, як показано останньою командою, ми отримуємо злиття, ідеально ідентичне гілці B, незалежно від того, були б конфлікти чи ні.
UncleZeiv

2
@UncleZeiv дякую за пояснення, так що в основному "їх" буде зберігати безконфліктні зміни та файли, в результаті чого код int не буде ідентичним витягнутому коду.
chrispepper1989

89

Старіші версії git дозволяли вам використовувати "їхню" стратегію злиття:

git pull --strategy=theirs remote_branch

Але це було знято, як пояснив у цьому повідомленні Хуніо Хамано (супроводжувач Git). Як зазначено у посиланні, замість цього ви зробите це:

git fetch origin
git reset --hard origin

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


7
Я взагалі не розумію пояснення Хуніо Хамано. Яким чином є git reset --hard originрішення для злиття їхнього стилю? Якби я хотів об'єднати BranchB у BranchA (як у відповіді Алана Сміта), як би це зробити з використанням resetметоду?
Джеймс Макмахон

3
@James McMahon: Точка Хуніо С Хамано - це не git reset --hardзлиття "їхнього" стилю. Звичайно, git reset --hardне створює жодних зобов'язань щодо злиття чи будь-яких зобов'язань з цього питання. Його сенс полягає в тому, що ми не повинні використовувати злиття, щоб замінити що-небудь у HEAD чимось іншим. Я не обов'язково погоджуюся.
Цуйоші Іто

11
Прикро, що theirsбуло усунуто, оскільки обґрунтування було неповним. Не вдалося дозволити для тих випадків, коли код хороший, його лише те, що висхідний потік підтримується на іншій філософській основі, тому в цьому сенсі є «погано», тому хочеться обидва бути в курсі з висхідним потоком, але в той же час зберігають хороший код "виправлення" [git & msysgit мають у цьому деякий "конфлікт" через різні філософії цільової платформи]
Philip Oakley,

1
У ньому також відсутній випадок використання, з яким я зараз застряг, де я ділюсь відділенням з кимось іншим, і нам обом трапилося вносити зміни (на різні файли) одночасно. Тож я хочу розім’яти всі старі версії, які в мене є локально, а замість цього використати новіші. Він хоче зробити те ж саме для файлів, які я оновив. Нічого не можна «скинути». І рішення для огляду кожного окремого файлу не є практичним, коли це ~ 20 файлів.
szeitlin

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

65

Не зовсім зрозуміло, який саме бажаний результат, тому існує певна плутанина щодо «правильного» способу його виконання у відповідях та коментарях. Я намагаюся дати огляд і побачити наступні три варіанти:

Спробуйте об'єднати та використовувати B для конфліктів

Це не "їхня версія для git merge -s ours", а "їхня версія для git merge -X ours" (що скорочено git merge -s recursive -X ours):

git checkout branchA
# also uses -s recursive implicitly
git merge -X theirs branchB

Це те, що, наприклад , відповідає відповідь Алана В. Сміта .

Використовуйте вміст лише з B

Це створює комісію для злиття для обох гілок, але відхиляє всі зміни branchAта зберігає лише вміст branchB.

# Get the content you want to keep.
# If you want to keep branchB at the current commit, you can add --detached,
# else it will be advanced to the merge commit in the next step.
git checkout branchB

# Do the merge an keep current (our) content from branchB we just checked out.
git merge -s ours branchA

# Set branchA to current commit and check it out.
git checkout -B branchA

Зауважимо, що злиття, яке здійснює перший батьків, зараз - це те, що є branchBлише другий branchA. Це, наприклад , відповідь Гандальф458 .

Використовуйте вміст лише з B та дотримуйтесь правильного батьківського порядку

Це справжня "їхня версія для git merge -s ours". Він має той самий зміст, що і у варіанті раніше (тобто лише в тому, що є з branchB), але порядок батьків правильний, тобто перший з батьків походить, branchAа другий з branchB.

git checkout branchA

# Do a merge commit. The content of this commit does not matter,
# so use a strategy that never fails.
# Note: This advances branchA.
git merge -s ours branchB

# Change working tree and index to desired content.
# --detach ensures branchB will not move when doing the reset in the next step.
git checkout --detach branchB

# Move HEAD to branchA without changing contents of working tree and index.
git reset --soft branchA

# 'attach' HEAD to branchA.
# This ensures branchA will move when doing 'commit --amend'.
git checkout branchA

# Change content of merge commit to current index (i.e. content of branchB).
git commit --amend -C HEAD

Це робить відповідь Пола Пладійса (не вимагаючи тимчасової гілки).


Більш чистий варіант варіанту 3:git checkout branchA; git merge -s ours --no-commit branchB; git read-tree -um @ branchB; git commit
1616

@jthill: Хоча ваша версія більш коротка, вона також використовує команду низького рівня ("сантехніка") read-tree, до якої середній користувач git може не використовуватися (принаймні, я не є ;-)). Рішення у відповіді використовують лише команди високого рівня ("фарфор"), які має знати більшість користувачів git. Я віддаю перевагу їм, але все-таки дякую за вашу версію!
siegi

Чи можете ви пояснити другий до останнього кроку (каса відділення A)? Здається, його там не повинно бути.
Патрік

@Patrick, цей крок необхідний. Якщо ви пропустите його, ви отримаєте ту саму комісію, але у вас не було б гілки, яка вказувала б на цю комісію. Отже, як тільки ви переключитесь на іншу команду / гілку, ви зробили цю команду з останньою командою ( …commit --amend…). Я планую додати кілька графічних пояснень до цієї відповіді, як тільки
встигну

62

З цього часу я використовував відповідь від Павла Пладійса. Я з'ясував, ви можете зробити "нормальне" злиття, виникають конфлікти, так що і ви

git checkout --theirs <file>

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

git merge <branch> -s theirs

У будь-якому випадку, зусиль більше, ніж було б із стратегією злиття! (Це було протестовано з git версія 1.8.0)


1
Було б чудово, якби список файлів можна було отримати. Наприклад, git ls-files --modified | xargs git addя хотів це зробити для доданого з обох сторін злиття: /
і

Насправді git повертає додані з обох сторін файли, git ls-files --modifiedтому я гадаю, що це також життєздатне рішення.
andho

1
Дякуємо, що також додали це. Частіше за все це не потрібно в базі файлів за файлом. Також у самому низу статусу git відображається "Unmerged Paths". Щоб отримати список невирішених шляхів, я використовую цей псевдонім:git config --global alias.unresolved '!git status --short|egrep "^([DAU])\1"'
Мельвін

в чому сенс -Xі в чому різниця між -Xі -s? не вдається знайти жоден документ.
Лей Ян

21

Я вирішив свою проблему за допомогою

git checkout -m old
git checkout -b new B
git merge -s ours old

гілка "Б" від "старої" гілки
ельмарко

13

Під час об'єднання гілки теми "B" в "A" за допомогою git merge я отримую деякі конфлікти. Я> знаю, всі конфлікти можна вирішити за допомогою версії в "В".

Мені відомо про злиття git - наше. Але я хочу щось подібне до git merge> -s їх.

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

Робіть саме те, що ви хочете зробити, за винятком того, як спочатку з’єднати одну гілку в іншу. Я просто це зробив, і це чудово спрацювало.

git checkout Branch
git merge master -s ours

Потім, майстер оформлення замовлення і об'єднайте в ньому свою філію (вона зараз пройде гладко):

git checkout master
git merge Branch

1
Дякую. Це має бути прийнята відповідь, безумовно, найпростіша і найправильніша. Якщо ваша гілка відстає / конфліктує з господарем, то її завдання філії об'єднатись і змусити все працювати, перш ніж об'єднати назад все до головного.
StackHola

Це спрацювало приголомшливо, дякую.
Kodie Grantham

12

Якщо ви перебуваєте у відділенні A, зробіть:

git merge -s recursive -X theirs B

Тестовано на git версії 1.7.8


8

Дійсно правильно зробити злиття, яке бере лише вхід з тієї гілки, яку ви зливаєте, ви можете зробити

git merge --strategy=ours ref-to-be-merged

git diff --binary ref-to-be-merged | git apply --reverse --index

git commit --amend

У будь-якому мені відомому сценарії не буде конфліктів, вам не доведеться робити додаткові гілки, і це діє як звичайна комісія злиття.

Однак це не грає з підмодулями.


Що робить тут -R?
P. Myer Nore

Таким чином ви втрачаєте історію файлів, "переміщених і змінених". Чи правий я?
elysch

1
-R - - зворотне, я оновив відповідь, щоб бути більш самодокументованим.
Ілля Лінн

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

7

Чому його не існує?

Хоча я в " git команді для створення однієї гілки як іншої " згадую, як імітувати git merge -s theirs, зауважте, що Git 2.15 (Q4 2017) тепер зрозуміліший:

Документація на -X<option>об'єднання " " для об'єднань була введена в оману, щоб припустити, що -s theirsіснує " ", що не так.

Див. Комісію c25d98b (25 вересня 2017 р.) Від Junio ​​C Hamano ( gitster) .
(Об’єднав Хуніо С Хамано - gitster- у комітеті 4da3e23 , 28 вересня 2017 р.)

стратегії злиття: уникайте того, що " -s theirs" існує

Опис -Xoursваріанту злиття має круглі дужки, які повідомляють читачам, що він дуже відрізняється від -s ours, що є правильним, але опис, -Xtheirsякий слідує за ним, недбало говорить, що "це протилежне ours", створюючи помилкове враження, що читачам також потрібно щоб попередити, що він сильно відрізняється від того -s theirs, що насправді навіть не існує.

-Xtheirs- це варіант стратегії, застосований до рекурсивної стратегії. Це означає, що рекурсивна стратегія все одно зливатиме все, що тільки може, і повернеться до theirsлогіки лише у випадку конфліктів.

Ця дискусія щодо доцільності theirsстратегії злиття чи ні, була повернута нещодавно в цій темі вересня 2017 року .
Він визнає більш старі теми (2008)

Коротше кажучи, попередню дискусію можна підсумувати до "ми не хочемо" -s theirs, оскільки це заохочує неправильний робочий процес ".

Він згадує псевдонім:

mtheirs = !sh -c 'git merge -s ours --no-commit $1 && git read-tree -m -u $1' -

Ярослав Гальченко знову намагається виступати за цю стратегію, але Хуніо К. Хамано додає :

Причина, чому наша і їхня не симетричні, полягає в тому, що ти - це ти, а не ти - - контроль і право власності на нашу історію та їх історію не симетричні.

Після того, як ви вирішите, що їх історія є основною лінією, ви краще хочете ставитися до своєї лінії розвитку як до бічної гілки та здійснити злиття в цьому напрямку, тобто перший батьківський результат результату злиття є зобов’язанням щодо їх історії, а другий батьків - останній поганий з вашої історії. Отже, ви в кінцевому підсумку використовуватимете "checkout their-history && merge -s ours your-history " ", щоб зберегти розумність першого батьківства.

І в цей момент використання " -s ours" вже не є способом вирішення нестачі " -s theirs".
Це належна частина бажаної семантики, тобто з погляду збереженої канонічної лінії історії, ви хочете зберегти те, що вона зробила, зводячи нанівець те, що зробив інший рядок історії .

Хуніо додає, як коментує Майк Бітон :

git merge -s ours <their-ref>фактично говорить, що "маркування комісій, зроблених <their-ref>на їхній гілці, як зобов'язання постійно ігноруватися";
і це має значення, тому що, якщо згодом ви з’єднаєтесь із пізнішими станами їхньої галузі, їх більш пізні зміни будуть внесені без ігнорованих змін .


1
Це корисна відповідь, але в ній не згадується жодного ключового моменту, який я щойно зрозумів з відповіді Хуніо К. Хамано: git merge -s ours <their-ref>фактично говорить, що «марка чинів, складена до <their-ref> у їхній гілці, як зобов’язується постійно ігноруватись». '; і це має значення, тому що, якщо згодом ви з’єднаєтесь із пізнішими штатами їх філії, їх більш пізні зміни будуть внесені без ігнорованих змін, що коли-небудь введені.
MikeBeaton,

1
@MikeBeaton Дякую Я включив ваш коментар у відповідь для більшої наочності.
VonC

5

Дивіться широко цитовану відповідь Хуніо Хамано : якщо ви збираєтесь відмовитись від скоєного вмісту, просто відмовтеся від комітетів або будь-яким чином не відхиляйте його від основної історії. Чому в майбутньому турбувати всіх, хто читає, виконувати повідомлення з комітетів, які нічого запропонувати?

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

(ред .: wow, чи вдалося мені помилитися раніше. Це працює.)

git update-ref HEAD $(
        git commit-tree -m 'completely superseding with branchB content' \
                        -p HEAD -p branchB    branchB:
)
git reset --hard

3

У цьому використовується команда читання дерева git сантехніка, але дозволяє скоротити загальний робочий процес.

git checkout <base-branch>

git merge --no-commit -s ours <their-branch>
git read-tree -u --reset <their-branch>
git commit

# Check your work!
git diff <their-branch>

0

Це об'єднає ваш newBranch у існуючий baseBranch

git checkout <baseBranch> // this will checkout baseBranch
git merge -s ours <newBranch> // this will simple merge newBranch in baseBranch
git rm -rf . // this will remove all non references files from baseBranch (deleted in newBranch)
git checkout newBranch -- . //this will replace all conflicted files in baseBranch

Я б запропонував додати git commit --amendв кінець вашого прикладу, або користувачі можуть побачити фіксацію злиття git logі вважати, що операція завершена.
Michael R

0

Я думаю, що ти насправді хочеш:

git checkout -B mergeBranch branchB
git merge -s ours branchA
git checkout branchA
git merge mergeBranch
git branch -D mergeBranch

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


0

Еквівалент (який зберігає батьківський порядок) "git merge -sss their branchB"

Перед об'єднанням:введіть тут опис зображення

!!! Переконайтесь, що ви в чистому стані !!!

Зробіть злиття:

git commit-tree -m "take theirs" -p HEAD -p branchB 'branchB^{tree}'
git reset --hard 36daf519952 # is the output of the prev command

Що ми зробили? Ми створили новий комітет, який є двома батьками, нашими та їхніми, а також мережею комітету є відділенняB - їхнє

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

Точніше:

git commit-tree -m "take theirs" -p HEAD -p 'SOURCE^{commit}' 'SOURCE^{tree}'

0

Таку відповідь дав Пол Пладійс. Я просто взяв його команди і зробив гіт-псевдонім для зручності.

Відредагуйте .gitconfig та додайте наступне:

[alias]
    mergetheirs = "!git merge -s ours \"$1\" && git branch temp_THEIRS && git reset --hard \"$1\" && git reset --soft temp_THEIRS && git commit --amend && git branch -D temp_THEIRS"

Тоді ви можете "git merge -s njihovo A", запустивши:

git checkout B (optional, just making sure we're on branch B)
git mergetheirs A

-1

Мені нещодавно потрібно було зробити це для двох окремих сховищ, які мають спільну історію. Я почав з:

  • Org/repository1 master
  • Org/repository2 master

Я хотів, щоб усі зміни repository2 masterбули застосовані repository1 master, приймаючи всі зміни, які буде внесено у сховище2. З точки зору git, це має бути стратегія під назвою, -s theirsАЛЕ її немає. Будьте обережні, тому що -X theirsназваний так, як це було б те, що ви хочете, але це НЕ те саме (це навіть написано на сторінці man).

Я вирішив це, щоб піти repository2і створити нове відділення repo1-merge. У цій гілці я побіг, git pull git@gitlab.com:Org/repository1 -s oursі це добре зливається без проблем. Потім натискаю на пульт.

Потім я повертаюся до repository1та роблю нову гілку repo2-merge. У цій гілці я працюю, git pull git@gitlab.com:Org/repository2 repo1-mergeяка завершиться питаннями.

Нарешті, вам потрібно буде або надіслати запит на об'єднання, repository1щоб зробити його новим господарем, або просто зберегти його як гілку.


-2

Простий та інтуїтивний (на мою думку) двоступеневий спосіб це зробити

git checkout branchB .
git commit -m "Picked up the content from branchB"

слідом за ним

git merge -s ours branchB

(що позначає дві гілки як об'єднані)

Єдиним недоліком є ​​те, що він не вилучає файли, які були видалені в branchB з вашої поточної гілки. Проста різниця між цими двома гілками згодом покаже, чи є такі файли.

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


це може бути правильним для початкового запитання, проте ОП оновила це питання у 2008 році таким чином, що ця відповідь була невірною.
користувач3338098

1
@ User3338098 Так, така жалість , що вони залишили в оману назва та просто вдарила не так помітні поновлення на кінці тіла питання .... тому що ця відповідь робить відповідь правильно в заголовку питання.
RomainValeri

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