Как происходит перекрестное слияние в Git?

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

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, указывающие на независимое объединение, как показано на диаграмме).

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

Ответ 1

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

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

Criss-cross слияния могут возникать по-разному. Например, возникает вопрос, когда у вас есть две ссылки на ветки, указывающие на одно и то же слияние (одна из этих ветвей проверяется), и вы запускаете 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 совершить -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

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

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

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

Ответ 2

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

Ответ 3

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

  #!/bin/sh
# Инициализировать
git init.
эхо-дата > file.txt; git добавить file.txt
git commit -m "Исходное сообщение" 

# Делать ветки
git-ветвь A; гит-ветвь B

# Сделать изменение в A
git checkout A
эхо-дата > fileA.txt; git добавить файлA.txt
git commit -m "сначала зафиксировать вдоль ветки A" 

# Внесите изменения в B; добавить тег для более поздней версии
git checkout B
эхо-дата > fileB.txt; git добавить файлB.txt
git commit -m "сначала фиксировать вдоль ветки B" 
git tag  "tag-B" 

# Слить A в B (все еще на B)
git merge --no-edit A; git tag "M" 

# Добавить еще одну фиксацию на B, родитель которой M
эхо-дата > fileB2.txt; git добавить fileB2.txt
git commit -m "вторая фиксация по B" 

# Переключитесь на A и добавьте фиксацию; Обратите внимание, что
# M не является предком этой фиксации
git checkout A
эхо-дата > fileA2.txt; git добавить файлA2.txt
git commit -m "second commit вдоль A" 

эхо "Лучшие общие предки (до креста):" 
# Должен быть только один
git merge-base - all A B

# Объединить фиксацию с тегами  "tag-B" 
# в A, генерирующий cris-cross-merge
git merge --no-edit tag-B

эхо "Лучшие общие предки (после креста):" 
# Должно быть два
git merge-base - all A B
Код>