По сути, вы должны сказать Git: Я хочу перебазировать cool-feature-B
против master
, но я хочу скопировать набор коммитов, отличный от тех, которые вы обычно вычисляетездесь. Самый простой способ сделать это - использовать --onto
.То есть, как правило, вы запускаете, как вы сказали:
git checkout cool-feature-B
git rebase master
, но вам нужно будет сделать:
git rebase --onto master cool-feature-A
до , вы удалите свою собственную ветку-имя / метка cool-feature-A
.
Вы всегда можете сделать это, даже если они используют обычное слияние. Это не помешает, за исключением того, что вы должны ввестипобольше и помните (как вам нравится), что вам понадобится этот --onto
, которому позже понадобится правильное имя или хеш-идентификатор.
(Если вы можете получить хеш-идентификатор коммита, к которому cool-feature-A
баллов в данный момент в reflog для вашего апстрима cool-feature-B
, вы можете использовать функцию --fork-point
, чтобы Git автоматически вычислял это для вас позже, при условии, что срок действия записи reflog не истек. Но это, вероятно, сложнее,в общем, чем просто делать это вручную. Плюс у него есть вся эта «предоставленная» часть.)
Почему это так
Начнем, как обычно, с графических рисунков.Изначально у вас есть эта настройка в вашем собственном репозитории:
...--A <-- master, origin/master
\
B--C <-- cool-feature-A
\
D <-- cool-feature-B
После того, как вы запустите git push origin cool-feature-A cool-feature-B
, вы получите:
...--A <-- master, origin/master
\
B--C <-- cool-feature-A, origin/cool-feature-A
\
D <-- cool-feature-B, origin/cool-feature-B
Обратите внимание, что все, что мы здесь сделали, это добавили два origin/
имен (два имени для удаленного слежения): в их хранилище, в origin
они получили коммиты B
, C
и D
и установили свои *Запоминаемые имена 1046 * cool-feature-A
и cool-feature-B
фиксируют коммиты C
и D
соответственно, поэтому ваш собственный Git добавил ваш origin/
варианты этих двух имен.
Еслиони (кем бы «они» ни были - люди, которые управляют хранилищем в origin
) выполняют быстрое слияние, они передвигают своего мастера до точки фиксации C
.(Обратите внимание, что веб-интерфейс GitHub не имеет кнопки для быстрого слияния. Я не использовал веб-интерфейс GitLab; он может отличаться по-разному.) Если они вызываютреальное слияние - это то, что по умолчанию делает веб-страница GitHub «слить сейчас»;опять же, я не знаю, что GitLab делает здесь - они сделают новый коммит слияния E
:
...--A------E
\ /
B--C
\
D
(здесь я намеренно удалил все именапоскольку их не совсем совпадают с вашими).Они, вероятно, удалят (или, возможно, даже никогда не будут созданы) свое имя cool-feature-A
.В любом случае, вы можете иметь свое собственное Git fast-forward ваше master
имя при обновлении ваших origin/*
имен:
...--A------E <-- master, origin/master
\ /
B--C <-- cool-feature-A [, origin/cool-feature-A if it exists]
\
D <-- cool-feature-B, origin/cool-feature-B
или:
...--A
\
B--C <-- cool-feature-A, master, origin/master [, origin/cool-feature-A]
\
D <-- cool-feature-B, origin/cool-feature-B
Независимо от того, удаляете ли вы свое имя cool-feature-A
сейчас - для удобства на последующих рисунках, скажем, вы делаете - если вы запустите:
git checkout cool-feature-B
git rebase master
Git теперь будет перечислять список коммитов, доступных с D
- D
, затем C
, затем B
и т. Д. - и вычтите список коммитов, достижимых из master
: E
(если он существует), затем A
(если E
существует) и C
(существует или нет E
существует), затем B
и так далее.Результатом вычитания будет просто фиксация D
.
Теперь ваш Git копирует коммиты в этом списке, так что новые копии появляются после подсказки master
: то есть после E
, если они сделалиреальное слияние, или после C
, если они произвели ускоренное слияние.Так что Git либо копирует D
в новый коммит D'
, который идет после E
:
D' <-- cool-feature-B
/
...--A------E <-- master, origin/master
\ /
B--C
\
D <-- origin/cool-feature-B
, либо оставляет D
в одиночку, потому что он уже идет после C
(так что ничего новогорисовать).
Хитрые части возникают, когда они используют любой эквивалент GitLab - это нажимаемые кнопки "сквош и слияние" или "перебазировать и объединить" в GitHub.Я пропущу случай «перебазирования и слияния» (который обычно не вызывает проблем, потому что перебаз Git тоже проверяет идентификаторы патчей) и перейду непосредственно к сложному случаю, «сквошу и слиянию».Как вы правильно заметили, это делает новый и другой , single, commit.Когда вы вносите этот новый коммит в свой собственный репозиторий, например, после git fetch
, вы получаете:
...--A--X <-- master, origin/master
\
B--C <-- cool-feature-A [, origin/cool-feature-A]
\
D <-- cool-feature-B, origin/cool-feature-B
where X
является результатом создания нового коммита, чей снимок будет соответствовать слиянию E
(если они должны были сделать слияние E
), но чей (единственный) родительский объект является существующим коммитом A
,Таким образом, история - список коммитов, перечисленных при работе в обратном направлении - от X
- это просто X
, затем A
, тогда все коммиты предшествуют A
.
Если вы запустите обычный git rebase master
в то время как на cool-feature-B
, Git:
- перечисляет коммиты, достижимые с
D
: D
, C
, B
, A
, ...; - перечисляет коммиты, достижимые из
X
: X
, A
, ...; - вычитает набор в шаге 2 из набора в шаге 1, оставляя
D
, C
, B
; - копирует эти коммиты (в обратном порядке), чтобы они следовали после
X
.
Обратите внимание, что оба шага 2 и 3 используютслово master
для поиска коммитов: коммиты для копирования на шаге 2 - это те, которые недоступны с master
.Место для размещения копий для шага 3 - после кончика master
.
Но если вы запустите:
git rebase --onto master cool-feature-A
, у вас есть использование Git различные элементы в шагах 2 и 3:
- Список коммитов для копирования, начиная с шага 2, исходит из
cool-feature-A..cool-feature-B
: вычтите C-B-A-...
из D-C-B-A-...
.Это оставляет просто коммит D
. - Место для размещения копий, на шаге 3, происходит от
--onto master
: поместите их после X
.
Так что теперь Gitтолько копирует D
в D'
, после чего git rebase
возвращает имя cool-feature-B
, чтобы указать D'
:
D' <-- cool-feature-B
/
...--A--X <-- master, origin/master
\
B--C <-- cool-feature-A [, origin/cool-feature-A]
\
D <-- origin/cool-feature-B
, что вы и хотели.
Если бы они - люди, контролирующие репозиторий GitLab - использовали истинное слияние или ускоренную перемотку вперед, а не слияние вообще, это все бы работало: ваш Git перечислил бы D
-on-в обратном направлении, но удалите C
-в обратном направлении из списка, оставив только D
для копирования;и затем Git скопирует D
так, чтобы он шел после E
(истинный случай слияния) или C
(случай ускоренной перемотки вперед).Случай ускоренной перемотки «скопируйте D так, чтобы он находился там, где он уже есть», умело не стал бы копировать вообще и просто оставил все на месте.