Короткий ответ — да. Git позволяет переносить отдельные коммиты (git cherry-pick) и отдельные хунки/файлы между ветками (git checkout/restore --patch, git add -p, git apply и т.п.). Как сложно — зависит от того, в каком виде сейчас находится нужная правка. Если она уже выделена отдельным коммитом — всё просто (cherry‑pick). Если она «пачкой» с другими изменениями — надо либо изолировать её (split commit / интерактивный rebase), либо интерактивно вытащить хунки в целевую ветку.
Ниже — практические способы и рекомендации.
1) Самый простой: если есть отдельный коммит с нужной функцией
- Переключиться в change_b:
git checkout change_b
- Перенести коммит:
git cherry-pick -x <commit-hash>
Ключ -x добавит в сообщение ссылку на исходный коммит, что полезно для трассировки.
2) Если нужная функция внутри большого коммита — лучше выделить её в отдельный коммит в change_a и потом cherry-pick
- На change_a запустите интерактивный ребейз и пометьте проблемный коммит как edit:
git checkout change_a
git rebase -i <base-commit> # или HEAD~N, где N охватывает тот коммит
- Когда процесс остановится на edit:
git reset HEAD^ # откатить коммит в рабочую директорию
git add -p # интерактивно проиндексировать только хунки с нужной функцией
git commit -m "Isolate function X"
git add -A
git commit -m "Remaining changes"
git rebase --continue
- Теперь в change_b:
git checkout change_b
git cherry-pick -x <hash-of-isolated-commit>
Важно: не делайте rebase/переписывание истории на ветке, которая уже пушена и используется другими, без согласования.
3) Если вы не хотите/не можете править историю change_a — вытащить хунки напрямую в change_b
- Переключитесь в change_b:
git checkout change_b
- Используйте checkout/restore --patch, чтобы интерактивно взять хунки из файлов change_a:
git checkout change_a --patch -- path/to/file # (в современных git можно использовать git restore --source change_a --patch path/to/file)
# git предложит выбирать хунки для замены в рабочей директории
- Затем stage и commit:
git add -p
git commit -m "Bring function X from change_a"
Альтернативно можно взять diff и применить:
git diff change_b..change_a -- path/to/file > /tmp/patch
(отредактировать патч только с нужной функцией)
git apply --index /tmp/patch
git commit -m "..."
Ещё вариант: cherry-pick без коммита и потом удалить лишнее:
git checkout change_b
git cherry-pick -n <commit-containing-many-things> # в рабочей директории появятся все изменения
git reset -p # убрать ненужные хунки из индексa
git add -p # оставить только нужные
git commit -m "Only function X"
4) Возможные проблемы при последующем слиянии в main
- Если вы перенесли фрагмент из change_a в change_b (cherry-pick или вручную), а затем будете мержить change_a в main, возможны конфликты или дублирование усилий. Git сравнивает содержимое, а не только идентификаторы коммитов:
- Если изменение в change_b идентично тому, что осталось в change_a, при мерже Git обычно не создаст «дупликата» кода, но может возникнуть конфликт, если контекст файлов изменился в обоих ветках.
- Если вы cherry-pick'или коммит из change_a и сохранили ссылку (-x), это облегчает отслеживание того, что изменение уже перенесено.
- Рекомендации:
- Используйте git cherry-pick -x, чтобы оставить след.
- По возможности выделите (split) изменения в отдельный чистый коммит и переносите именно его — тогда при мерже будет проще.
- При будущем слиянии change_a в main можно перед мержем сделать rebase change_a на main (если история не публичная) и удалить/переопределить дублирующие коммиты, либо разрешать конфликты по месту.
- Включите git rerere, если часто решаете одни и те же конфликты.
Итого: технически это вполне нормальная и стандартная задача. Если хотите, могу написать конкретную последовательность команд для вашего конкретного случая — укажите, какие файлы/коммиты содержат требуемую функцию и какая история (commit hashes или пример) у вас сейчас.