Когда вы запускаете git rebase
(интерактивный или нет), git в основном выполняет серию cherry-pick
операций, чтобы скопировать исходную цепочку коммитов в новую цепочку. Давайте используем o
для исходных коммитов и нарисуем фрагмент графа коммитов для ветки branch
, отходящей от ветки main
:
o1 - o2 - o3 - o4 <-- branch
/
..- * - x <-- main
Теперь вы можете запустить git rebase
, чтобы скопировать все старые коммиты o
в новые коммиты n
, но на основе x
, вершины main
, а не на основе *
, старой базовой точки слияния. Чтобы сделать это еще более похожим на то, что произошло, давайте «случайно» опустим одно:
o1 - o2 - o3 - o4 <-- ???
/
..- * - x <-- main
\
n1 - n3 - n4 <-- branch
Метка ???
выше представляет ссылку git (имя ветки, имя тега или любую другую подходящую метку), которая указывает или указывала на коммит o4
. Все ваши старые коммиты все еще там, если на них указывает имя. Если имени нет, они останутся, пока git gc
не очистит их (но вы не хотите, чтобы это произошло, поэтому не запускайте git gc
:-) ).
Таким образом, возникает важный вопрос: «Какое имя или имена мы можем (и git) использовать, чтобы найти o4
?» Оказывается, их как минимум два:
- один или несколько в «reflog» и
- один пишется
ORIG_HEAD
.
ORIG_HEAD
проще всего использовать, но это имя также используется другими командами (например, git merge
), поэтому вам нужно проверить, правильно ли оно по-прежнему:
$ git log ORIG_HEAD
Если это дает вам правильную цепочку, дайте себе более постоянное имя, указывающее на коммит o4
. Это может быть имя ветки (таким образом вы «воскресите» старую ветку под новым именем), или имя тега, или любое другое имя, но ветка и тег — самые простые:
$ git branch zombie ORIG_HEAD
(Вы не обязаны делать это, и когда вы освоитесь с git, вы можете пропустить этот шаг, но, вероятно, лучше сделать до тех пор.)
Что, если ORIG_HEAD
был взломан (например, другой перебазировкой, слиянием или чем-то еще)? Ну, тогда есть рефлоги.
Существует один журнал ссылок для HEAD
и по умолчанию другой журнал ссылок для каждого имени ветки. В этом случае следует использовать reflog для branch
:
$ git reflog branch
$ git log -g branch
но вы можете просто использовать git reflog
, чтобы показать вариант для HEAD
(это более шумный, поэтому может быть лучше посмотреть на вариант только для branch
):
$ git reflog
$ git log -g
Где-то во всем этом выводе вы сможете найти коммит o4
. Вы можете найти много других коммитов, похожих на o4
, поэтому git log -g
может быть полезным, поскольку он позволит вам найти настоящий (или правильный) o4
.
В любом случае, если вы в конечном итоге придумаете «относительное имя» в стиле журнала ссылок (например, branch@{1}
или branch@{yesterday}
), вы можете найти необработанный SHA-1 или использовать это относительное имя, чтобы еще раз воскресить зомби-версию branch
:
$ git branch zombie branch@{yesterday}
or:
$ git branch zombie feedd0gf00d
или что-то еще.
Все, что это делает, это дает вам имя, zombie
, где на рисунке графика было три вопросительных знака. Вам все еще нужно использовать это, чтобы найти удаленный коммит, в данном случае коммит o2
. Вы можете найти его с помощью необработанного SHA-1 (прочитав git log
) и перебазировать и извлечь его, или выбрать его, чтобы добавить копию в n4
, или что-то еще.
Если все, что вы хотите сделать, это установить branch
обратно, чтобы зафиксировать o4
, вы даже можете полностью отказаться от ветки зомби и просто выполнить git reset --hard
, находясь на ветке branch
:
$ git checkout branch # if needed
$ git reset --hard feedd0gf00d
or:
$ git reset --hard ORIG_HEAD
Обратите внимание, что после reset --hard
указывается просто любой идентификатор фиксации. --hard
заставляет reset
стереть ваше рабочее дерево и заменить его целевым коммитом, в то время как само действие reset
сообщает git: "сделайте текущую ветку точкой на идентификатор коммита, который я собираюсь вам дать, независимо от того, какая ветка- подсказка - зафиксируйте его имена прямо сейчас».
Другими словами, после того, как ваш git rebase
завершится и вы обнаружите, что пропустили o2
при создании цепочки n1 - n3 - n4
, если вы сразу git reset --hard ORIG_HEAD
, git изменит это:1
o1 - o2 - o3 - o4 <-- ORIG_HEAD
/
..- * - x <-- main
\
n1 - n3 - n4 <-- HEAD=branch
к этому:
o1 - o2 - o3 - o4 <-- ORIG_HEAD, HEAD=branch
/
..- * - x <-- main
\
n1 - n3 - n4 [abandoned]
Конечно, цепочка [abandoned]
из n
коммитов все еще находится в репозитории: в журналах ссылок есть имя, указывающее на n4
!
(Записи reflog в конечном счете истекают — по умолчанию через 30–90 дней, в зависимости от деталей, которые еще не представляют интереса, — и когда срок их действия истечет, и не будет имени, по которому можно найти n4
или o4
или что-то еще, < em>затем git gc
очистит и удалит их.)
1Обратите внимание, что я добавил к этому графику обозначение HEAD=
, чтобы указать, на какой ветви вы находитесь. Этот материал HEAD=
на самом деле является довольно хорошим приближением к тому, как git отслеживает, на какой ветке вы находитесь. В каталоге .git
есть файл с именем HEAD
, и этот файл просто содержит название текущей ветки!2 Если вы напишете в файле новое имя, git изменит представление о том, какую ветку вы используете. снова (ничего не меняя). Именно это и делает git reset --soft
: записывает новое имя в HEAD
. (Использование --mixed
добавляет немного больше действий: git reset
затем обновляет индекс/промежуточную область; а использование --hard
добавляет еще больше: git reset
затем стирает содержимое рабочего каталога, заменяя его тем, что вы поместили в файл HEAD
. )
2В режиме "detached HEAD" файл содержит необработанный SHA-1 текущего коммита вместо имени текущей ветки . В этом, собственно, и заключается реальная разница между нахождением «на ветке» и режимом «отсоединенной HEAD». Когда git хочет узнать текущий коммит , он просматривает файл HEAD
. Если у него есть необработанный SHA-1, это ответ. Если у него есть имя ветки, git считывает имя ветки, чтобы получить необработанный SHA-1. Это единственные две разрешенные настройки — ничего другого в файле HEAD
быть не должно.
person
torek
schedule
09.08.2014
git rebase --skip
, поэтому сообщений об ошибках не было вообще, просто паршивое отношение. - person milton   schedule 09.08.2014