Git cherry pick vs rebase

120

Недавно я начал работать с Git.

Переходя к книге Гита онлайн я нашел следующее в разделе «Git перебазироваться»:

С помощью команды rebase вы можете взять все изменения, которые были зафиксированы в одной ветке, и воспроизвести их в другой.

(Цитата по: http://git-scm.com/book/en/Git-Branching-Rebasing )

Я думал, что это точное определение git cherry-pick (повторно применить фиксацию или набор объектов фиксации в текущей проверенной ветке).

Какая разница между двумя ?

лизергиновой кислоты
источник

Ответы:

166

Со временем, когда git cherry-pickнаучились применять несколько коммитов, различие действительно стало несколько спорным, но это можно назвать конвергентной эволюцией ;-)

Настоящее различие заключается в первоначальном намерении создать оба инструмента:

  • git rebaseЗадача состоит в том, чтобы перенаправить серию изменений, которые разработчик имеет в своем частном репозитории, созданных для версии X некоторой восходящей ветки, на версию Y той же ветки (Y> X). Это эффективно меняет базу этой серии коммитов, отсюда и "перебазирование".

    (Это также позволяет разработчику перенести серию коммитов на любую произвольную фиксацию, но это менее очевидное применение.)

  • git cherry-pickпредназначен для переноса интересного коммита с одной линии разработки на другую. Классический пример - перенос исправления безопасности, сделанного в нестабильной ветке разработки, в стабильную (обслуживаемую) ветку, где mergeнет смысла, так как это приведет к большому количеству нежелательных изменений.

    С момента своего первого появления git cherry-pickон может выбирать сразу несколько коммитов, один за другим.

Следовательно, возможно, наиболее разительное различие между этими двумя командами заключается в том, как они обрабатывают ветку, над которой работают: git cherry-pickобычно приносит фиксацию откуда-то еще и применяет ее поверх вашей текущей ветки, записывая новую фиксацию, в то время как git rebaseберет текущую ветку и перезаписывает серия его собственных советов так или иначе фиксируется. Да, это сильно упрощенное описание того, что git rebaseможно делать, но оно сделано намеренно, чтобы попытаться понять общую идею.

Обновите, чтобы подробнее объяснить git rebaseобсуждаемый пример использования .

Учитывая эту ситуацию,
состояние репо до перебазирования
Книга утверждает:

Однако есть другой способ: вы можете взять патч изменения, который был внесен в C3, и повторно применить его поверх C4. В Git это называется перебазированием. С помощью команды rebase вы можете взять все изменения, которые были зафиксированы в одной ветке, и применить их к другой.

В этом примере вы запустите следующее:

$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command

«Загвоздка» здесь в том, что в этом примере ветвь «эксперимент» (подлежащая перебазированию) изначально была ответвлена ​​от ветки «master», и, следовательно, она разделяет коммиты от C0 до C2 - фактически, «эксперимент» - это master "до C2 включительно плюс коммит C3 поверх него. (Это простейший случай; конечно, «эксперимент» может содержать несколько десятков коммитов поверх своей исходной базы.)

Теперь git rebaseпредлагается переназначить «эксперимент» на текущую вершину «мастера», и он git rebaseвыглядит следующим образом:

  1. Запускается, git merge-baseчтобы увидеть, какая последняя фиксация используется как «экспериментальным», так и «главным» (другими словами, в чем смысл отклонения). Это C2.
  2. Сохраняет все коммиты, сделанные с момента переадресации; в нашем примере с игрушкой это просто C3.
  3. Перематывает ГОЛОВУ (которая указывает на конец фиксации «эксперимента» перед запуском операции), чтобы указать на вершину «мастера» - мы перемещаемся на него.
  4. Пытается применить каждый из сохраненных коммитов (как будто с git apply) по порядку. В нашем примере с игрушкой это всего лишь одна фиксация, C3. Допустим, его приложение произведет фиксацию C3 '.
  5. Если все прошло успешно, ссылка на «эксперимент» обновляется и указывает на фиксацию, полученную в результате применения последней сохраненной фиксации (в нашем случае C3 ').

А теперь вернемся к вашему вопросу. Как видите, здесь технически git rebase действительно переносится серия коммитов от «эксперимента» до «вершины», так что вы можете с полным правом сказать, что действительно есть «другая ветвь» в процессе. Но суть в том, что фиксация подсказки из «эксперимента» оказалась новой фиксацией подсказки в «эксперименте», она просто изменила свою базу:
состояние после слияния

Опять же, технически вы можете сказать, что git rebaseздесь включены определенные коммиты от «мастера», и это абсолютно правильно.

kostix
источник
2
Спасибо. Я все еще не совсем понял, что вы здесь имеете в виду. В книге приведен пример того, что rebase применяет серию советов из другой ветки, а вы говорите, что это из «той же ветки». Или, может быть, есть несколько примеров того, как это работает?
лизергиновая кислота
1
Пытался объяснить вопрос, обновив свой ответ.
kostix 07
98

С помощью Cherry-Pick исходные коммиты / ветки остаются и создаются новые. При использовании rebase вся ветка перемещается так, чтобы ветка указывала на воспроизведенные коммиты.

Допустим, вы начали с:

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

Rebase:

$ git rebase master topic

Ты получаешь:

              A'--B'--C' topic
             /
D---E---F---G master

Вишневый выбрать:

$ git checkout master -b topic_new
$ git cherry-pick A^..C

Ты получаешь:

      A---B---C topic
     /
D---E---F---G master
             \
              A'--B'--C' topic_new

больше информации о git содержится в этой книге (http://git-scm.com/book)

Кенни Хо
источник
3
Хорошо ответил. Также часто вы можете выбрать только коммиты A и B, но оставить C висеть в тех случаях, когда вы можете оставить ветку и просто выбрать изменения, которые могут потребоваться коллегам. Git создан для работы с людьми, поэтому, если вы не видите преимуществ чего-то при работе в одиночку, его чаще используют при работе в больших группах.
Пабло Джомер
Если бы вместо этого было выполнено интерактивное перебазирование, исключая один или несколько коммитов, какие ветки у вас были бы в конце? если бы он был только topicперебазирован поверх master, он не содержал оставшихся коммитов, так в какой ветке они будут?
Энтони
Еще кое-что хочу добавить: если вы git checkout topicа затем git reset --hard C'после сбора вишен, то вы получите тот же результат, что и после ребазирования. Я спас себя от множества конфликтов слияния, используя выбор вишни вместо ребазирования, потому что общий предок был давно.
sorrymissjackson
@anthony - stackoverflow.com/questions/11835948/… : насколько я понимаю, они потеряны. Я не git-guru , но это rebase/ cherry-pickна всех деталях с , gitчто у меня было понимание проблемы.
thoni56 03
1
Ваши графики приносят больше вреда, чем пользы, потому что они функционально идентичны. Единственное отличие - это ветвь, созданная пользователем git checkout -b, не имеющая к этому никакого отношения git cherry-pick. Лучший способ объяснить , что вы пытаетесь сказать , будет «вы бежите git rebaseна topicветви и передать его master; вы бежите git cherry-pickна masterветви и передать его (коммиты) topic«.
Рори О'Кейн
14

Сбор вишни для индивидуальных коммитов .

Когда вы выполняете ребазирование, он применяет все коммиты в истории к HEAD ветки, которые там отсутствуют.

iltempo
источник
Спасибо. Вы знаете, работают ли они под укрытием одинаково? (сохраните свои промежуточные выходные данные в файлах «патчей» и т. д.).
лизергиновая кислота
Афаик да. Он применяет все патчи один за другим. Это причина, по которой вам иногда приходится разрешать конфликты слияния в середине перебазирования, прежде чем продолжить.
iltempo 06
6
@iltempo, он работал для отдельных коммитов только в старых версиях Git; в настоящее время вы можете сделать что-то вроде git cherry-pick foo~3..fooи получить коммиты вершины дерева из "foo", выбранные один за другим.
kostix 06
1
git-rebase использует тот же API, что и выбор вишни в кодовой базе, iirc
альтернатива
Я не думаю, что они действительно работают одинаково под прикрытием. Я пробовал перебазировать тысячи коммитов и думаю, что git создает огромный файл почтового ящика, а затем запускает git amего. В то время как вишневый выбор применяет фиксацию путем фиксации (возможно, путем создания почтового ящика с одним сообщением для каждого патча). Моя перебазировка не удалась, потому что создаваемому файлу почтового ящика не хватило места на диске, но выбор вишни с тем же диапазоном ревизий завершился успешно (и, похоже, работает быстрее).
onlynone
11

Короткий ответ:

  • git cherry-pick более "низкий уровень"
  • Таким образом, он может эмулировать git rebase.

Приведенные выше ответы хороши, я просто хотел привести пример, чтобы продемонстрировать их взаимосвязь.

Не рекомендуется заменять «git rebase» такой последовательностью действий, это просто «проверка концепции», которая, я надеюсь, помогает понять, как все работает.

Учитывая следующий репозиторий игрушек:

$ git log --graph --decorate --all --oneline
* 558be99 (test_branch_1) Test commit #7
* 21883bb Test commit #6
| * 7254931 (HEAD -> master) Test commit #5
| * 79fd6cb Test commit #4
| * 48c9b78 Test commit #3
| * da8a50f Test commit #2
|/
* f2fa606 Test commit #1

Скажем, у нас есть некоторые очень важные изменения (коммиты с №2 по №5) в мастере, которые мы хотим включить в наш test_branch_1. Обычно мы просто переключаемся на ветку и выполняем «git rebase master». Но поскольку мы делаем вид, что у нас есть только «git cherry-pick», мы делаем:

$ git checkout 7254931                # Switch to master (7254931 <-- master <-- HEAD)
$ git cherry-pick 21883bb^..558be99   # Apply a range of commits (first commit is included, hence "^")    

После всех этих операций наш график фиксации будет выглядеть так:

* dd0d3b4 (HEAD) Test commit #7
* 8ccc132 Test commit #6
* 7254931 (master) Test commit #5
* 79fd6cb Test commit #4
* 48c9b78 Test commit #3
* da8a50f Test commit #2
| * 558be99 (test_branch_1) Test commit #7
| * 21883bb Test commit #6
|/
* f2fa606 Test commit #1

Как мы видим, коммиты №6 и №7 были применены против 7254931 (фиксация подсказки мастера). HEAD был перемещен и указывает на фиксацию, которая, по сути, является вершиной перебазированной ветки. Теперь все, что нам нужно сделать, это удалить старый указатель ветки и создать новый:

$ git branch -D test_branch_1
$ git checkout -b test_branch_1 dd0d3b4

test_branch_1 теперь основан на последней позиции мастера. Готово!

raiks
источник
Но rebase также может имитировать git cherry-pick?
Number945 02
Поскольку cherry-pickможет применять ряд коммитов, думаю, да. Хотя это немного странный способ делать что-то, ничто не мешает вам выбрать все коммиты в своей функциональной ветке поверх master, а затем удалить функциональную ветку и воссоздать ее так, чтобы она указывала на кончик master. Вы можете думать об этом git rebaseкак о последовательности git cherry-pick feature_branch, git branch -d feature_branchи git branch feature_branch master.
raiks
7

Это обе команды для перезаписи коммитов одной ветки поверх другой: разница в том, какая ветвь - «ваша» (текущая извлеченная HEAD) или «их» (ветвь, переданная в качестве аргумента команде) - это база для этого переписывания.

git rebaseберет начальную фиксацию и воспроизводит ваши коммиты как следующие за их (начальная фиксация).

git cherry-pickберет набор коммитов и воспроизводит их коммиты как следующие после ваших (ваших HEAD).

Другими слова, две команды, в своем основном поведении (игнорируя их расходящиеся характеристики, соглашение о вызовах, а также возможности улучшения), симметрично : проверить ветви barи работают git rebase fooсеты, barветви одной и ту же историю, проверяя ветви fooи работает git cherry-pick ..barустановим fooдо (изменения с foo, за которыми следуют изменения с bar).

Что касается именования, разницу между двумя командами можно запомнить в том, что каждая из них описывает то, что она делает с текущей веткой: rebaseделает другую голову новой базой для ваших изменений, тогда как cherry-pickвыбирает изменения из другой ветки и помещает их поверх вашHEAD (как вишня на мороженом).

Стюарт П. Бентли
источник
1
Я не мог понять ни одного ответа, кроме твоего! Он лаконичен и имеет смысл, без лишних слов.
neoxic
4

Оба делают очень похожие вещи; основное концептуальное отличие (упрощенно) заключается в том, что:

  • rebase перемещает коммиты из текущей ветки в другую .

  • cherry-pick копирует коммиты из другой ветки в текущую .

Используя диаграммы, подобные ответу @Kenny Ho :

Учитывая это начальное состояние:

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

... и предполагая, что вы хотите, чтобы коммиты из topicветки воспроизводились поверх текущей masterветки, у вас есть два варианта:

  1. Использование перебазирования: сначала нужно topicвыполнить git checkout topic, а затем переместить ветку, выполнив команду git rebase master:

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

    Результат: ваша текущая ветка topicбыла перебазирована (перемещена) наmaster . Филиал был обновлен, в то время как отрасль осталась на месте.
    topicmaster

  2. Используя вишневый выбор : сначала нужно masterвыполнить git checkout master, а затем скопировать ветку, запустивgit cherry-pick topic~3..topic (или, что то же самое, git cherry-pick B..G), создав:

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

    Результат: коммиты из topicбыли скопированы в master.
    masterФилиал был обновлен, в то время как topicотрасль осталась на месте.


Конечно, здесь нужно было явно указать cherry-pick выбрать последовательность коммитов , используя обозначение диапазона foo..bar . Если бы вы просто передали имя ветки, как в git cherry-pick topic, он бы принял только фиксацию на конце ветки, что привело бы к:

A---B---C---D---G' master
     \
      E---F---G topic
waldyrious
источник