2015-01-25 1 views
18

Из того, что я читал, они помогают нам получить линейную историю.Есть ли разница между git rebase и git merge -ff-only

Из того, что я экспериментировал, rebase работает все время. Но merge -ff-only работает только в сценариях, где его можно быстро перенаправить.

Я также заметил, что git merge создает слияние, но если мы используем -ff-only, он дает линейную историю, которая по существу равна git rebasing. Так что -ff-only убивает цель слияния git, не так ли?

Так в чем же разница между ними?

ответ

41

Обратите внимание, что 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, в зависимости от того, как вы его настраиваете и запускаете.

+3

Отличный ответ. Благодарю. – Ivin

3

Да, есть разница. git merge --ff-only прервется, если он не может выполнить ускоренную перемотку вперед, и берет фиксацию (обычно ветвь) для слияния. Он будет создавать коммит слияния, если он не может выполнить перемотку вперед (т. Е. Никогда не будет делать этого с --ff-only).

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

+0

Скажем, --ff-only проверяет и не может слиться, если это невозможно без перезаписи истории. В то время как rebase делает это, переписывая историю. Это? – Ivin

+1

Не совсем. Слияния не переписывают историю, и поэтому 'git merge' никогда не перезаписывает историю (с или без' --ff-only'). '--ff-only' предотвращает любое слияние, отличное от быстрого слияния (без перезаписи истории перезаписи). Слияния, отличные от сглаживания перемотки вперед, в общем случае просто применяют изменения без изменений (это упрощение, но достаточно хорошее, чтобы идти), плюс запись метаданных, которые сменили набор изменений; нет перезаписи истории. – abligh

2

Да, --ff-only всегда терпит неудачу, когда ошибка git merge завершится неудачно, и может завершиться неудачно, когда удастся достичь git merge. В этом-то и дело - если вы пытаетесь сохранить линейную историю, и слияние не может быть сделано таким образом, вы должны хотите это сбой.

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

+0

Так оно и есть, --ff-only используется для проверки сценария и создания линейной истории, если это возможно; тогда как rebase будет больше похож, создайте линейную историю независимо от сценария. Это? – Ivin

+2

Да, и если вы переустанавливаете что-то, что было недостаточно чистым, чтобы «merge -ff-only», вы удаляете фиксации с одного места в дереве истории и вставляете их (с изменениями) в другое место. Это называется «переписыванием истории», и это нормально, если вы находитесь в неопубликованной ветке, но после того, как вы опубликовали свою ветку, а другие люди клонировали ее, переписывание истории вызывает проблемы для них. –

Смежные вопросы