Как возникают перекрестные слияния в Git?

Я просматривал справочную страницу "git merge-base" и не могу понять, как развиваются множественные базы слияния. В частности, я зациклен на следующей иллюстрации на странице руководства:

When the history involves criss-cross merges, there can be more than one best common
ancestor for two commits. For example, with this topology:
  ---1---o---A
      \ /
       X
      / \
  ---2---o---o---B
both 1 and 2 are merge-bases of A and B. Neither one is better than the other (both
are best merge bases). When the --all option is not given, it is unspecified which
best one is output.

Я просто не могу понять, как могла быть создана такая ситуация. Я попытался воссоздать эту ситуацию перекрестного слияния между ветвями, используя тестовый репозиторий, но я не могу ее воспроизвести. Во всех случаях я всегда получаю 1 коммит слияния, на который указывают как A, так и B (вместо A и B, указывающих на независимые коммиты слияния, как показано на диаграмме).

Кто-нибудь может проиллюстрировать, как может возникнуть такая ситуация? Это обычная ситуация или ситуация с ошибкой?


person MACS    schedule 14.10.2014    source источник
comment
Связанная концепция: Foxtrot Merge. Также см. Этот вопрос SO   -  person Christian Long    schedule 13.10.2016
comment
Также по теме: Как мне успешно создать перекрестное слияние в Git?.   -  person Wai Ha Lee    schedule 14.03.2019


Ответы (4)


Я просто не могу понять, как могла быть создана такая ситуация. Я попытался воссоздать эту ситуацию перекрестного слияния между ветвями, используя тестовый репозиторий, но я не могу ее воспроизвести. Во всех случаях я всегда получаю 1 коммит слияния, на который указывают как A, так и B (вместо A и B, указывающих на независимые коммиты слияния, как показано на диаграмме).

Кто-нибудь может проиллюстрировать, как может возникнуть такая ситуация?

Перекрестные слияния могут возникать по-разному. На ум приходит пример, когда у вас есть две ссылки ветки, указывающие на одну и ту же фиксацию слияния (одна из этих веток извлекается), и вы запускаете git commit --amend. Следующий пример игрушки делает именно это:

# set things up
cd ~/Desktop
mkdir crisscross
cd crisscross
git init

# make an initial commit
printf "bar\n" > README.md
git add README.md
git commit -m "add README"

# add a line at the top of the file and commit
sed -i '' '1s/^/foo\n/' README.md
git commit -am "add foo line"

# create and checkout a branch pointing at the initial commit
git checkout -b other master^

# add a line at the bottom of the file and commit
printf "baz\n" >> README.md
git commit -am "add baz line"

# do a merge and make both branches point to this merge commit
git merge master
git checkout master
git merge other

На этом этапе результат git log --graph --oneline --decorate --all равен

*   30b9175 (HEAD, master, other) Merge branch 'master' into other
|\  
| * f318ead add foo line
* | 31875d9 add baz line
|/  
* 8b313a0 add README

Теперь внесите поправки в последнюю фиксацию (см. Как именно работает git commit --amend ? подробнее):

git commit --amend

После этого результат git log --graph --oneline --decorate --all будет

*   69bbcbe (HEAD, master) Amended merge commit
|\  
| | *   30b9175 (other) Merge branch 'master' into other
| | |\  
| |/ /  
|/| /   
| |/    
| * f318ead add foo line
* | 31875d9 add baz line
|/  
* 8b313a0 add README

Вот и все: история теперь содержит перекрестные слияния.

Это обычная ситуация или ситуация с ошибкой?

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

person jub0bs    schedule 14.10.2014
comment
Спасибо за пример, это именно то, что я искал! Теперь вопрос для меня проясняется. - person MACS; 15.10.2014
comment
@MACS Да, ты прав; --all отсутствует. Отредактирую свой ответ. - person jub0bs; 15.10.2014

Вот последовательность коммитов, которые приведут к перекрестному слиянию. Больше шагов, чем тот, который включает --amend, но использует только основы. Запустите этот сценарий, чтобы убедиться, что вы действительно получаете перекрестное слияние.

#!/bin/sh
# Initialize 
git init .                                                                                                                       
echo date > file.txt; git add file.txt
git commit -m "Initial commit"

# Make branches                                                                                                                     
git branch A; git branch B

# Make change in A                                                                                                                  
git checkout A
echo date > fileA.txt; git add fileA.txt
git commit -m "first commit along branch A"

# Make change in B; add tag for later                                                                                                               
git checkout B
echo date > fileB.txt; git add fileB.txt
git commit -m "first commit along branch B"
git tag "tag-B"

# Merge A into B (still on B)                                                                                                       
git merge --no-edit A; git tag "M"

# Add another commit on B whose parent is M                                                                                         
echo date > fileB2.txt; git add fileB2.txt
git commit -m "second commit along B"

# Switch to A and add a commit; note that
# M is not an ancestor of this commit                                                                          
git checkout A
echo date > fileA2.txt; git add fileA2.txt
git commit -m "second commit along A"

echo "Best common ancestors (before criss-cross):"
# Should be only one
git merge-base --all A B

# Merge the commit tagged "tag-B" 
# into A generating the cris-cross-merge                                                            
git merge --no-edit tag-B

echo "Best common ancestors (after criss-cross):"
# Should be two
git merge-base --all A B
person rlandster    schedule 30.04.2017
comment
Спасибо за подробный ответ. Я попробую это со своей стороны и посмотрю историю изменений, чтобы увидеть, как это работает. :) - person MACS; 01.05.2017

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

person Fred Foo    schedule 14.10.2014
comment
Спасибо за ответ. Думаю, мне придется настроить репозиторий для проверки вашего сценария, чтобы я мог увидеть, как он разворачивается. - person MACS; 15.10.2014
comment
возможно, мы получаем данные с одного сервера, а не друг с другом, если нет проблем - person briefy; 06.11.2018

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

---1---o---A
    \ /
     X
    / \
---2---o---o---B

Вышеупомянутое взято с веб-сайта git, я думаю, он забывает о добавлении стрелки .

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

Более того, легко понять, что ветвь A и ветвь B имеют общих предков 1 и 2.

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

person briefy    schedule 06.11.2018