Как слить ветку с другой веткой в ​​GIT? - PullRequest
6 голосов
/ 12 ноября 2009

Позвольте мне объяснить проблему подробно.

У меня есть основная ветка git, в которой я создал новую боковую ветку bug10101010, теперь я не хочу объединять bug10101010 с main. Пока все хорошо. Теперь у меня есть другая ветвь того же продукта, названная legacy. Я не хочу объединять bug10101010 с устаревшей веткой в ​​GIT.

Есть идеи?

Я не могу просто слить его напрямую, так как ветка bug10101010 выделяется из основной ветви, а в прежней версии мне нужен только diff между веткой bug10101010 и ее родительской ветвью.

Ответы [ 3 ]

6 голосов
/ 14 ноября 2009

Вы должны использовать git rebase --onto и указать диапазон.
(см. git rebase справочную страницу :

трансплантируйте ветку темы на основе одной ветки в другую, чтобы сделать вид, что вы разветвили ветку темы из последней ветки, используя rebase --onto.

).

Конечно, это переместит вашу ветку bug10 поверх ветки legacy, а это не то, что вам нужно / нужно.

Таким образом, одним из обходных путей было бы сделать эту перебазировку в клонированном репо, а затем объединить эту «улучшенную» legacy ветку (ту, что в репозитории клонов, с модификациями bug10 сверху из этого) в локальную и текущую legacy ветку (ту, которую вы хотите изменить, оставив bug10 в покое).

Сейчас:

  • это включает в себя дополнительное репо (что может привести к ограничению дискового пространства)
  • В целом, это довольно эквивалентно определить патч и применить его к legacy ветви, поэтому остальные ответы (патч) являются действительными (и более простыми).
  • единственное преимущество, которое я вижу в этом методе, - это возможность определить унаследованную среду, в которой вы перебазируете то, что хотите (например, коммиты bug10), прежде чем помещать только эту ветку legacy в исходное репо (вы бы не нажимайте bug10, поскольку его история была бы полностью переписана!)

Я просто хотел посмотреть, работает ли он, так что ... Давайте проверим этот подход.
(Git1.6.5.1, на старой версии XP SP2, с сеансом Powershell 1.0 из-за команды Start-Transcript )


PS D:\> mkdir git
PS D:\> cd git
PS D:\git> mkdir tests
PS D:\git> cd tests
PS D:\git\tests> git init mainRepo

Мне нравится, что у меня больше нет возможности сначала создать каталог git repo, а затем ввести его git init! С 1.6.5 :

"git init" научился mkdir / chdir помещать в каталог при наличии дополнительного аргумента (т. Е. "git init this").

Это здорово!

Давайте создадим 3 файла для 3 разных целей.
(Для примера, я буду хранить модификации файлов отдельно для каждой ветви: здесь нет конфликтов при слиянии или перебазировании.)

PS D:\git\tests> cd mainRepo
PS D:\git\tests\mainRepo> echo mainFile > mainFile.txt
PS D:\git\tests\mainRepo> echo contentToBeFixed > toBeFixedFile.txt
PS D:\git\tests\mainRepo> echo legacyContent > legacy.txt
PS D:\git\tests\mainRepo> git add -A
PS D:\git\tests\mainRepo> git ci -m "first commit"
PS D:\git\tests\mainRepo> echo firstMainEvol >> mainFile.txt
PS D:\git\tests\mainRepo> git ci -a -m "first evol, for making 1.0"
PS D:\git\tests\mainRepo> git tag -m "1.0 legacy content" 1.0

В этот момент возвращается git log --graph --oneline --branches:

* b68c1f5 first evol, for making 1.0
* 93f9f7c first commit

Давайте построим legacy ветку

PS D:\git\tests\mainRepo> git co -b legacy
PS D:\git\tests\mainRepo> echo aFirstLegacyEvol >> legacy.txt
PS D:\git\tests\mainRepo> git ci -a -m "a first legacy evolution"

Мы возвращаемся к мастеру, делаем еще один коммит, который мы будем помечать "2.0" (релиз, который потребует исправления ошибок!)

PS D:\git\tests\mainRepo> git co -b master
PS D:\git\tests\mainRepo> git co master
PS D:\git\tests\mainRepo> echo aMainEvol >> mainFile.txt
PS D:\git\tests\mainRepo> git ci -a -m "a main evol"
PS D:\git\tests\mainRepo> echo aSecondMainEvolFor2.0 >> mainFile.txt
PS D:\git\tests\mainRepo> git ci -a -m "a second evol for 2.0"
PS D:\git\tests\mainRepo> git tag -m "main 2.0 before bugfix" 2.0

У нас есть:

* e727105 a second evol for 2.0
* 473d44e a main evol
| * dbcc7aa a first legacy evolution
|/
* b68c1f5 first evol, for making 1.0
* 93f9f7c first commit

Теперь мы делаем bug10 ветку для исправления ошибок:

PS D:\git\tests\mainRepo> git co -b bug10
PS D:\git\tests\mainRepo> echo aFirstBug10Fix >> toBeFixedFile.txt
PS D:\git\tests\mainRepo> git ci -a -m "a first bug10 fix"
PS D:\git\tests\mainRepo> echo aSecondBug10Fix >> toBeFixedFile.txt
PS D:\git\tests\mainRepo> git ci -a -m "a second bug10 fix"

Давайте добавим окончательный коммит в основную ветку

PS D:\git\tests\mainRepo> git co master
PS D:\git\tests\mainRepo> echo anotherMainEvol >> mainFile.txt
PS D:\git\tests\mainRepo> git ci -a -m "another main evol"

Конечное состояние нашего основного репо:

* 55aac85 another main evol
| * 47e6ee1 a second bug10 fix
| * 8183707 a first bug10 fix
|/
* e727105 a second evol for 2.0
* 473d44e a main evol
| * dbcc7aa a first legacy evolution
|/
* b68c1f5 first evol, for making 1.0
* 93f9f7c first commit

На этом этапе я не буду больше манипулировать в mainRepo. Я только клонирую это, чтобы сделать некоторые тесты. Если это не удается, я всегда могу вернуться к этому репо и клонировать его снова.

Первый клон на самом деле обязателен, чтобы выполнить наш git rebase --onto

PS D:\git\tests\mainRepo> cd ..
PS D:\git\tests> git clone mainRepo rebaseRepo
PS D:\git\tests> cd rebaseRepo

Нам нужны две ветви mainRepo в нашем клонированном репо:

PS D:\git\tests\rebaseRepo> git co -b bug10 origin/bug10
PS D:\git\tests\rebaseRepo> git co -b legacy origin/legacy

Давайте перебазируем только bug10 (то есть все коммиты после 2.0 тега до HEAD из bug10 ответвления) :

PS D:\git\tests\rebaseRepo> git co bug10
PS D:\git\tests\rebaseRepo> git rebase --onto legacy 2.0
First, rewinding head to replay your work on top of it...
Applying: a first bug10 fix
Applying: a second bug10 fix

В этот момент bug10 было воспроизведено поверх legacy без всех других промежуточных коммитов .
Теперь мы можем перемотать HEAD из legacy к вершине воспроизводимой ветви bug10.

PS D:\git\tests\rebaseRepo> git co legacy
Switched to branch 'legacy'
PS D:\git\tests\rebaseRepo> git merge bug10
Updating dbcc7aa..cf02bfc
Fast forward
 toBeFixedFile.txt |  Bin 38 -> 104 bytes
 1 files changed, 0 insertions(+), 0 deletions(-)

Содержание соответствует тому, что нам нужно:

  • У нас есть все устаревшее содержимое:

PS D:\git\tests\rebaseRepo> type legacy.txt
legacyContent
aFirstLegacyEvol

  • содержимое для ветви main доступно только до тега 1.0 (корень для ветви legacy), и больше нет .

PS D:\git\tests\rebaseRepo> type mainFile.txt
mainFile
firstMainEvol

  • и исправления bug10 здесь:

PS D:\git\tests\rebaseRepo> type toBeFixedFile.txt
contentToBeFixed
aFirstBug10Fix
aSecondBug10Fix

Вот и все.
Идея состоит в том, чтобы вытянуть эту «улучшенную» ветвь legacy в исходном репо, которая по-прежнему будет иметь bug10 неизменной (т.е. все еще начиная с тега 2.0, и не воспроизводиться нигде, как мы это делали на * 1167). *.
В этом клонированном репо я отслеживаю ветку origin/legacy, чтобы объединить в нее ветку legacy другого удаленного источника: rebaseRepo.

PS D:\git\tests\rebaseRepo> cd ..
PS D:\git\tests> git clone mainRepo finalRepo
PS D:\git\tests> cd finalRepo

PS D:\git\tests\finalRepo> git co -b legacy origin/legacy

В этом оригинальном репо (я только клонировал его, чтобы не связываться с состоянием mainRepo, на случай, если у меня были какие-то другие эксперименты), я объявлю rebaseRepo удаленным и получу его ветви.

PS D:\git\tests\finalRepo> git remote add rebasedRepo D:/git/tests/rebaseRepo
PS D:\git\tests\finalRepo> type D:\git\tests\finalRepo\.git\config
[remote "origin"]
    fetch = +refs/heads/*:refs/remotes/origin/*
    url = D:/git/tests/mainRepo
[branch "master"]
    remote = origin
    merge = refs/heads/master
[branch "legacy"]
    remote = origin
    merge = refs/heads/legacy
[remote "rebasedRepo"]
    url = D:/git/tests/rebaseRepo
    fetch = +refs/heads/*:refs/remotes/rebasedRepo/*

PS D:\git\tests\finalRepo> git fetch rebasedRepo
remote: Counting objects: 8, done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 6 (delta 3), reused 0 (delta 0)
Unpacking objects: 100% (6/6), done.
From D:/git/tests/rebaseRepo
 * [new branch]      bug10      -> rebasedRepo/bug10
 * [new branch]      legacy     -> rebasedRepo/legacy
 * [new branch]      master     -> rebasedRepo/master

Теперь мы можем обновить legacy, не касаясь bug10:

PS D:\git\tests\finalRepo> git merge rebasedRepo/legacy
Updating dbcc7aa..4919b68
Fast forward
 toBeFixedFile.txt |  Bin 38 -> 104 bytes
 1 files changed, 0 insertions(+), 0 deletions(-)

Вы можете повторять процесс столько раз, сколько захотите, всякий раз, когда новые коммиты bug10 необходимо воспроизвести поверх старой ветки legacy, не включая все промежуточные коммиты.

2 голосов
/ 12 ноября 2009

Это трудно сделать. Git сохраняет историю слияний, и если вы «cherrypick» и укажете на коммит в bug10101010 как на родителя (что указывает на то, что вы сделали слияние), Git будет считать, что все коммиты до этого (обратно в точку, где они разделены) были объединены как Что ж. Давать вам проблемы, когда вы хотите выполнить «реальное» слияние.

С другой стороны, вы можете просто вручную сгенерировать патч из этой (и только этой) конкретной фиксации. Но это также создаст вам проблемы, когда вы позже выполните «реальное» слияние, поскольку оно пытается применить ваш обработанный вручную коммит дважды.

Но опять же, поскольку одна ветвь называется "Legacy", я подозреваю, что вы все равно не планируете делать это реальное слияние, и в этом случае вы в значительной степени свободны делать это в любом случае, пожалуйста.

Вот интересное сообщение в блоге на тему .

1 голос
/ 12 ноября 2009

Используйте git-diff , а затем git-apply ?

...