Чем результат перебазирования может отличаться от результата слияния?
В одной из статей GitHub я прочитал следующее:
Вы не можете автоматически перебазировать и объединить на GitHub, когда: Перебазирование коммитов считается "небезопасным", например, когда перебазирование возможно без конфликтов слияния, но приведет к отличному результату, чем слияние.
Мне не ясно, как перебазирование может привести к другому результату, чем слияние.
Кто-нибудь может объяснить, как это возможно?
Ссылка на оригинальную статью: https://help.github.com/articles/about-pull-request-merges/
1 ответ
Вот конструктивное доказательство случая, когда перебазирование и слияние дают разные результаты. Я предполагаю, что это тот случай, о котором они говорят. Редактировать: существует еще один случай, когда объединение ветвей происходит, когда боковая ветвь, подлежащая перебазированию, содержит или объединяет одну фиксацию, которая будет пропущена (из-за совпадения идентификатора патча) во время перебазировки с последующей реверсией этого совершать (это не будет пропущено). См. Изменения в файле не сохраняются слиянием, почему? Если у меня будет время, я постараюсь добавить доказательство конструкции и для этого примера.
Хитрость заключается в том, что поскольку rebase-копии фиксируют, но пропускают слияния, нам нужно отбросить слияние, разрешение которого не является простой композицией его предшественников. Чтобы у этого слияния не было конфликтов, я думаю, что это должно быть "злое слияние", так что это то, что я положил в сценарий.
График, который мы строим, выглядит так:
B <-- master
/
A--C--E <-- branch
\ /
\ /
D <-- br2
Если вы на master
(ваш совет коммит B
) и ты git merge branch
это сочетает в себе изменения от A
vs- B
с теми, кто от A
vs- E
, Полученный график:
B-----F <-- master
/ /
A--C--E <-- branch
\ /
\ /
D <-- br2
и содержание коммита F
определяются теми из A
, B
, а также E
,
Если вы на branch
(ваш совет коммит E
) и ты git rebase master
эта копия передает C
а также D
в каком-то порядке (не понятно, какой именно). Он полностью пропускает коммит E
, Полученный график:
B <-- master
/ \
A C'-D' <-- branch
\
D <-- br2
(оригинал C
а также E
доступны только через reflogs и ORIG_HEAD
). перемещение master
в ускоренном порядке, верхушка master
становится совершенным D'
, Содержание коммита D'
определяются путем добавления изменений, извлеченных из C
а также D
в B
,
Поскольку мы использовали "злое слияние", чтобы внести изменения в E
которые появляются ни в одном C
ни D
эти изменения исчезают.
Вот сценарий, который создает проблему (обратите внимание, это делает временный каталог tt
что он уходит в текущий каталог).
#! /bin/sh
fatal() {
echo fatal: "$@" 1>&2; exit 1
}
[ -e tt ] && fatal tt already exists
mkdir tt && cd tt && git init -q || fatal failed to create tt repo
echo README > README && git add README && git commit -q -m A || fatal A
git branch branch || fatal unable to make branch
echo for master > bfile && git add bfile && git commit -q -m B || fatal B
git checkout -q -b br2 branch || fatal checkout -b br2 branch
echo file for C > cfile && git add cfile && git commit -q -m C || fatal C
git checkout -q branch || fatal checkout branch
echo file for D > dfile && git add dfile && git commit -q -m D || fatal D
git merge -q --no-commit br2 && git rm -q -f cfile && git commit -q -m E ||
fatal E
git branch -D br2
git checkout -q master || fatal checkout master
echo merging branch
git merge --no-edit branch || fatal merge failed
echo result is: *
echo removing merge, replacing with rebase of branch onto master
git reset -q --hard HEAD^ || fatal reset failed
git checkout -q branch || fatal switch back to master failed
git rebase master || fatal rebase failed
echo result is: *
echo removing rebase as well so you can poke around
git reset --hard ORIG_HEAD