Я удивлен, что изменения, сделанные после выбора вишни в git, устаревают при слиянии. Вот полный пример.
Следующее - обычное дело.
- Создать репо
- Добавляем файл с тестом "ротумс канонер оч круть"
- Зайдите в новую ветку и добавьте строку "mutors kanoner och krut"
- Проверьте мастер и выберите коммит с помощью "mutors kanoner och krut"
Mac:git user1$ mkdir myrepo; cd myrepo; git init
Initialized empty Git repository in /Users/user1/tmp/git/myrepo/.git/
Mac:myrepo user1$ echo "rotums kanoner och krut" > rotum.txt
Mac:myrepo user1$ git add rotum.txt
Mac:myrepo user1$ git commit -m "Added file"
[master (root-commit) 1044abb] Added file
1 file changed, 1 insertion(+)
create mode 100644 rotum.txt
Mac:myrepo user1$ git checkout -b mybranch
Switched to a new branch 'mybranch'
Mac:myrepo user1$ echo "mutors kanoner och krut" >> rotum.txt
Mac:myrepo user1$ git commit -am "Added mutor"
[mybranch 19afeba] Added mutor
1 file changed, 1 insertion(+)
Mac:myrepo user1$ git checkout master
Switched to branch 'master'
Mac:myrepo user1$ git cherry-pick 19af
[master cce2ca5] Added mutor
Date: Wed May 19 16:12:04 2021 +0200
1 file changed, 1 insertion(+)
Mac:myrepo user1$ cat rotum.txt
rotums kanoner och krut
mutors kanoner och krut
Сейчас происходит неожиданное поведение.
- Я удаляю строку, которая была добавлена и отобрана (я делаю это путем переопределения файла, нетрадиционный метод, но полезный в данном случае).
- Потом сливаю свою ветку в master. Я бы ожидал, что изменения, сделанные в f63dc50, удаление строки, останутся, но таинственным образом исчезнет. Вернулась линия "муторс канонер оч круть".
Mac:myrepo user1$ echo "rotums kanoner och krut" > rotum.txt
Mac:myrepo user1$ cat rotum.txt
rotums kanoner och krut
Mac:myrepo user1$ git commit -am "Removed mutor"
[master f63dc50] Removed mutor
1 file changed, 1 deletion(-)
Mac:myrepo user1$ git merge mybranch
Merge made by the 'recursive' strategy.
rotum.txt | 1 +
1 file changed, 1 insertion(+)
Mac:myrepo user1$ cat rotum.txt
rotums kanoner och krut
mutors kanoner och krut
Это ожидаемое поведение или ошибка?
2 ответа
Со схемой:
Initial commit: create file
| Add a line in file (cherry-pick b)
| | Remove line, return file to its state in a.
v v v
a----c----d <- master
\
b <- mybranch
^
Add a line in file
Когда вы объединяете mybranch
в master
:
- git ищет общего предка Closes (он же база слияния, commit
a.
на диаграмме), - он смотрит на
master
(commitd
) и видит, что по сравнению сa.
файл остается без изменений - он смотрит на
mybranch
(commitb
) и видит, что по сравнению сa.
следует добавить строку
Поэтому слияние проходит без конфликтов и "вносит" изменения из mybranch
.
Вы попадаете в эту ситуацию, потому что:
git merge
не проверяет промежуточные коммиты в историиmaster
иmybranch
,git cherry-pick
создает новые, несвязанные коммиты, и в графе фиксации ничего не сохраняется, чтобы указать, что "эти изменения уже были включены",- Так получилось, что изменения можно комбинировать без конфликтов (ваш пример действительно прост, но в реальных ситуациях вполне может произойти «отсутствие конфликтов»).
Чтобы дать дальнейшую перспективу:
- если вы используете
git rebase
:
git checkout mybranch
git rebase master
В отличие от git merge
, git rebase
действительно сравнивает список коммитов, и если перебазированная фиксация вводит те же самые изменения, что и другая фиксация в целевой ветви, эта фиксация отбрасывается.
В вашем примере: b
не будет применяться повторно, потому что он вводит те же изменения, что и c
, который уже есть на master
.
- если вы объединили фиксацию
b
вместо выбора вишни:
merge 'mybranch'
v
a---c----d <- master
\ /
b <- mybranch
Тогда git merge mybranch
сказал бы already merged
и не применил бы повторно фиксацию b
Ключевым моментом здесь является то, что git не записывает тот факт, что фиксация была выбрана из одной ветки в другую; он просто создает новую фиксацию на основе указанной вами (то же самое относится и к «git rebase»).
Что касается git, у вас есть следующие коммиты:
- 1044abb, который создает файл
- 19afeba, который добавляет строку
- cce2ca5, который добавляет строку
- f63dc50, который удаляет линию
Обратите внимание, что я не описал эти коммиты как «на» одной или другой ветке, потому что, строго говоря, это не имеет значения в git; ветвь указывает на фиксацию, а другие коммиты доступны через «родительские» указатели.
В момент объединения двух веток:
- 1044abb, cce2ca5 и f63dc50 доступны с "мастера"
- 1044abb и 19afeba доступны из mybranch
- файл в "master" имеет только одну строку
- файл в "mybranch" состоит из двух строк
Когда вы выполняете слияние, git определяет «базу слияния» на основе самого последнего коммита, доступного из обеих веток; в данном случае это 1044abb. Затем он смотрит на различия между этой фиксацией и двумя объединяемыми ветвями:
- между 1044abb и f63dc50 ("master") файл не изменяется
- между 1044abb и 19afeba ("моя ветка") в файле есть добавленная строка
Затем он объединяет эти два изменения и применяет их для создания новой версии файла. Поскольку одна сторона слияния хочет добавить строку, а другая не хочет ничего делать, решение состоит в том, чтобы добавить строку.
Результат:
- cce2ca5, f63dc50 и 19afeba доступны от "мастера"
- 19afeba доступна с "моей ветки"
- у вас есть мастер, у которого снова есть линия
Или, говоря короче: вы добавили строку, удалили ее, а затем снова добавили.
Похожие вопросы
Связанные вопросы
Новые вопросы
git
Git - это система управления распределенными версиями с открытым исходным кодом (DVCS). Используйте этот тег для вопросов, связанных с использованием Git и рабочими процессами. НЕ ИСПОЛЬЗУЙТЕ тег [github] для решения проблем, связанных с Git, просто потому, что репозиторий размещен на GitHub. Также не используйте этот тег для общих вопросов по программированию, которые связаны с репозиторием Git.