Git: как отделить ветку функций после факта
Как я могу переместить некоторые коммиты функции из существующей ветви в ветку функции и эффективно удалить их из текущей ветви - как если бы функция была разработана в отдельной ветви функции?
Фон: ветка develop
содержит коммиты двух функций A (скажем, 50 коммитов) и B (10 коммитов), дико смешанные, так как они изначально должны были быть в одном выпуске. Теперь мы откладываем функцию B. Таким образом, мы хотим иметь только функцию A в develop
филиал и новый featureB
ветвь, которая содержит коммиты A или A и B, так что если мы объединяем обе ветви, мы получаем текущую позицию develop
ветка.
Один из способов сделать это - создать featureB
ответвление от текущей позиции develop
ветвь, а затем обратно применить коммиты от А до develop
, Но тогда слияние нового develop
а также featureB
будет просто А.
Другим способом было бы переименовать текущий develop
в featureB
и вишня забрать все коммиты А в новый develop
ветка. Но это эффективно изменило бы историю develop
, что хлопотно.
Какой лучший способ сделать это?
4 ответа
Если твой develop
ветка была опубликована, и вы не хотите переписывать ее историю (что было бы самым простым способом), тогда вы действительно можете отменить свои изменения в develop
, начать все заново featureB
ответвление перебазировано B
изменения в верхней части develop
, Что-то в этом роде:
#given history (top - newest)
#shaA3 <-- develop, HEAD
#shaB2
#shaA2
#shaB1
#shaA1
вернуться:
git revert B2
git revert B1
Теперь история содержит:
#revert of shaB1 <-- develop, HEAD
#revert of shaB2
#shaA3
#shaB2
#shaA2
#shaB1
#shaA1
Создайте featureB
и заново воспроизвести возвращенные коммиты заново:
git checkout -b featureB
git rebase -i --onto develop shaB1~1 featureB
Закомментируйте все коммиты, кроме тех, которые принадлежат функции B (shaB1
а также shaB2
в нашем случае) и завершите ребаз. На данный момент у вас должна быть история:
#shaB2' <-- featureB, HEAD
#shaB1'
#revert of shaB1 <-- develop
#revert of shaB2
#shaA3
#shaB2
#shaA2
#shaB1
#shaA1
Чтобы перепроверить, что все прошло хорошо, вы можете сделать git diff shaA3
- должно быть пустым, git diff develop
- должен содержать все желаемые B изменения.
PS Вы, конечно, можете использовать cherry-pick или reverts откатов, чтобы воспроизвести b изменения в branchB
вместо интерактивной перебазировки, например, при разработке:
git checkout -b branchB
git revert <revert of shaB1>
git revert <revert of shaB2>
Дам тебе:
#revert of revert of shaB2 = shaB2' <-- featureB, HEAD
#revert of revert of shaB1 = shaB1'
#revert of shaB1 <-- develop
#revert of shaB2
#shaA3
#shaB2
#shaA2
#shaB1
#shaA1
Безусловно, самый чистый метод, если вы можете вообще с ним справиться, - это просто написать правильную историю и поменять ссылки. Называя базу истории, которую вы хотите разделить X
,
git checkout -b new-develop X
git cherry-pick [all the feature_A commits]
# repeat the cherry-pick as needed or convenient if there's too many
git checkout -b new-featureB X
git cherry-pick [all the feature_B commits]
# ...
затем поменяйте местами имена с git branch -m
, принудительно нажмите и заставьте всех переиздать и перебазировать любую неопубликованную работу по мере необходимости.
Любой другой вариант оставит у вас действительно грязную историю, и нет никаких причин навязывать это всем потомкам, если вообще разумно избегать этого. Избегайте этого, если по какой-либо причине проблема в общении.
Если вы на самом деле не можете этого сделать, тогда посмотрите подробный ответ @MykolaGurov.
(branch
→ checkout -b
,,,)
Создать ветку featureB
от текущего состояния развития. Передать код в ветку develop
для дальнейшего развития FeatureA. Передать код в ветку featureB
для разработки FeatureB. Регулярно перебазировать ветку featureB
против ветви develop
так что в нем есть изменения, которые добавляются для разработки featureA.
Если вы хотите избежать изменения опубликованной истории ветки develop
, как в ответе @thill, а также хотите избежать отмены возвращенных коммитов, как в ответе @Mikola Gorov, вы также можете
- Создать ветку
featureB
отdevelop
- Вернуть коммитов
B
в филиалdevelop
, Было бы разумно сделать это за один коммитrevert B
, так как это одна операция в истории. - Слияние филиал
develop
вfeatureB
со стратегиейours
, Это не меняет никаких файлов в веткеfeatureB
, но помечает отмененный коммит из 2. как уже слил вfeatureB
, Таким образом, если вы позже объедините веткуfeatureB
Вернуться вdevelop
, результат не будет содержать commitrevert B
больше.
При слиянии featureB
Вернуться в develop
Вы могли бы хотеть иметь featureB
в качестве первого родителя коммита. (Например, вы сливаете develop
в featureB
а затем установить develop
в featureB
, а не наоборот.) Я полагаю, таким образом, возвращение больше не будет путать вину и т.д. (?)