2015-02-17 4 views
6

Предположим, что у меня есть следующая история, где верхняя строка является главной ветвью, нижняя - это ветвь функции, которая объединена с мастером в одной точке, а D просто возвращает C (это означает, что рабочий каталог тот же в B и D).Rebasing для изменения родительского элемента фиксации слияния

A---B---C---D   master 
\ \ 
    E---F---G    feature 

Я хочу добавить C и его возвращение D истории до слияния в F, как это:

A---B---C---D   master 
\   \ 
    E-----------F'--G' feature 

Я не хочу, чтобы изменить E (который на самом деле длинная серия коммитов).

git rebase --onto D B (как предложено here) приводит к конфликтам слияния (с или без --preserve-merges).

Есть ли способ выполнить то, что я хочу?

+3

Что-то вроде 'git update-ref feature_branch $ (git commit-tree -p E -p B -m" fakeout merge "F^{tree})', а затем 'git cherry-pick G' должен работать –

+0

@AndrewC для копирования сообщения фиксации 'git show -s -pretty =% BF' | git commit-tree и т. д. '. – jthill

ответ

8

Большинство методов будет несколько болезненным. Существует умеренно безболезненная версия, использующая git filter-branch, за исключением того, что сама фильтрующая ветвь болезненна. :-) (вы фильтр совершает F и G и написать коммит-фильтр, который заменяет в новых родителей, которые вы хотите для F', и пусть работа фильтра-ветви заменить отцовство для G.)

Я думаю, Простейший метод, который не прибегает к командам низкого уровня, - это просто создать новое слияние, а затем перезагрузить G на новое слияние. Новое слияние может иметь конфликты, но мы не заботимся, мы просто хотим взять дерево старого Merge, которые мы можем сделать так:

$ git checkout <sha1> # Use D or E's sha-1 here. 
         # Note: whichever you use will 
         # be the first parent of our new 
         # merge; choose based on that. 
$ git merge --no-commit <sha1> # use the remaining sha-1 here 
[ignore resulting stuff] 
$ git rm -rf .   # Note: assumes you're in top dir of work tree 
$ git checkout <sha1-of-F> -- . 
$ git commit   # Create merge commit F' 

первым checkout получает вас на отдельный РУКОВОДИТЕЛЬ с одним SHA- 1, merge --no-commit запускает процесс слияния с другим SHA-1, git rm -rf . выбрасывает объединенное дерево и любые конфликты, а git checkout <id> -- . заполняет индекс и рабочее дерево из предыдущего слияния. Финал git commit создает слияние F' с тем же деревом, что и слияние F, но с разными родителями.

На данный момент (все еще с отсоединенной головкой) вы можете переустановить (или вишневое) совершить G (или много коммитов G), а затем заставьте свою ветвь указывать на кончик нового графика. Я бы предложил использовать git rebase ... --onto HEAD, но я не тестировал это с отдельной головкой, и есть хотя бы один способ, которым это может пойти не так (разрешение HEAD на ID слишком поздно).

Команда нижнего уровня git commit-tree может быть даже проще. Andrew C wrote the correct command in a comment, хотя вы должны указать название филиала git update-ref. [Изменить: возможно, не совсем правильно, два родителя, которых вы хотите, - D и E, а не D и B. Опять же, поставьте тот, который вы хотите, в качестве первого родителя.]

Преимущество (?) Использования более знакомых команд в том, что они, ну, более знакомы.

+0

Спасибо большое! Я смог сделать оба подхода работать довольно просто (просто используя 'cherry-pick' для' G' commits). –

+0

Yup возглавляет, взял родителей с неправильной диаграммы. –

+0

Спасибо в любом случае @ Andrew C-я понял, что вы имели в виду. –

Смежные вопросы