Подтвердить что ты не робот

Git выбор вишни против rebase

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

Просматривая Git book онлайн, я нашел следующее в разделе "Git Rebase":

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

(Цитируется из: http://git-scm.com/book/en/Git-Branching-Rebasing)

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

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

4b9b3361

Ответ 1

С тех пор, как 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.

Учитывая эту ситуацию,
a state of the repo before rebasing
Книга утверждает:

Однако есть и другой способ: вы можете взять патч изменения, который был введен в 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

"Подвох" здесь заключается в том, что в этом примере ветвь "эксперимент" (объект для перебазирования) была первоначально разветвлена от ветки "мастер", и, следовательно, она делит коммиты с C0 по C2 с ним &— по сути, "эксперимент" - это "мастер" вплоть до C2 плюс коммит C3 поверх него. (Это простейший возможный случай; конечно, "эксперимент" может содержать несколько десятков коммитов поверх своей первоначальной базы.)

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

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

Теперь вернемся к вашему вопросу. Как вы можете видеть, здесь технически git rebase действительно трансплантирует серию коммитов от "эксперимента" до вершины "мастера", так что вы можете справедливо сказать, что в этом процессе действительно есть "другая ветвь". Но суть в том, что коммит tip из "эксперимента" оказался новым коммитом tip в "эксперименте", он просто изменил свою базу:
state after merging

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

Ответ 2

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

Скажем, вы начали с:

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

Rebase:

$ git rebase master topic

Вы получаете:

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

Cherry-выбор:

$ 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

Черри-сборщик работает для отдельных совершений.

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

Ответ 4

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

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

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

Не рекомендуется заменять "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) в туловище, которые мы хотим включить в нашу ветку. Обычно мы просто переключаемся на ветку и выполняем "git мастер перезаписи". Но поскольку мы притворяемся, что у нас есть только 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 теперь уходит корнями из последней главной позиции. Готово!

Ответ 5

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

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

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

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

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

Ответ 6

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

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

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

Вы можете думать об этом как о разнице между копией и ходом.

Использование диаграмм, похожих на @Kenny Ho answer:

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

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

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

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

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

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

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

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

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


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

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