Есть ли "их" версия "git merge -s ours"?

При объединении ветки темы "B" в "A" с помощью git mergeЯ получаю некоторые конфликты. Я знаю, что все конфликты могут быть решены с помощью версии в "B".

Я в курсе git merge -s ours, Но я хочу что-то вроде git merge -s theirs,

Почему его не существует? Как можно добиться того же результата после противоречивого слияния с существующим git команды? (git checkout каждый распакованный файл из Б)

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

22 ответа

Решение

Добавить -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

Возможное и проверенное решение для объединения ветки 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,

Более старые версии git позволяли вам использовать "их" стратегию слияния:

git pull --strategy=theirs remote_branch

Но с тех пор это было удалено, как объяснено в этом сообщении Джунио Хамано (сопровождающий Git). Как отмечено в ссылке, вместо этого вы должны сделать это:

git fetch origin
git reset --hard origin

Остерегайтесь, однако, что это отличается от фактического слияния. Ваше решение, вероятно, является вариантом, который вы действительно ищете.

Не совсем ясно, каков ваш желаемый результат, поэтому существует некоторая путаница в отношении "правильного" способа сделать это в ответах и ​​их комментариях. Я пытаюсь дать обзор и вижу следующие три варианта:

Попробуйте объединить и использовать 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, Это то, что, например, делает ответ Gandalf458.

Используйте контент только из B и сохраняйте правильный родительский порядок

Это настоящая "их версия для git merge -s oursMsgstr "Содержит то же содержимое, что и в предыдущем варианте (т.е. только 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

Это то, что делает ответ Пола Пладийса (не требуя временной ветки).

С тех пор я использовал ответ от Поля Пладийса. Я обнаружил, что вы можете сделать "нормальное" слияние, конфликты происходят, так что вы делаете

git checkout --theirs <file>

разрешить конфликт с помощью ревизии из другой ветки. Если вы сделаете это для каждого файла, у вас будет то же поведение, которое вы ожидаете от

git merge <branch> -s theirs

Во всяком случае, усилия больше, чем было бы со стратегией слияния! (Это было протестировано с git версии 1.8.0)

При слиянии ветки темы "B" в "A" с помощью git merge я получаю некоторые конфликты. Я> знаю, что все конфликты могут быть решены с помощью версии в "B".

Я знаю, что Git Merge - наш. Но я хочу что-то вроде git merge >-s их.

Я предполагаю, что вы создали ветку от мастера и теперь хотите объединиться с мастером, переопределяя все старые вещи в мастере. Это именно то, что я хотел сделать, когда наткнулся на этот пост.

Делайте именно то, что вы хотите сделать, за исключением того, что сначала объедините одну ветку с другой. Я только что сделал это, и это сработало отлично.

git checkout Branch
git merge master -s ours

Затем извлеките мастер и объедините в нем свою ветку (теперь все пройдет гладко):

git checkout master
git merge Branch

Я решил свою проблему, используя

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

Если вы находитесь на ветке А, сделайте:

git merge -s recursive -X theirs B

Протестировано на git версии 1.7.8

Почему его не существует?

Хотя я упоминаю в " git command для создания одной ветви как другую ", как имитировать git merge -s theirs обратите внимание, что Git 2.15 (4 квартал 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 --strategy=ours ref-to-be-merged

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

git commit --amend

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

Однако это не очень хорошо работает с подмодулями.

См . Широко цитируемый ответ Хунио Хамано: если вы собираетесь отказаться от зафиксированного контента, просто откажитесь от коммитов или, во всяком случае, не допускайте его в основную историю. Зачем беспокоить всех в будущем, читая сообщения коммитов от коммитов, которым нечего предложить?

Но иногда возникают административные требования или, возможно, какая-то другая причина. Для тех ситуаций, когда вам действительно нужно записывать коммиты, которые ничего не вносят, вы хотите:

(правка: вау, мне раньше удалось ошибиться. Это работает.)

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

Этот использует дерево чтения команды git plumbing, но сокращает общий рабочий процесс.

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>

Эквивалент (сохраняющий родительский порядок) "git merge -s itss branchB"

Перед слиянием:

!!! Убедитесь, что вы в чистоте!

Сделать слияние:

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

Что мы сделали? Мы создали новый коммит, у которого два родителя - наш и их, а коннект коммита - BranchB - их

После слияния:

Точнее:

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

Этот ответ дал Пол Пладийс. Я просто взял его команды и сделал псевдоним для удобства.

Отредактируйте ваш.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 -ssis A", выполнив:

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

Я думаю, что вы на самом деле хотите:

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

Это кажется неуклюжим, но это должно работать. Единственное, что мне действительно не нравится в этом решении, это то, что история git будет сбивать с толку... Но, по крайней мере, история будет полностью сохранена, и вам не нужно будет делать что-то особенное для удаленных файлов.

этот эксперимент помогает. Он создает конфликт, а затем разрешает его.

      git checkout -b b1
echo "on branch b1 making the first change" >> merge_test.txt
git add merge_test.txt
git commit merge_test.txt -m "b1 first change"

git checkout -b b1-2
echo "on branch b1-2 making the first change" >> merge_test.txt
git add merge_test.txt
git commit merge_test.txt -m "b1-2 first change"

git checkout b1
echo "on branch b1 making the second change" >> merge_test.txt
git add merge_test.txt
git commit merge_test.txt -m "b1 2nd change"

git checkout b1-2

на этом этапе, если вы запуститеgit merge b1вы видите, что есть конфликт. так что ты бежишьgit merge --abortчтобы вернуть это

тогда бегиgit merge -X theirs b1

если вы посмотрите, вы увидите, что содержимое файла теперь

      on branch b1 making the first change
on branch b1 making the second change

изменения по сравнению с b1 отражаются в ветке b1-2

Возвращаясь к этому старому вопросу, я только что нашел решение, которое является коротким и, поскольку в нем используются только фарфоровые команды, легко понять. Чтобы было ясно, я хочу ответить на проблему, указанную в заголовке вопроса (реализоватьgit merge -s theirs), а не тело вопроса. Другими словами, я хочу создать коммит слияния с деревом, идентичным дереву его второго родителя:

      # Start from the branch that is going to receive the merge.
git switch our_branch

# Create the merge commit, albeit with the wrong tree.
git merge -s ours their_branch

# Replace our working tree and our index with their tree.
git restore --source=their_branch --worktree --staged :/

# Put their tree in the merge commit.
git commit --amend

Предостережение :git restore— довольно новая команда, представленная в git 2.23.git help restoreпредупреждает, что

ЭТА КОМАНДА ЯВЛЯЕТСЯ ЭКСПЕРИМЕНТАЛЬНОЙ. ПОВЕДЕНИЕ МОЖЕТ ИЗМЕНИТЬСЯ.

Я протестировал этот метод с несколькими версиями git (2.25.1, 2.30.2, 2.31.1, 2.34.1 и 2.35.1), и он работал, как и ожидалось.

ОБНОВИТЬ

Хотя второй ответ отвечает на первоначальный вопрос ОП, он не отражает обновленную цель вопроса ОП.


То, что вы ищете, не является противоположностью git merge -s ours Команда, которую вы ищете противоположность git merge -s recursive -X ours команда, которая git merge -s recursive -X theirs


Оригинальный ответ

даны оригинальные ветки

*    0123456 (HEAD->A, origin/A)
| *  1234556 (origin/B, B)
|/
*    ??????? (common root)

объединить ветвь B в A, игнорируя состояние ветки A, а затем сначала выполнить регулярное слияние

git merge -s ours B    (this is the opposite of what we want)

это приводит к

*    2345678 (HEAD->A) Merge branch 'B' into A    (all wrong)
|\
* |  0123456 (origin/A)
| *  1234567 (origin/B, B)
|/
*    ??????? (common root)

не продвигайте это слияние, так как идентификатор фиксации изменится (и это в любом случае неверно), теперь мы можем изменить, после факта, ours стратегия в theirs стратегия

не забудьте записать идентификатор фиксации слияния, так как он будет недоступен после следующего полного сброса. (Если вы забудете, никакого ущерба не будет, но вам придется сделать полный сброс обратно к A и начать все сначала).

git reset --hard B    (this is what we want the merge to look like (the merge can now only be referenced by the commit id `2345678`))
git reset --soft 2345678    (without changing any files go back to the merge)
git commit --amend    (branch B is now the final version of the merge)

теперь у нас есть

*    3456789 (HEAD->A) Merge branch 'B' into A    (correctly ignores changes from 0123456)
|\
* |  0123456 (origin/A)
| *  1234567 (origin/B, B)
|/
*    ??????? (common root)

$ diff HEAD B
[... no changes ...]
git push

сделанный

Мне нужно было сделать это, когда коллега применил мои изменения A в его независимую ветвь B без слияния это позволяет мне принимать его модификации в моей ветке.

Это не обязательно отвечает на вопрос исходного плаката, но я оказался здесь в ситуации, когда я уже пытался выполнить слияние, но в итоге возникли конфликты. Обычно я управляю конфликтами в среде IDE, но когда у меня нет к ней доступа, можно использовать следующий REGEX для поиска и замены различий «своим» содержимым:

Заменитьс\1

(На всякий случай, если кто-то еще попадет на эту страницу по той же причине, что и я).

Это объединит ваш новый филиал с существующим базовым.

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

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

  • Org/repository1 master
  • Org/repository2 master

Я хотел все изменения от repository2 master быть примененным к repository1 master, принимая все изменения, которые сделал бы репозиторий2. С точки зрения Git, это должна быть стратегия под названием -s theirs НО его не существует. Будьте осторожны, потому что -X theirs назван так, как будто это то, что вы хотите, но это НЕ то же самое (об этом даже говорится на странице руководства).

Я решил, что это 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 сделать его новым хозяином или просто сохранить его как ветку.

Простой и интуитивно понятный (на мой взгляд) двухэтапный способ сделать это

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

с последующим

git merge -s ours branchB

(который отмечает две ветви как объединенные)

Единственным недостатком является то, что он не удаляет файлы, которые были удалены в BranchB из вашей текущей ветви. Простая разница между двумя ветками позже покажет, есть ли такие файлы.

Этот подход также дает понять из журнала ревизий впоследствии, что было сделано и что было задумано.

Другие вопросы по тегам