Объединить ряд ревизий с Git или TortoiseGit - PullRequest
9 голосов
/ 21 февраля 2012

Я пытаюсь объединить изменения, сделанные в основной ветке моего репозитория, в ветку разработки, используя git или TortoiseGit.Я знаю, что могу просто использовать git pull или merge , но это объединяет слишком много изменений сразу в ветке разработки и усложняет разрешение конфликтов.

Если бы я использовал SVN или TortoiseSVN, я мог бы просто объединять изменения из основного транка за раз, а не все сразу, используя диапазон ревизий для слияния.Могу ли я сделать что-то подобное с Git или TortoiseGit?То есть можно ли объединить ряд изменений в мою ветку разработки вместо того, чтобы объединить все изменения сразу?

Ответы [ 3 ]

6 голосов
/ 18 июля 2013

Сводка

Используя Git из командной строки, вы можете перебазировать feature на master:

git rebase master feature

# Now force push the changes to your remote
git push <remote> feature --force

Предупреждение : не делайте неосторожных принудительных изменений (переписанных) коммитов / ветвей push , если вы делитесь этими коммитами / ветками с другими людьми, потому что вы будете вызывать конфликты с любыми изменениями, основанными на более старых версиях коммитов/ветви.Можно работать таким образом в (очень) небольших командах, но требуется хорошая координация.

Вы также можете использовать флаг --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, потому что это вызвало слишком много конфликтов, которые должны были быть разрешены одновременно.Мне просто хотелось иметь возможность постепенно объединять изменения, чтобы я мог разрешать конфликты небольшими, более управляемыми частями.

Введите git rebase

Лучший способ сделать это -факт git rebase.Это на самом деле идеально.Все, что я сделал в feature и , сделает копии этих коммитов в том же порядке, за исключением того, что он рекомендует их поверх последней версии целевой ветви , которая в моем случае была master.

Это важно по двум причинам:

  1. Это эквивалентно объединению master в feature.Зачем?Поскольку feature в основном воссоздан поверх самых последних ревизий master ... все коммиты master теперь существуют в истории feature, включая коммиты в master, которые feature didn 'пока нет.

  2. Git повторно применяет коммиты по одному по порядку, поэтому, если возникают конфликты, онивведены в процесс несколькими небольшими, более управляемыми частями, по одной за раз, и это именно то, что я надеялся сделать!

Вот так это выглядит визуально (примеры адаптированы из официальная документация по Linux Kernel Git ):

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

В приведенном выше примере коммиты 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 в конечном итоге соберет мусор.

4 голосов
/ 21 февраля 2012

Да, вы можете сделать это. Допустим, ваш репозиторий выглядит так:

      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 к force, поскольку tmp уже существует). Например, если мы хотим перейти к c6 сейчас, используйте это:

 git branch -f tmp c6

Повторяйте это, пока все коммиты, которые вы хотите объединить, не будут в.

2 голосов
/ 21 февраля 2012

Примечание: ответ Джона правильный;это просто дальнейшее объяснение, основанное на последующих вопросах в комментариях - мне просто нужно было немного больше места:)

Мне нравится идея не создавать временную веткудля слияния в коммитах, но проблема с использованием git merge <commit-hash> заключается в том, что он сливается только в одной ревизии, а не в диапазоне (правильно?).Если бы у меня было 100 ревизий для объединения, не пришлось бы мне использовать эту команду 100 раз?

Нет, в Git история всегда связана.Поэтому, если вы объединяете коммит в мастер, и между общим предком master и этим коммитом больше коммитов, они полностью сохраняются.В отличие от SVN, Git хранит полные ссылки на предыдущие коммиты (с неограниченным количеством указателей, поэтому вы можете объединять несколько веток одновременно).Таким образом, в конце вы всегда увидите, где началась ветка, что с ней произошло, что было объединено между ними, и где она была объединена с основной веткой - только имя (или, скорее, label )ветви не сохраняются (за исключением текста автоматического слияния, если это считается ^^).

Так, например, ваша история может выглядеть следующим образом:

* -- A -- * ---------- * ----- * -- * -- M [master]
      \
       \
        B1 -- B2 -- B3 -- B4 -- B5 -- B6 -- B7 -- B8 -- B9 [br]

Предположим, вы хотитеобъединить B9 (коммит HEAD в ветке 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]

И снова, вы всегда можете оглянуться назад на все дерево, ивсе отдельные коммиты, которые были сделаны в ветви - даже если вы удалите ветку (которая снова просто удаляет указатель).

Так что, возможно, это объяснение также покажет вам, что вам не обязательно делатьсливается такими маленькими шагами.Вы никогда не потеряете информацию во время слияния, так как вы всегда можете оглянуться назад на все коммиты.

...