Если я рендерим ваши четыре коммита в стиле, который я предпочитаю для сообщений StackOverflow, это выглядит так, с более новыми коммитами вправо:
C <-- branch1
/
D
\
B--A <-- branch2
Два имени - вы не сказали нам, что они есть, поэтому я просто использовал branch1
и branch2
здесь - содержит необработанный идентификатор ha sh фактических коммитов C
и A
, которые являются коммитом наконечника двух ветвей. (C
и A
заменяют настоящие идентификаторы ha sh, которые являются большими, некрасивыми и случайными на вид.)
Родителем коммита A
является коммит B
. Родителем коммита B
является коммит D
. Родителем коммита C
также является коммит D
. (Если есть коммиты до коммита D
, мы их не видим.)
Вы говорите, что хотели бы иметь:
D--C--B--A
в качестве конечного результата. Вы не можете: commit B
Родитель - commit D
, сейчас и навсегда. Вы можете иметь:
D--C--B'-A'
, где B'
является копией из B
, но с другим идентификатором ha sh, поскольку он также имеет другой родитель (и, возможно, другой снимок). Аналогично, A'
должен быть результатом извлечения коммитов B'
и A
и повторной передачи чего-то немного другого: родительский элемент, если A'
равен B'
, а A'
, вероятно, содержит снимок, отличный от A
точно так же, как B'
, вероятно, содержит снимок, отличный от B
.
фиксирует B
и A
продолжит существовать, т. е. ваш репозиторий будет иметь:
D--C--B'-A'
\
B--A
в этом. Если вы запишите где-нибудь действительные идентификаторы ha sh, равные B
и A
, и введете их позже, вы увидите, что коммиты продолжают существовать. (По умолчанию они будут работать не менее 30 дней, если вы решите, что хотите их вернуть.)
Однако в вашем конечном результате не хватает того, какие имен вы хотите указывать на , который фиксирует . Вы можете, если хотите, оба имени указывать на коммит A'
, например:
D--C--B'-A' <-- branch1, branch2
, или вы можете branch2
указывать на A'
и оставлять branch1
, указывая на существующий коммит C
, вот так:
B'-A' <-- branch2
/
D--C <-- branch1
Набор команд Git, которые вы можете использовать для достижения конечного результата, зависит от того, какой из этих конечных результатов вы хотите.
Примечание что ответ Саураба П Бхандари эквивалентен:
git checkout --detach branch2 # or git checkout <hash-of-A>
git rebase branch1
, что дает:
B'-A' <-- HEAD
/
D--C <-- branch1
\
B--A <-- branch1
т.е. вы отсоединены от HEAD в режиме конец, с ни одним именем ветви перемещено.
Поскольку существует два имени ветви задействовано - branch1
и branch2
являются двумя именами - он принимает как минимум две Git команды для достижения:
D--C--B'-A' <-- branch1, branch2
\
B--A [abandoned]
в качестве конечного результата, если вы этого хотите. Минимальный набор команд - есть много последовательностей команд, которые достигнут этого результата - это:
git checkout branch2
git rebase branch1
git push . branch2:branch1
, что оставляет вас с HEAD
прикрепленным к имени branch2
:
D--C--B'-A' <-- branch1, branch2 (HEAD)
\
B--A [abandoned]
ответ символической ссылки с подстановками имен составляет:
git checkout branch2
git rebase branch1
git checkout branch1
git merge --ff-only branch2 # the --ff-only is not required here
, что делает то же самое, но оставляет HEAD
прикрепленным к имени branch1
:
D--C--B'-A' <-- branch1 (HEAD), branch2
\
B--A [abandoned]
Флаг --ff-only
гарантирует, что если что-то пошло не так и фиксация, идентифицируемая именем branch2
, теперь не строго впереди фиксации, идентифицируемой branch1
, Git выдаст сообщение об ошибке, а не новый коммит слияния.
Команда git push . <em>src</em>:<em>dst</em>
особенно хитрая / хитрая, и я не очень рекомендую ее вообще, но она иллюстрирует способ попросить Git перемотать вперед одно из ваших имен - dst
- указывать на любой данный коммит, в данном случае src
. Здесь мы переместим имя branch1
, чтобы оно указывало на тот же коммит, что и branch2
. Опять же, команда pu sh имеет source слева и destination справа, поэтому мы пишем это как branch2:branch1
.
Ключ к пониманию всего этого
Git заключается в commits . Каждый коммит имеет уникальный идентификатор ha sh, и как только коммит сделан, его часть не может быть изменена . Поэтому, если вы хотите другие коммиты, вы должны делать новые. Это обычно не имеет большого значения. Вот что делает git rebase
: копирует некоторые существующие коммиты в некоторые новые и улучшенные.
Вы - и Git в этом отношении - находка Коммит, однако, начинается с last в цепочке, идентифицируемого по имени ветви, а затем работает в обратном направлении. (Вот почему commit A
является последним, а не первым, на каждом из наших рисунков: вы помещаете последний вверху, а я помещаю последний справа.)
Скопировав серию коммитов, найденных по определенному имени, git rebase
now перетаскивает имя, чтобы указать на последний из новых коммитов . Для одного имени это как раз то, что вы хотите.
Но после того, как вы скопировали серию коммитов, если есть более одного имени , которое вы хотели бы переместить, Вы должны переместить остальные имена. Это действительно все, что нужно сделать.