У меня есть локальный репозиторий git с удаленным апстримом в Github. Я создал ветку A в своем локальном репозитории для новой функции, над которой работал, а затем отправил запрос на извлечение для этой ветки. Ожидая, пока этот PR будет объединен с восходящей ветвью, я хотел поработать над некоторыми другими функциями, основанными на branchA. Поэтому я создал новую ветку A1 из ветки A и внес в нее некоторые изменения.
Теперь мой PR для ветки A удален и объединен с удаленным восходящим потоком в Github, и я хотел бы отправить PR для моей ветки A1. Что я сделал, так это то, что я сначала получил изменения из удаленного восходящего потока в мою локальную основную ветку. Затем я попытался выполнить git rebase master
в ветке A1, но эта команда, кажется, удаляет все изменения, которые я сделал в ветке A1.
Что лучше всего сделать сейчас, если я хочу отправить PR из филиала A1?
Если я сделал что-то не так в своем рабочем процессе, как правильно работать над новыми функциями, ожидая слияния PR из родительской ветки?
TL;DR: вы, вероятно, хотите git rebase --onto
(для чего нужны дополнительные параметры).
Когда вы — или кто-то другой; Я буду использовать здесь «они» и предположу, что кто-то сделал «сквош и слияние» с вашим первоначальным запросом на вытягивание, они заменены ваши коммиты с одним новым коммитом, который, по их мнению, лучше вашего исходного коммита.
Однако у вас все еще есть исходный коммит(ы). Теперь ваша задача — использовать git rebase
для копирования Только хороших коммитов на ваш A1
, а не коммитов с заменой на лучший, которые были и на A1
, и на A
, а теперь есть только на A1
.
Чтобы представить это в графической форме, ты имел, например:
...--o--o--A <-- master, origin/master
\
B--C--D <-- branch-A
\
E--F--G <-- branch-A1
где каждая заглавная буква обозначает коммит, т. е. A
— это хэш-идентификатор коммита, который было на кончике вашего (и их) master
, когда вы начали, B
— это первый коммит, который вы сделали на своем branch-A
, и так далее. .
Потом сказали: Хорошо, нам нравятся ваши B-C-D
коммиты. Но мы собираемся «улучшить» их, сделав новый коммит H
, который является результатом объединениеB
+ C
+ D
в один большой коммит, который мы поместим в конец нашего master
(вашего origin/master
). Тем временем они могли или не могли добавить еще несколько коммитов в свои master
— давайте проведем один раунд o
, чтобы представить любые такие неинтересные коммиты. Как только вы запускаете git fetch origin
, вы получаете коммиты ихновый (у вас уже есть их старые) в репозиторий твой, обновляя свой origin/master
:
o--H <-- origin/master
/
...--o--o--A <-- master
\
B--C--D <-- branch-A
\
E--F--G <-- branch-A1
Если вы git checkout master
и git merge origin/master
, вы переместите свой master
вперед, чтобы он соответствовал вашему origin/master
:
...--o--o--A--o--H <-- master, origin/master
\
B--C--D <-- branch-A
\
E--F--G <-- branch-A1
Ваш branch-A
все еще существует, если вы его специально не удалили. Но даже если вы его удалили, у вас все еще есть ваши B-C-D
коммиты:
...--o--o--A--o--H <-- master, origin/master
\
B--C--D--E--F--G <-- branch-A1
Если вы теперь git checkout branch-A1
и git rebase master
— независимо от того, есть ли у вас все еще ваше имя branch-A
, указывающее на ваш коммит D
, который они выбросили в пользу своего H
— ваш Git теперь попытается скопировать коммиты все шесть, B-C-D-E-F-G
, поверх H
, в попытке произвести это:
B'-C'-D'-E'-F'-G' <-- branch-A1 (rebased)
/
...--o--o--A--o--H <-- master, origin/master
\
B--C--D--E--F--G [abandoned]
Но копировать B
поверх H
не получится: будет конфликтовать сам с собой, плюс нужен эффект C
и D
удаленный. Так что это будет выглядеть так, как будто вы пытаетесь удалить свой собственный код только для того, чтобы вернуть его снова, когда вы копируете C
в C'
, а затем D
в D'
. В любом случае все от этого бесполезен.
На данный момент вы хотели бы сообщить Git не Скопируйте все мои коммиты на branch-A1
, которых нет на master
, чтобы перейти к H
в конце master
, а скорее: Скопируйте только немного моих коммитов на branch-A1
, которых нет на master
, чтобы перейти после H
на кончике master
. Набор коммитов, которые вы хотите скопировать, в данном конкретном примере — это E-F-G
. Вы хотите завершить с:
E'-F'-G' <-- branch-A1 (rebased)
/
...--o--o--A--o--H <-- master, origin/master
\
B--C--D--E--F--G [abandoned]
способ, чтобы сообщить Git, что нужно использовать git rebase --onto
, который принимает аргументы два вместо одного. Вы хотите перебазировать * на master
и хотите исключить коммит D
и все, что раньше (C
, B
, A
и все скучные коммиты слева от A
). Итак, если у вас делать все еще есть имя branch-A
, идентифицирующее коммит D
, вы можете использовать:
git checkout branch-A1 # make sure you're on the right branch
git rebase --onto master branch-A # tell Git: copy only commits after branch-A
Если у вас не есть имя branch-A
, вы можете использовать необработанный хэш-идентификатор коммита в качестве ограничителя вместо имени branch-A
, или вы можете запускать git rebase -i master
и удалять нежелательные pick
команды; или вы можете запустить git log --all --decorate --oneline --graph
и подсчитать коммиты или что вам нужно, чтобы найти коммит.
Обратите внимание, что после перебазирования у вас есть новыйbranch-A1
(совершенно не связанный история с вашим предыдущим branch-A1
, хотя три коммита скопированы, чтобы иметь тот же эффект). Поэтому вам нужно будет принудительно отправить это в любую веб-службу, которую вы используете для обработки запросов на вытягивание (очевидно, GitHub). Вы можете использовать --force-with-lease
, если беспокоитесь, что кто-то другой может добавить коммиты к вашему branch-A1
на сервере GitHub.