Задать вопрос
@historydev

Как безопасно, выборочно извлечь файлы/папки из одной ветки (сохраняя историю) и переместить в другую?

У меня 3 ветки: M, X, Y.
  • В ветке M есть файл m.txt, ветки X и Y ветвились от M, в них есть свои файлы x.txt в ветке X, y.txt в ветке Y и m.txt в обоих.
  • В каждой ветке были внесены изменения в m.txt: 2 коммита в M, 3 коммита в X и 3 коммита в Y.

Как переместить в новую ветку W файл m.txt и его историю из 8 коммитов?
  • Вопрос задан
  • 172 просмотра
Подписаться 2 Простой 11 комментариев
Пригласить эксперта
Ответы на вопрос 2
@Vitsliputsli
Мне нужно отделить фичу размазанную по веткам в отдельную ветку

Создаете ветку W в HEAD ветки M. С помощью git checkout затягиваете в нее нужные файлы. Проще конечно это сделать в какомнибудь gui (в IDEA IDE это называется get from Revision).

Как переместить в новую ветку W файл m.txt и его историю из 8 коммитов?

Довольно сомнительная хотелка, придется создать много новых коммитов.
Создаете ветку W в HEAD ветки X, затем удаляете все ненужное
git filter-branch --tree-filter 'rm -f x.txt' X
Создаете ветку W2 в HEAD ветки Y, затем
git filter-branch --tree-filter 'rm -f y.txt' Y
затем мержите W и W2. Разумеется можно и в другом порядке.
Ответ написан
Комментировать
sergey-kuznetsov
@sergey-kuznetsov Куратор тега Git
Автоматизатор
Если ваша фича вся в одном файле и другие фичи этот файл не трогали то вот пару методов:

Способ A: format-patch → am
# Общая база
B=$(git merge-base --octopus M X Y)

# База для W
git checkout -b W "$B"

# Пакеты патчей только по m.txt (полный путь от корня!)
git format-patch --no-stat --keep-subject -o /tmp/patches_M "$B"..M -- path/to/m.txt
git format-patch --no-stat --keep-subject -o /tmp/patches_X "$B"..X -- path/to/m.txt
git format-patch --no-stat --keep-subject -o /tmp/patches_Y "$B"..Y -- path/to/m.txt

# Применяем по порядку
git checkout W
git am --3way /tmp/patches_M/*.patch
git am --3way /tmp/patches_X/*.patch
git am --3way /tmp/patches_Y/*.patch
# при конфликте: правим path/to/m.txt, затем git add path/to/m.txt && git am --continue

# Контроль: кроме m.txt ничего не менялось
git diff --name-only "$B"..W -- . ':!path/to/m.txt'


Способ B: git-filter-repo → cherry-pick
# Имена веток и путь к файлу
P=path/to/m.txt
# В исходном репо: M, X, Y — исходные ветки

# 1) Временный клон и очистка истории до одного файла
git clone --no-local . ../tmp-m
cd ../tmp-m
git filter-repo --force --path "$P" --refs M X Y

# 2) Во втором репо найдём общий предок ОЧИЩЕННЫХ веток и дадим ему имя
B2=$(git merge-base --octopus M X Y)
git branch base_mxy "$B2"
cd -

# 3) В основном репо создаём базовую ветку W на настоящем предке (старое дерево целиком)
B=$(git merge-base --octopus M X Y)
git checkout -b W "$B"

# 4) Подключаем очищенный репозиторий как внешний и подтягиваем нужные refs
git remote add onlym ../tmp-m
git fetch onlym base_mxy M X Y

# 5) Переносим ТОЛЬКО коммиты после B2, в нужном порядке
git cherry-pick onlym/base_mxy..onlym/M
git cherry-pick onlym/base_mxy..onlym/X
git cherry-pick onlym/base_mxy..onlym/Y
# при конфликте: правим $P, git add "$P", затем git cherry-pick --continue

# 6) Контроль: кроме m.txt ничего не менялось
git diff --name-only "$B"..W -- . ':!'"$P"
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы
Сбер Санкт-Петербург
До 100 000 ₽
ИТРУМ Ростов-на-Дону
от 75 000 ₽