Как заметил RomainValeri, график можно нарисовать лучше. Я начну с его варианта, но нарисуйте то, что у нас есть до , и вы попытаетесь выполнить любое слияние:
E---F <<< patch
/
A---B---C---D <<< master
\
G---H <<< my-branch
Имя patch
относится к существующему коммиту F
и имена master
и my-branch
относятся к существующему коммиту H
.
Проблема, с которой вы столкнулись, возникает из-за того, что git merge
работает на основе графа фиксации , а не названия филиалов . Имена find commit, что нормально, насколько это возможно, но если вы запустите git merge <commit F>
, результат будет включать commit F
, который будет включать commit E
, который будет включать commit D
, который будет включать в себя фиксацию C
, которая будет включать в себя фиксацию B
и т. Д.
То есть любое успешное слияние фиксации F
автоматически включает каждый коммит, ведущий к и включая F
сам .
Вы можете использовать от git cherry-pick
до копии отдельных коммитов. Помните, что фиксация - это снимок , полный набор всех файлов. Но каждый коммит также имеет родительский коммит. 1 Если вы сравните снимок в коммите E
со снимком в его родительском коммите D
, вы увидите что вы изменили в коммите E
. Если вы сравните снимок в коммите F
с его родителем в коммите E
, вы увидите то, что вы изменили в коммите F
. Вишневый выбор, по сути, позволяет вам «воспроизвести» эти изменения в другом месте.
Это приводит к рекомендации, которую люди часто дают, чтобы использовать вишневый выбор здесь. Если вы создаете новую ветку с именем patch2
, начиная с коммита H
, и копируйте E
и F
в новые и улучшенные коммиты E'
и F'
, где E'
и F'
после коммита H
, вы получаете:
E---F <-- patch
/
A---B---C---D <-- master
\
G---H <-- my-branch
\
E'-F' <-- patch2
Теперь вы можете объединить оригинальный патч с master
и новым и другим (предположительно улучшенным) patch2
ответвлением в my-branch
.
Есть лучший способ! Ну, в любом случае часто . Давайте на мгновение отступим и спросим: Почему вы сначала написали коммиты E
и F
?
Скорее всего, это исправить ошибку, которая находится в commit A
, или commit B
, или, может быть, оба. Или, возможно, он добавляет функцию, которой просто не хватает в коммите B
. Как бы то ни было, вы считаете, что его можно применить без коммитов C
и D
.
Теперь вам нужно доказать это . Создайте новую ветку patch2
, но не указывайте на существующий коммит H
. Запустите его вместо коммита B
. Затем скопируйте E
и F
в свои новые и улучшенные E'
и F'
, получив:
E--F <-- patch
/
C--D <-- master
/
A--B--E'-F' <-- patch2
\
G--H <-- my-branch
Обратите внимание, что все существующие коммиты все еще там, точно так же. (Вы еще ничего не слили.) Но если patch2
действительно работает, мы можем теперь выбросить ветку с именем patch
, отказавшись от коммитов E-F
:
E--F [abandoned]
/
C--D <-- master
/
A--B--E'-F' <-- patch2
\
G--H <-- my-branch
Теперь, когда мы это сделали, давайте прекратим рисовать их и переименуем существующую ветку patch2
в patch
:
C--D <-- master
/
A--B--E'-F' <-- patch
\
G--H <-- my-branch
Скопированный (выбранный вишней или перебазированный ) коммиты E'-F'
теперь находятся в положении, в котором мы можем запустить git checkout master; git merge patch
и git checkout my-branch; git merge patch
. Когда мы это сделаем, мы получим два коммита слияния:
C--D--M1 <-- master
/ /
A--B--E'-F' <-- patch
\ \
G--H--M2 <-- my-branch
Эти слияния ведут себя так, как вам нравится: они приносят только патч в ветку, а не любые другие коммиты, не связанные с патч.
Это приводит к общему правилу создания патчей
Если вы собираетесь исправить ошибку или добавить общую функцию, найдите самый ранний коммит в вашем графике , где ошибка или функция могут go. В этом случае это было сразу после коммита B
. (Может быть возможно поставить его сразу после коммита A
, и если это так, вы можете сделать это вместо этого. Если баг или функция требует части коммита B
, то коммит B
является самым дальним спиной, который вы можете go.)
Причина этого заключается в том, что полученный патч может быть чисто объединен с любым коммитом, который находится "ниже по потоку" от этой точки. В этом случае вы хотели объединить патч с коммитами D
, на master
ниже B
и в H
на my-branch
ниже B
. Поскольку patch
в коммите F'
находится ниже B
, объединение patch
в любую ветку приведет к коммиту B
. Но commit B
- это первое место, в котором ветка patch
является полезной . Любая ветвь, кончик которой не является ниже B
, вообще не может использовать патч! Таким образом, поместив сам патч сразу после первого места, где он может быть использован , каждая другая ветвь, в которой может использовать патч вообще, может использовать патч легко и чисто.