Git выбор вишни против rebase
Я недавно начал работать с Git.
Просматривая Git book онлайн, я нашел следующее в разделе "Git Rebase":
С помощью команды rebase вы можете принять все изменения, которые были зафиксировано в одной ветки и воспроизведет их в другой.
(Цитируется из: http://git-scm.com/book/en/Git-Branching-Rebasing)
Я подумал, что это точное определение git cherry-pick (повторно применить коммит или набор коммитов в текущей проверенной ветке).
Какая разница между двумя?
Ответы
Ответ 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
выглядит так:
- Запускает
git merge-base
, чтобы увидеть, какой последний коммит разделил и "эксперимент", и "мастер" (другими словами, какая точка отвлечения). Это С2.
- Сохраняет все коммиты, сделанные после точки отвода; в нашем игрушечном примере это просто C3.
- Перематывает ГОЛОВКУ (которая указывает на фиксацию кончика "эксперимента" до начала выполнения операции), чтобы указать на кончик "мастера" &— мы перебиваемся на него.
- Пытается применить каждый из сохраненных коммитов (как с
git apply
) по порядку. В нашем игрушечном примере это всего один коммит, C3. Допустим, его приложение произведет коммит C3 '.
- Если все прошло хорошо, ссылка "эксперимент" обновляется, чтобы указать на фиксацию, полученную в результате применения последней сохраненной фиксации (в нашем случае 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
Оба делают очень похожие вещи; основное концептуальное различие заключается (в упрощенном выражении) в том, что:
Вы можете думать об этом как о разнице между копией и ходом.
Использование диаграмм, похожих на @Kenny Ho answer:
Учитывая это начальное состояние:
E---F---G topic
/
A---B---C---D master
... и при условии, что вы хотите получить коммиты из ветки topic
, воспроизводимой поверх текущей ветки master
, у вас есть два варианта:
Использование rebase: Сначала вы перейдете к topic
, выполнив git checkout topic
, а затем переместите ветку, запустив git rebase master
, получив:
E'---F'---G' [topic]
/
A---B---C---D master
Результат: ваша текущая ветка topic
была перебазирована (перемещена) на master
.
Ветвь topic
была обновлена, а ветка master
осталась на месте.
Используя 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