2016-02-10 3 views
6

Согласно моему пониманию git pull --rebase origin master, оно должно быть эквивалентно, выполнив следующие команды:Понимания «мерзавец тянуть --rebase» против «мерзавца перебазироваться»

(from branch master): $ git fetch origin 
(from branch master): $ git rebase origin/master 

Я, кажется, нашел случай, когда это не работает должным образом. В моем рабочем месте, у меня есть следующие настройки:

  • филиал origin/master ссылки филиал master на удаленном origin
  • ветви master настроенная для отслеживания origin/master и является позади мастеров нескольких фиксаций.
  • филиал feature размещено для отслеживания филиалов master и из master по нескольким сообщениям.

Иногда я потеряю фиксации, выполнив следующую последовательность шагов

(from branch master): $ git pull --rebase 
(from branch master): $ git checkout feature 
(from branch feature): $ git pull --rebase 

На данный момент, несколько совершает вперед, я был на feature теперь был потерян. Теперь, если я сбросить свою позицию, и вместо того, чтобы сделать следующее:

(from branch feature): $ git reset --hard [email protected]{2} # rewind to before second git pull 
(from branch feature): $ git rebase master 

коммиты были применены правильно, и мои новые коммиты на feature все еще присутствуют. Кажется, это прямо противоречит моему пониманию того, как работает git pull, если только git fetch . не делает что-то странное, чем я ожидал.

К сожалению, это не является 100% воспроизводимым для всех коммитов. Однако, когда он работает для фиксации, он работает каждый раз.

Примечание: Здесь я должен быть прочитан --rebase=preserve, если это имеет значение. У меня есть следующие в моей ~/.gitconfig:

[pull] 
    rebase = preserve 
+0

Вы не должны перегружать ветвь удаленного отслеживания 'origin/master', а вы пытаетесь перенести это вперед, не затрагивая вашу (отслеживающую) копию этого пульта. Я не проверял руководства для правильных вызовов, но, возможно, check -b, которые связаны с новым временным именем, и переустановка временной ветви на ваш текущий HEAD. –

+1

Я думаю, здесь есть какая-то путаница. Я не перезаряжаю 'origin/master', но переустанавливаю текущую ветку' master' на 'origin/master'. Это должно, по моему мнению, по существу довести 'HEAD' до конца' origin/master' и повторно использовать фиксации, которые были на кончике 'master' обратно в ветку. По существу, переписывать новые коммиты на 'master', как если бы они произошли после изменений из' origin/master'. – ashays

+0

Какая у вас (локальная) версия git? Я спрашиваю, потому что я помню (несколько неопределенно и еще не вернулся к проверке), что в нескольких версиях 2.x была автоматизированная ошибка перебора басов в «git pull», и, зная, что версия может сделать проверку на это немного проще. – torek

ответ

8

(Edit, 30 ноября 2016: Смотри также this answer к Why is git rebase discarding my commits? Теперь уже практически уверен, что это связано с опцией вилочного точки.).

Там - несколько отличий между руководством и pull - git rebase (меньше в 2.7, чем в версиях git, предшествующих --fork-point в git merge-base). И, я подозреваю, что ваши автоматические сбережения-слияния могут быть задействованы. Это немного сложно убедиться, но тот факт, что ваш локальный филиал следует за вашим другим местным филиалом, который получает пересылку, весьма наводящий на размышления. Тем временем старый сценарий git pull был также переписан в C недавно, поэтому сложнее увидеть, что он делает (хотя вы можете установить переменную окружения GIT_TRACE на 1, чтобы git показывал вам команды, когда они запускают их внутри).

В любом случае, есть два или три ключевых пунктов здесь (в зависимости от того, как вы считаете, и разделить их, я сделаю это в 3):

  • git pull работает git fetch, то либо git merge или git rebase в соответствии с инструкциями, но когда он запускается git rebase, он использует новый механизм fork-point для «восстановления с восходящей ребазы».

  • Когда git rebase запущен без аргументов, у него есть специальный футляр, который вызывает машины для токарной обработки. При запуске с аргументами механизм fork-point отключается, если явно не запрашивается с --fork-point.

  • Когда git rebase поручено сохранять слияния, он использует интерактивный код переустановки (не интерактивно). Я не уверен, что это действительно имеет значение здесь (отсюда «может быть задействовано» выше). Обычно он сглаживает слияния, и только для интерактивного скрипта сводки есть код для их сохранения (этот код действительно повторяет слияния, поскольку нет другого способа справиться с ними).

Самый важный пункт здесь (обязательно) - код точки вилки. Этот код использует reflog для обработки случаев, наилучшим образом отображаемых путем рисования части графика фиксации.

В нормальном (без точки вил материала необходим) перебазироваться случае у вас есть что-то вроде этого:

... - A - B - C - D - E <-- origin/foo 
      \ 
       I - J - K <-- foo 

где A и B являются совершившим вас, когда вы начали свой филиал (так что B является merge- основание), через E - это новые коммиты, которые вы выбрали с пульта дистанционного управления через git fetch, а I через K - ваши собственные коммиты. Коды пересчета копируются I по номеру K, прилагается первый экземпляр до E, второй экземпляр-номер I, а третий - копия J.

Гит выяснит или раньше, anyway- который совершающего скопировать с помощью git rev-list origin/foo..foo, то есть, используя имя текущей ветви (foo), чтобы найти K и работать в обратном направлении, и имя его вверх по течению (origin/foo), чтобы найти E и работать в обратном направлении. Назад марш останавливается на базе слияния, в этом случае B и скопированный результат выглядит следующим образом:

... - A - B - C - D - E <-- origin/foo 
      \   \ 
      \    I' - J' - K' <-- foo 
      \ 
       I - J - K [[email protected]{1}: reflog for foo] 

Проблема с этим методом возникает, когда origin/foo здесь расположенные в верхнем-сама нормированный. Предположим, например, что на origin кто-то силой нажал так, что B был заменен новой копией B' с другой формулировкой фиксации (и, возможно, другим деревом, но, мы надеемся, ничего не скажется на нашем I -through-K).Отправная точка теперь выглядит следующим образом:

  B' - C - D - E <-- origin/foo 
     /
... - A - B <-- [origin/[email protected]{n}] 
      \ 
       I - J - K <-- foo 

Используя git rev-list origin/foo..foo, мы бы выбрать фиксации B, I, J и K быть скопированы, и попытаться вставить их после E, как обычно; но мы не хотим, чтобы скопировать B так как он действительно исходил от origin и был заменен на свою собственную копию B'.

Что представляет собой код точки прокрутки, это посмотреть на reflog для origin, чтобы узнать, удалось ли добраться до B. То есть, он проверяет не только origin/master (поиск E и сканирование обратно в B' и затем A), но и origin/[email protected]{1} (указывая непосредственно B, вероятно, в зависимости от того, как часто вы запускаете git fetch), origin/[email protected]{2}, и так далее. Любые фиксации на foo, которые достижимы от любыеorigin/[email protected]{n} включены для рассмотрения при поиске узла Lowest Common Ancestor на графике (т. Е. Все они рассматриваются как опции, чтобы стать базой слияния, которую выдает git merge-base).

(Здесь стоит отметить такой дефект: это автоматическое обнаружение точек разметки может обнаруживать только те коммиты, которые были доступны в течение времени, в течение которого сохраняется запись reflog, которая в этом случае по умолчанию составляет 30 дней. Однако это не особенно отношение к вашему вопросу)


в вашем случае, у вас есть три имена ветвей (и, следовательно, три reflogs) участвуют:.

  • origin/master, который обновляется git fetch (первый шаг ваш git pull в то время как филиал master)
  • master, которая обновляется с помощью и вы (с помощью обычных фиксаций) и git rebase (на втором этапе вашего git pull) и
  • feature, который обновляется обоими вы (через обычные коммиты) и git rebase (второй шаг вашего второйgit pull: вы «забираете» себя, нет-op, затем переустанавливаете feature на master).

Оба rebases выполняются с --preserve-merges (и, следовательно, не взаимодействующим интерактивным режимом) и --onto new-tipfork-point, где fork-point фиксацией ID найдено, запустив git merge-base --fork-point upstream-name HEAD. upstream-name для первого Rebase является origin/master (ну, refs/remotes/origin/master) и upstream-name для второго Rebase является master (refs/heads/master).

Это должно быть все Just Work.Если ваша фиксация графика в начале всего процесса что-то вроде того, что вы описали:

... - A - B <-- master, origin/master 
      \ 
       I - J - K <-- feature 

тогда первый fetch приносит в некоторых фиксаций и делает origin/master точку на новый наконечник:

   C - D - E <-- origin/master 
      /
... - A - B <-- master, origin/[email protected]{1} 
      \ 
       I - J - K <-- feature 

и первый Rebase то ничего не находит для копирования (слияние-основание master и B - B = вилка-точка (мастер, происхождения/мастер) -это просто B так что нет ничего, чтобы скопировать), что дает:

   C - D - E <-- master, origin/master 
      /
... - A - B <-- [email protected]{1}, origin/[email protected]{1} 
      \ 
       I - J - K <-- feature 

Вторая выборка - от вас самих и без-op/пропущена полностью, оставив это как вход во вторую перезагрузку. --onto цели master который совершает E и вилку-точку HEAD (feature) и master также совершают B, оставляя фиксации I через K скопировать после E, как обычно.

Если какой-либо фиксатор (ы) отбрасывается, в этом процессе что-то происходит не так, но я не вижу, что.

+2

Ничего себе. Это потрясающий ответ и дает много понимания того, что здесь происходит. Я собираюсь потратить некоторое время, пытаясь воссоздать обстоятельства, которые привели к этой проблеме, и посмотреть, дает ли этот ответ некоторый свет на то, что происходит. Я подозреваю, что это приведет меня прямо к нему, но я бы хотел найти точную причину. Я дам вам знать, найду ли что-нибудь. – ashays

+0

Потратив немного времени на то, что я не смог полностью восстановить прецедент (но я снова нашел его снова). Это определенно кажется проблемой! Спасибо, что потратили время на этот отличный ответ. Это определенно добавило мое понимание git. – ashays

+0

@ashays В чем заключалась основная причина, по которой вы теряете фиксации? – mschuett

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