Перебазирован на пульт и изменения отсутствуют;что случилось? - PullRequest
0 голосов
/ 26 ноября 2018

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

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

a --- b --- c --- e <-- local/dev

a --- b --- c --- d <-- remote/dev

Я подумал, что способ сделать это:

git fetch
git checkout dev
git rebase remote/dev

Я был довольноуверен, что я делал это в прошлом.Я ожидаю, что в результате получится:

a --- b --- c --- d --- e

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

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

Возможно, git pull --rebase - лучший вариант здесь.

1 Ответ

0 голосов
/ 27 ноября 2018

Должны быть и другие обстоятельства.(Я не догадываюсь, какими они могут быть.)

Возможно, git pull --rebase - лучший вариант здесь.

Это делает то же самое.Ваша исходная последовательность команд git checkout dev плюс пара команд git fetch; git rebase.git pull выполняет команду git fetch, за которой следует вторая команда Git, обычно git merge, но git pull --rebase выполняет git rebase секунду.Следовательно:

git checkout dev; git pull --rebase

- это то же самое, что и:

git checkout dev; git fetch; git rebase

Это всего на четыре символа короче (включая точки с запятой и пробелы).

Восстановление после неудачной перебазировки

Обратите внимание, что ваши исходные коммиты все еще доступны в вашем хранилище.Чтобы найти их, используйте один из этих двух механизмов:

  • ORIG_HEAD: это маркер, который задается несколькими различными командами перед внесением каких-либо изменений.Если последнее, что вы сделали, внесло изменения, было git rebase, то ORIG_HEAD будет соответствовать тому, что ребаз сохранял до внесения изменений.(Другие команды, которые его устанавливают: git am, git reset и - иногда - git merge, когда он выполняет операцию ускоренной перемотки вместо слияния.)

  • 1039 * reflog для HEAD.Это хранит несколько предыдущих значений HEAD.Каждый из них пронумерован и помечен временем.Срок действия старых записей истекает: Git гарантирует, что этого не произойдет, по крайней мере, до 30 или 90 дней по умолчанию.Немного сложно объяснить, что здесь происходит, без другой очень полезной части справочной информации.

    (Для справки см. Think Like (a) Git . Что действительно происходит, так это то, что достижимых записей журнала - достижимых из текущего значения ссылки, то есть - имеют срок действия 90 дней по умолчанию, в то время как недоступных -из-ссылки имеют срок действия 30 днейпо умолчанию. Оба настраиваемы, и специальная ссылка refs/stash имеет другое значение по умолчанию: по умолчанию записи в stash reflog никогда не истекают.)

За исключением фактов, что есть только один ORIG_HEAD, и что срок действия записей reflog истекает на time , а не перезаписывается следующим значением, хранящимся в ORIG_HEAD, эти две вещи работают примерно одинаково.

Чтобы увидетьфиксирует с ORIG_HEAD, используйте git log ORIG_HEAD (или то же самое с дополнительными опциями).Чтобы увидеть коммиты, которые есть в reflog, используйте git reflog show или git log -g (git reflog show фактически вызывает git log -g, чтобы вы могли передать дополнительные git log опции git reflog).

Пример

Давайте рассмотрим перебазирование, которое по какой-то причине пошло не так - чаще всего это перебазировка, требующая устранения слишком большого количества конфликтов слияния.Мы начнем с последовательности команд, которая запускает все это, но произнесите это следующим образом:

git checkout dev && git fetch && git rebase

Команда git checkout dev возвращает нас к ветви, которую мы хотели бы перебазировать.Шаг git fetch заполняет origin/dev, а команда git rebase запускает перебазирование, которое будет копировать коммиты, которые находятся на нашем dev, которых нет на нашем origin/dev.&& гарантирует, что каждая команда завершится успешно до запуска следующей команды - точка с запятой запустит следующую команду, даже если предыдущая не удалась.

Копии будут отправлены после фиксации, на которую указывает origin/dev,То есть после git fetch у нас может появиться следующий граф коммитов в нашем репозитории:

...--o--o--A--B--C   <-- dev (HEAD)
         \
          E--F   <-- origin/dev

Что мы хотели бы получить в итоге:

...--o--o--A--B--C   [abandoned]
         \
          E--F   <-- origin/dev
              \
               A'-B'-C'  <-- dev (HEAD)

где A' является нашей копией A, B' является нашей копией B, а C' является нашей копией C.

Если все работает - или, по крайней мере, если Git думает, что все работает - перебазирование заканчивается следующим образом:

...--o--o--A--B--C   <-- ORIG_HEAD, dev@{1}
         \
          E--F   <-- origin/dev
              \
               A'-B'-C'  <-- dev

Настройка ORIG_HEAD выполняется после завершения перебазировки.dev@{1} - это запись reflog для dev после завершения ребазирования.(Обратите внимание, что, как и другие команды, запись № 1 переносится на запись № 2, № 3 и т. Д., Поэтому вы должны проверить, с git reflog show или эквивалентным, что это число , теперь если это не сразу после завершения.)

Если вызакончил ребазинг и запустил git log или просмотр результата или запустил ваши тесты или что-то еще и в ужасе и хотите вернуть вещи, теперь вы можете запустить:

git reset --hard ORIG_HEAD

или:

git reset --hard dev@{1}

Оба из них:

  • найдут указанный коммит, который является фактическим хешем для коммита C;
  • сделает имя dev указанием наэтот коммит (нажатие на записи reflog вниз на одну в процессе);и
  • также переустановите индекс и рабочее дерево (из-за --hard), чтобы индекс и рабочее дерево теперь соответствовали commit C.

Обратите внимание, что git reset сделает ORIG_HEAD точку, которую HEAD сделал минуту назад.То есть теперь у нас будет:

...--o--o--A--B--C   <-- dev (HEAD), dev@{2}
         \
          E--F   <-- origin/dev
              \
               A'-B'-C'  <-- ORIG_HEAD, dev@{1}

Обычный git log, который начинается с HEAD и работает в обратном направлении, теперь покажет нам commit C, затем B, затем A, затем самый правый o и т. Д.

Предположим, с другой стороны, что мы начинаем перебазирование и достигли этой точки:

...--o--o--A--B--C   <-- dev
         \
          E--F   <-- origin/dev
              \
               A'-B'  <-- HEAD

Мы находимся в середине перебазирования, пытаемся сделать коммит C, чтобы сделать C', и столкнулись с множеством конфликтов.Мы смотрим на конфликты и решаем: В конце концов, не время это делать. Мы бы хотели вернуться к тому, как все было до того, как мы начали.

Команда git statusскажите нам, что мы находимся в режиме «отсоединенной головы», в середине перебазирования.Мы запускаем:

git rebase --abort

, что останавливает нашу перебазировку и повторную проверку dev для нас, давая нам это:

...--o--o--A--B--C   <-- dev (HEAD)
         \
          E--F   <-- origin/dev
              \
               A'  <-- HEAD@{2}
                \
                 B'  <-- HEAD@{1}

На этот раз я нарисовалHEAD записи reflog, которые запоминают коммиты A' и B'.Они всегда существуют - мы просто оставляем их вне графических рисунков большую часть времени, так как записи reflog обычно невидимы.Это верно и для ORIG_HEAD: мы оставляем это, когда нас это не волнует, потому что git log не смотрит на это, пока мы не обращаемся к нему явно.

Другой пример

Предположим, вы думаете, что закончили свою перебазировку, но либо вышли из нее (git rebase --quit, относительно новый вариант), либо на самом деле все еще находитесь в середине, с конфликтами.В этом случае вы должны сначала запустить git status, чтобы убедиться, что все так, как вы думаете:

git status

Если это говорит о том, что вы находитесь в серединеrebase, у вас есть возможность закончить rebase или прервать его, как в примере выше.

Если вы действительно сделали это, вы можете использовать git reflog, чтобы найти любые частичные выборки вишни, которые были выполнены успешно, и сделатьновая временная ветка, указывающая на это.Например, предположим, что мы успешно выполнили A' и B', как и раньше, увидели конфликты для C' и случайно завершили перебазирование с помощью git rebase --abort.Мы много работаем над разрешением конфликтов с A' и B' и хотим их вернуть.Теперь мы запускаем:

git reflog

, чтобы найти HEAD@{1}, HEAD@{2} и т. Д., Чтобы убедиться, что у нас действительно есть:

...--o--o--A--B--C   <-- dev (HEAD)
         \
          E--F   <-- origin/dev
              \
               A'  <-- HEAD@{2}
                \
                 B'  <-- HEAD@{1}

Поскольку B' является ценнымдавайте дадим ему новое имя ветви, например, new-dev:

git checkout -b new-dev HEAD@{1}

, и теперь у нас есть этот график, который мы будем рисовать без HEAD@{...} частей:

...--o--o--A--B--C   <-- dev
         \
          E--F   <-- origin/dev
              \
               A'-B'  <-- new-dev (HEAD)

и мы можем вернуться к работе в обычном режиме.В конце концов мы можем dev указать, чтобы совершить B' или новый C' или что-то еще, что мы выберем;но на данный момент, мы хорошо работаем над new-dev, сохраняя при этом dev.

Основные моменты, которые нужно запомнить

  • Коммиты в основном постоянны и полностью-unchangeable.Их настоящее имя - их большой уродливый идентификатор хэша, но людям невозможно запомнить и иметь с ними дело, поэтому мы даем им имена.Они держатся, пока они достижимы (см. Думай как (а) Git ).

  • Филиал Имена - это удобочитаемые идентификаторы, которые содержат хэш-идентификаторы. Мы выбираем имена; Git выбирает их значения (хэш-идентификаторы базовых коммитов).Значение текущего имени ветви обновляется автоматически всякий раз, когда мы делаем new commit.Каждое имя указывает на последний коммит, который Git должен показать, когда мы git log ветвь, и коммит, который Git должен проверить , когда мы git checkoutветвь.

  • Использование git checkout с именем ветки присоединяет имя HEAD к одному из имен веток, так что новые коммиты обновят это имя ветки.Используя git checkout с идентификатором хэша коммита или с именем, которое не является именем ветви, отсоединяет имя HEAD, заставляя его указывать непосредственно на некоторый коммит.

  • Используя git reset, мы можем переместить ветку current , чтобы она указала на любой коммит, или, если мы находимся в режиме отсоединенного HEAD, переместим отсоединенный HEAD (т. е. само имя HEAD), указывающее на любой коммит.Выполнение этого прервет любое продолжающееся слияние, выбор вишни или возврат.Это не (по крайней мере, в современном Git) не прекращает текущую перезагрузку.Git останется в режиме «отделяемого HEAD», и ваша ребаз фактически будет продолжаться.Использование git rebase --continue или git rebase --skip может отбросить много коммитов на этом этапе.

  • ORIG_HEAD похоже на дешевый (во всех смыслах) вариант reflogs: он запоминает один предыдущий коммит, из последней операции, которая много переместила ваш HEAD.

  • Реальные reflogs - есть один для HEAD плюс один для каждого имени ветви- сохранить многие предыдущие значения.Используйте git reflog show или git log -g, с именем (ями) ветви или именем HEAD, если хотите, чтобы показать журналы (и) этих ветвей или HEAD.

  • git rebase работает:

    1. Перечисление коммитов для копирования.
    2. Отключение HEAD, указывающее на коммит, после которого идут копии.
    3. Cherry-picking 1 каждый коммит, который нужно скопировать, по одному за раз.У каждой вишни могут быть конфликты;если это так, Git останавливается и заставляет вас разрешить конфликт.
    4. После завершения последнего выбора вишни перемещая имя ветви, чтобы указать на последний скопированный коммит.Это повторно присоединяет HEAD и устанавливает ORIG_HEAD к предыдущему значению имени ветви, которое также теперь находится в reflog ветви.
  • git pull делаетне делать ничего особенного.Это должно быть удобное сокращение.Я рекомендую избегать этого, но если вы действительно уверены, что собираетесь запустить git merge или git rebase сразу после git fetch, это то, что он сделает для вас: запустите git fetch, затем выполните вторую команду Git.


1 git rebase --interactive буквально бежит git cherry-pick;в современном Git оба встроены в то, что Git внутренне называет секвенсором .Некоторые другие режимы перебазировки также буквально используют вишнюПо умолчанию для неинтерактивного перебазирования фактически используется другой путь, с использованием git format-patch и git am для копирования фиксаций.В некотором смысле этот путь немного дефектен, так как он не обрабатывает переименования, а также метод, основанный на вишне.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...