Обратите внимание, что git rebase
имеет другую работу чем git merge
(с или без --ff-only
). То, что rebase
делает, это взять существующие коммиты и копия их. Предположим, например, что вы на branch1
и сделали два коммита A
и B
:
...-o--o--A--B <-- HEAD=branch1
\
o--C <-- branch2
и вы решили, что вы хотели бы иметь эти два коммита быть на branch2
вместо этого. Вы можете:
- получить список изменений, сделанных в
A
(дифф A
против своего родителя)
- получить список изменений, сделанных в
B
(дифф B
против A
)
- переключатель в
branch2
- внесите те же изменения, которые вы сделали в
A
, и скопируйте их, скопировав сообщение о фиксации с A
; назовем это commit A'
- , а затем внесите те же изменения, которые вы сделали в
B
, и скопируйте их, скопировав сообщение о фиксации с B
; назовем это B'
.
Есть команда git, которая выполняет эту команду diff-and-then-copy-and-commit для вас: git cherry-pick
.Итак:
git checkout branch2 # switch HEAD to branch2 (commit C)
git cherry-pick branch1^ # this copies A to A'
git cherry-pick branch1 # and this copies B
Теперь у вас есть это:
...-o--o--A--B <-- branch1
\
o--C--A'-B' <-- HEAD=branch2
Теперь вы можете вернуться к branch1
и удалить оригинал A
и B
, используя git reset
(я буду использовать --hard
здесь, это более удобно, что способ, как он очищает работу дерево тоже):
git checkout branch1
git reset --hard HEAD~2
Это удаляет исходный A
и B
, так что теперь у вас есть:
...-o--o <-- HEAD=branch1
\
o--C--A'-B' <-- branch2
Теперь вам просто нужно повторно проверить выход branch2
продолжать работать там.
Это то, что делает git rebase
: он «движется» совершает (хотя и не перемещает их, потому что он не может: в git коммит никогда не может быть изменен, поэтому даже простое изменение родительского идентификатора требует его копирования к новой и немного другой фиксации).
Другими словами, в то время как git cherry-pick
представляет собой автоматизированный дифф-и-переделку один фиксации, git rebase
представляет собой автоматизированный процесс переделывания несколько фиксаций, а также, в конце концов, перемещение этикетки вокруг «забыть» или скрывать оригиналы.
выше иллюстрирует перемещение совершающих от одного местного отделения branch1
к другой местной ветви branch2
, но мерзавец использует точно такой же процесс двигаться фиксациями, когда у вас есть пульт дистанционного слежения ветви, которая приобретает некоторые новые коммиты, когда вы делаете git fetch
(включая fetch
, что является первым шагом git pull
). Вы могли бы начать работать по отрасли feature
, который имеет вверх по течению origin/feature
, и сделать несколько фиксаций самостоятельно:
...-o <-- origin/feature
\
A--B <-- HEAD=feature
Но тогда вы решили, что должны увидеть, что случилось вверх по течению, так что вы запустите git fetch
, и, ах, кто-то выше по течению написало совершить C
:
...-o--C <-- origin/feature
\
A--B <-- HEAD=feature
На данный момент вы можете просто перебазировать свои feature
«S A
и B
на C
, давая:
...-o--C <-- origin/feature
\
A'-B' <-- HEAD=feature
Это копии оригинального A
и B
с оригиналами выбрасываются (но см сноску 1) после того, как копии являются полными.
Иногда нет ничего, что можно было бы переупаковать, т. Е. Никакой работы, которую вы сами делали.То есть график перед fetch
выглядеть следующим образом:
...-o <-- origin/feature
`-- HEAD=feature
Если вы затем git fetch
и совершить C
приходит, хотя, вы остаетесь с вашfeature
ветвь указывает на старое обязательство, в то время как origin/feature
имеет продвинулись вперед:
...-o--C <-- origin/feature
`---- <-- HEAD=feature
Это где git merge --ff-only
приходит: если вы попросите, чтобы объединить текущую ветвь feature
с origin/feature
, мерзавец видит, что можно просто слайд как бы стрелка вперед, так что feature
указывает непосредственно на фиксацию C
. Никакого фактического слияния не требуется.
Если вы имели свои собственные фиксаций A
и B
, хотя, и вы попросили объединить тех, с C
, мерзавец бы реальное слияние, делая новое слияние совершить M
:
...-o--C <-- origin/feature
\ `-_
A--B--M <-- feature
Здесь, --ff-only
будет остановить и дать вам ошибку. С другой стороны, Rebase может скопировать A
и B
в A'
и B'
, а затем спрятать оригинал A
и B
.
Итак, короче (хорошо, слишком поздно :-)), они просто делают разные вещи. Иногда результат один и тот же, а иногда и нет. Если все в порядке, чтобы скопировать A
и B
, вы можете использовать git rebase
; но если есть некоторая веская причина не, чтобы скопировать их, вы можете использовать git merge
, возможно, с --ff-only
, чтобы слить-или-сбой, если необходимо.
Git фактически хранит оригиналы в течение некоторого времени нормально месяц в этом случае, но она скрывает их. Самый простой способ найти их - это «reflogs» git, в котором хранятся истории того, где указана каждая ветка, и где HEAD
указывал перед каждым изменением, которое обновляло ветку и/или HEAD
.
В конце концов записи истории рефлогов истекают, и в этот момент эти фиксации имеют право на получение garbage collection.
Или, опять же, вы можете использовать git pull
, который является удобство скрипт, который начинается с запуска git fetch
. После того, как выбор сделан, сценарий удобства работает либо git merge
, либо git rebase
, в зависимости от того, как вы его настраиваете и запускаете.
Отличный ответ. Благодарю. – Ivin