Должны быть и другие обстоятельства.(Я не догадываюсь, какими они могут быть.)
Возможно, 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
работает:
- Перечисление коммитов для копирования.
- Отключение HEAD, указывающее на коммит, после которого идут копии.
- Cherry-picking 1 каждый коммит, который нужно скопировать, по одному за раз.У каждой вишни могут быть конфликты;если это так, Git останавливается и заставляет вас разрешить конфликт.
- После завершения последнего выбора вишни перемещая имя ветви, чтобы указать на последний скопированный коммит.Это повторно присоединяет
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
для копирования фиксаций.В некотором смысле этот путь немного дефектен, так как он не обрабатывает переименования, а также метод, основанный на вишне.