Объединить ряд ревизий с Git или TortoiseGit
Я пытаюсь объединить изменения, сделанные в главной ветви моего репозитория, в ветку разработки с помощью git или TortoiseGit. Я знаю, что могу просто использовать либо git тянуть или слияние, но это сливается в слишком много изменений в ветке развития сразу, и делает его более трудным для разрешения конфликтов.
Если бы я использовал SVN или TortoiseSVN, я мог бы просто объединить изменения из основного ствола немного за раз, а не все сразу, использование диапазона ревизий для слияния. Могу ли я сделать что-то подобное с git или TortoiseGit? То есть, могу ли я объединить ряд изменений в моей ветви разработки вместо слияния всех изменений сразу?
3 ответов
резюме
использовать Git из командной строки, вы можете перебазировать feature
на master
:
git rebase master feature
# Now force push the changes to your remote
git push <remote> feature --force
предупреждение: не заставляйте небрежно нажимать rebased (переписанные) коммиты / ветви если вы разделяете эти коммиты / ветви с другими людьми, потому что вы вызовете конфликты с любыми изменениями, которые они основывали на более старых версиях коммитов/ветвей. Можно работать таким образом в (очень) небольших командах, но хорошо требуется координация.
вы также можете использовать --onto
флаг, чтобы указать ряд, где <start commit>
является эксклюзивным началом диапазона:
git rebase --onto master <start commit> feature
наконец, cherry-pick
может фактически принять ряд аргументов. С master
филиала выезжали:
# Cherry-pick all commits from `<start commit>`
# exclusive, not included) to feature:
git cherry-pick <start commit>..feature
Подробное Объяснение
моей первоначальной целью было синхронизировать мой feature
филиала с изменениями, внесенными в master
, но я не хотел просто раствориться в master
потому что это вызвало слишком много конфликтов, которые должны быть решены сразу. Я просто хотел быть в состоянии постепенно сливаться с изменениями, чтобы я мог разрешать конфликты меньшими, более управляемыми частями.
Enter git rebase
лучший способ сделать это на самом деле git rebase
. Вообще-то, это идеально. Что он будет делать, это взять все коммиты, которые я сделал в feature
и сделать копии!--46--> из тех, кто совершает в том же порядке, за исключением того, что он возобновляет их поверх последней редакции целевой ветви, что в моем случае было master
.
это важно по двум причинам:
это эквивалентно слиянию
master
наfeature
. Почему? Потому чтоfeature
в основном воссоздан поверх самых последних версийmaster
все...master
теперь коммиты существуют в историяfeature
, в том числе совершает вmaster
этоfeature
еще не было.Git повторно применяет коммиты один раз, для того, чтобы, если есть какие-либо конфликты, они вводятся в процесс в нескольких меньших, более управляемых частях, по одному за раз, что именно то, что я надеялся сделать!
вот как это выглядит визуально (примеры адаптированы из официальный Linux ГИТ документации к ядру):
A---B---C feature
/
D---E---F---G master
в приведенном выше примере commits F
и G
, где совершил на master
так как я ветвистый feature
от него. Я хочу синхронизировать эти изменения с feature
:
git rebase master feature
теперь мои ветви выглядят так:
A'--B'--C' feature
/
D---E---F---G master
\
A---B---C (no branch)
совершает A
через C
были воссозданы как A'
через C'
поверх последней версии master
. Старые коммиты все еще существуют в репозитории Git, но поскольку нет указателя ветви, который ссылается на них, они в конечном итоге будут мусором, собранным Git.
Да, вы можете сделать это. Предположим, что ваш репозиторий выглядит следующим образом:
master
A---[B]
\
\ feature
(c1)---(c2)---(...)---(c100)
вы хотите объединить feature
филиала в master
, но там много коммитов. Вместо этого, давайте сделаем новую ветку tmp
это указывает на более раннюю фиксациюfeature
:
git branch tmp c2
A---[B]
\
\ tmp feature
(c1)---[(c2)]---(...)---(c100)
теперь tmp
указывает на c2
. Теперь мы можем объединить только коммиты c1...c2
в master без учета c3...c100
:
git checkout master
git merge tmp
сейчас,tmp
на следующий пакет коммитов (нам нужно -f
to force
, поскольку tmp
уже существует). Например, если мы хотим перейти к c6
теперь использовать это:
git branch -f tmp c6
повторите это, пока все коммиты, которые вы хотите объединить в.
Примечание: ответ Джона правильный; это просто дополнительное объяснение, основанное на последующих вопросах в комментариях – мне просто нужно было немного больше места:)
мне нравится идея не создавать временную ветвь для слияния в коммитах, но проблема с использованием
git merge <commit-hash>
это только слияние в одной редакции, а не их диапазон (правильно?). Если бы у меня было 100 ревизий для слияния, разве мне не пришлось бы использовать эту команду 100 раз?
нет, в Git, история всегда связана. Поэтому, если вы объедините фиксацию в master и между общим предком master
и что фиксация больше фиксаций, они полностью сохранены. В отличие от SVN, Git сохраняет полные ссылки на предыдущие коммиты (с неограниченным количеством указателей, поэтому вы можете объединить сразу несколько ветвей). Так что в итоге вы всегда будете видеть, где начиналась ветка, что на ней происходило, что было объединено между собой и где было объединено назад в главную ветку-только название (вернее метка) ветви не сохраняется (за исключением текста автоматического слияния, если это считается ^^).
так что ваша история может, например, выглядеть так:
* -- A -- * ---------- * ----- * -- * -- M [master]
\
\
B1 -- B2 -- B3 -- B4 -- B5 -- B6 -- B7 -- B8 -- B9 [br]
Предположим, вы хотите объединить B9
(руководитель совершает на ветке br
) в M
где master
бранч указывает на. При прямом слиянии вы получите это (#
слияния коммитов):
* -- A -- * ---------- * ----- * -- * -- M ---------------- # [master]
\ /
\ /
B1 -- B2 -- B3 -- B4 -- B5 -- B6 -- B7 -- B8 -- B9 [br]
так даже если вы удалите указатель ветви br
, вы все еще можете видеть все коммиты, которые произошли в этой отдельной ветви.
или если вы хотите объединить в несколько шагов, вы можете легко объединить его такой:
* -- A -- * -- * -- * -- * -- M -- #---------- # --------------- # --- # [master]
\ / / / /
\ / / / /
B1 -- B2 ------------ - B3 -- B4 -- B5 -- B6 -- B7 -- B8 -- B9 [br]
и снова вы всегда можете оглянуться на все дерево и все отдельные коммиты, которые были сделаны на ветке-даже если вы удалите ветку (которая снова просто удаляет указатель).
так что, возможно, это объяснение также покажет вам что вам не обязательно делать слияния такими маленькими шагами. Вы никогда не потеряете информацию во время слияния, так как всегда можете оглянуться на все коммиты.