Как правильно перенести ветку в Git на наследник предыдущего коммита?
Как правильно переместить ветку react-app на последний коммит и слить ее с deps?
PS: Новичок в Git
- Вопрос задан более двух лет назад
- 179 просмотров
Комментировать
Решения вопроса 1

Сергей Кузнецов @sergey-kuznetsov Куратор тега Git
Автоматизатор
Это называется перебазирование. Мы пересаживаем одну ветку на вершину другой ветки.
git rebase deps react-app
Но ветка react-app у тебя уже запушена, значит её придётся принудительно обновить во внешнем репозитории.
git push —force
И будь внимателен, если ветку react-app уже кто-то скачал себе, то ты их должен предупредить, чтобы они удалили её у себя и скачали заново. Либо обновили через git pull —rebase
3.6 Ветвление в Git — Перебазирование
В Git есть два способа внести изменения из одной ветки в другую: слияние и перебазирование. В этом разделе вы узнаете, что такое перебазирование, как его осуществлять и в каких случаях этот удивительный инструмент использовать не следует.
Простейшее перебазирование
Если вы вернётесь к более раннему примеру из Основы слияния, вы увидите, что разделили свою работу и сделали коммиты в две разные ветки.

Рисунок 35. История коммитов простого разделения
Как мы выяснили ранее, простейший способ выполнить слияние двух веток — это команда merge . Она осуществляет трёхстороннее слияние между двумя последними снимками сливаемых веток ( C3 и C4 ) и самого недавнего общего для этих веток родительского снимка ( C2 ), создавая новый снимок (и коммит).

Рисунок 36. Слияние разделённой истории коммитов
Тем не менее есть и другой способ: вы можете взять те изменения, что были представлены в C4 , и применить их поверх C3 . В Git это называется перебазированием. С помощью команды rebase вы можете взять все коммиты из одной ветки и в том же порядке применить их к другой ветке.
В данном примере переключимся на ветку experiment и перебазируем её относительно ветки master следующим образом:
$ git checkout experiment $ git rebase master First, rewinding head to replay your work on top of it. Applying: added staged command
Это работает следующим образом: берётся общий родительский снимок двух веток (текущей, и той, поверх которой вы выполняете перебазирование), определяется дельта каждого коммита текущей ветки и сохраняется во временный файл, текущая ветка устанавливается на последний коммит ветки, поверх которой вы выполняете перебазирование, а затем по очереди применяются дельты из временных файлов.

Рисунок 37. Перебазирование изменений из C4 поверх C3
После этого вы можете переключиться обратно на ветку master и выполнить слияние перемоткой.
$ git checkout master $ git merge experiment

Рисунок 38. Перемотка ветки master
Теперь снимок, на который указывает C4′ абсолютно такой же, как тот, на который указывал C5 в примере с трёхсторонним слиянием. Нет абсолютно никакой разницы в конечном результате между двумя показанными примерами, но перебазирование делает историю коммитов чище. Если вы взглянете на историю перебазированной ветки, то увидите, что она выглядит абсолютно линейной: будто все операции были выполнены последовательно, даже если изначально они совершались параллельно.
Часто вы будете делать так для уверенности, что ваши коммиты могут быть бесконфликтно слиты в удалённую ветку — возможно, в проекте, куда вы пытаетесь внести вклад, но владельцем которого вы не являетесь. В этом случае вам следует работать в своей ветке и затем перебазировать вашу работу поверх origin/master , когда вы будете готовы отправить свои изменения в основной проект. Тогда владельцу проекта не придётся делать никакой лишней работы — всё решится простой перемоткой или бесконфликтным слиянием.
Учтите, что снимок, на который ссылается ваш последний коммит — является ли он последним коммитом после перебазирования или коммитом слияния после слияния — в обоих случаях это один и тот же снимок, отличаются только истории коммитов. Перебазирование повторяет изменения из одной ветки поверх другой в том порядке, в котором эти изменения были сделаны, в то время как слияние берет две конечные точки и сливает их вместе.
Более интересные перемещения
Также возможно сделать так, чтобы при перебазировании воспроизведение коммитов применялось к совершенно другой ветке. Для примера возьмём История разработки с тематической веткой, ответвлённой от другой тематической ветки. Вы создаёте тематическую ветку server , чтобы добавить в проект некоторую функциональность для серверной части, и делаете коммит. Затем вы выполнили ответвление, чтобы сделать изменения для клиентской части, и создали несколько коммитов. Наконец, вы вернулись на ветку server и сделали ещё несколько коммитов.

Рисунок 39. История разработки с тематической веткой, ответвлённой от другой тематической ветки
Предположим, вы решили, что хотите внести изменения клиентской части в основную линию разработки для релиза, но при этом не хотите добавлять изменения серверной части до полного тестирования. Вы можете взять изменения из ветки client , которых нет в server ( C8 и C9 ), и применить их на ветке master при помощи опции —onto команды git rebase :
$ git rebase --onto master server client
В этой команде говорится: «Переключись на ветку client , найди изменения относительно ветки server и примени их для ветки master ». Несмотря на некоторую сложность этого способа, результат впечатляет.

Рисунок 40. Перемещение тематической ветки, ответвлённой от другой тематической ветки
Теперь вы можете выполнить перемотку (fast-forward) для ветки master (см Перемотка ветки master для добавления изменений из ветки client ):
$ git checkout master $ git merge client

Рисунок 41. Перемотка ветки master для добавления изменений из ветки client
Представим, что вы решили добавить наработки и из ветки server . Вы можете выполнить перебазирование ветки server относительно ветки master без предварительного переключения на неё при помощи команды git rebase , которая извлечёт тематическую ветку (в данном случае server ) и применит изменения в ней к базовой ветке ( master ):
$ git rebase master server
Это повторит работу, сделанную в ветке server поверх ветки master , как показано на рисунке:

Рисунок 42. Перебазирование ветки server на вершину ветки master
После чего вы сможете выполнить перемотку основной ветки ( master ):
$ git checkout master $ git merge server
Теперь вы можете удалить ветки client и server , поскольку весь ваш прогресс уже интегрирован и тематические ветки больше не нужны, а полную историю вашего рабочего процесса отражает рисунок Окончательная история коммитов:
$ git branch -d client $ git branch -d server

Рисунок 43. Окончательная история коммитов
Опасности перемещения
Но даже перебазирование, при всех своих достоинствах, не лишено недостатков, которые можно выразить одной строчкой:
Не перемещайте коммиты, уже отправленные в публичный репозиторий
Если вы будете придерживаться этого правила, всё будет хорошо. Если не будете, люди возненавидят вас, а ваши друзья и семья будут вас презирать.
Когда вы что-то перемещаете, вы отменяете существующие коммиты и создаёте новые, похожие на старые, но являющиеся другими. Если вы куда-нибудь отправляете свои коммиты и другие люди забирают их себе и в дальнейшем основывают на них свою работу, а затем вы переделываете эти коммиты командой git rebase и выкладываете их снова, то ваши коллеги будут вынуждены заново выполнять слияние для своих наработок. В итоге, когда вы в очередной раз попытаетесь включить их работу в свою, вы получите путаницу.
Давайте рассмотрим пример того, как перемещение публично доступных наработок может вызвать проблемы. Предположим, вы клонировали репозиторий с сервера и сделали какую-то работу. И ваша история коммитов выглядит так:

Рисунок 44. Клонирование репозитория и выполнение в нём какой-то работы
Теперь кто-то другой внёс свои изменения, слил их и отправил на сервер. Вы стягиваете их к себе, включая новую удалённую ветку, что изменяет вашу историю следующим образом:

Рисунок 45. Извлекаем ещё коммиты и сливаем их со своей работой
Затем автор коммита слияния решает вернуться назад и перебазировать свою ветку; выполнив git push —force , он перезаписывает историю на сервере. При получении изменений с сервера вы получите и новые коммиты.

Рисунок 46. Кто-то выложил перебазированные коммиты, отменяя коммиты, на которых основывалась ваша работа
Теперь вы оба в неловком положении. Если вы выполните git pull , вы создадите коммит слияния, включающий обе линии истории, и ваш репозиторий будет выглядеть следующим образом:

Рисунок 47. Вы снова выполняете слияние для той же самой работы в новый коммит слияния
Если вы посмотрите git log в этот момент, вы увидите два коммита с одинаковыми авторами, датой и сообщением, что может сбить с толку. Помимо этого, если вы отправите свою историю на удалённый сервер в таком состоянии, вы вернёте все эти перебазированные коммиты на сервер, что ещё больше всех запутает. Логично предположить, что разработчик не хочет, чтобы C4 и C6 были в истории, и именно поэтому она перебазируется в первую очередь.
Меняя базу, меняй основание
Если вы попали в такую ситуацию, у Git есть особая магия чтобы вам помочь. Если кто-то в вашей команде форсирует отправку изменений на сервер, переписывающих работу, на которых базировалась ваша работа, то ваша задача будет состоять в определении того, что именно было ваше, а что было переписано ими.
Оказывается, что помимо контрольной суммы коммита SHA-1, Git также вычисляет контрольную сумму отдельно для патча, входящего в этот коммит. Это контрольная сумма называется «patch-id».
Если вы скачаете перезаписанную историю и перебазируете её поверх новых коммитов вашего коллеги, в большинстве случаев Git успешно определит, какие именно изменения были внесены вами, и применит их поверх новой ветки.
К примеру, если в предыдущем сценарии вместо слияния в Кто-то выложил перебазированные коммиты, отменяя коммиты, на которых основывалась ваша работа мы выполним git rebase teamone/master , Git будет:
- Определять, какая работа уникальна для вашей ветки (C2, C3, C4, C6, C7)
- Определять, какие коммиты не были коммитами слияния (C2, C3, C4)
- Определять, что не было перезаписано в основной ветке (только C2 и C3, поскольку C4 — это тот же патч, что и C4′)
- Применять эти коммиты к ветке teamone/master

Рисунок 48. Перемещение в начало force-pushed перемещённой работы
Это возможно, если C4 и C4′ фактически являются одним и тем же патчем, который был сделан вашим коллегой. В противном случае rebase не сможет определить дубликат и создаст ещё один патч, подобный C4 (который с большой вероятностью не удастся применить чисто, поскольку в нём уже присутствуют некоторые изменения).
Вы можете это упростить, применив git pull —rebase вместо обычного git pull . Или сделать это вручную с помощью git fetch , а затем git rebase teamone/master .
Если вы используете git pull и хотите использовать —rebase по умолчанию, вы можете установить соответствующее значение конфигурации pull.rebase с помощью команды git config —global pull.rebase true .
Если вы рассматриваете перебазирование как способ наведения порядка и работаете с коммитами локально до их отправки или ваши коммиты никогда не будут доступны публично — у вас всё будет хорошо. Однако, если вы перемещаете коммиты, отправленные в публичный репозиторий, и есть вероятность, что работа некоторых людей основывается на этих коммитах, то ваши действия могут вызвать существенные проблемы, а вы — вызвать презрение вашей команды.
Если в какой-то момент вы или ваш коллега находите необходимость в этом, убедитесь, что все знают, как применять команду git pull —rebase для минимизации последствий от подобных действий.
Перемещение vs. Слияние
Теперь, когда вы увидели перемещение и слияние в действии, вы можете задаться вопросом, что из них лучше. Прежде чем ответить на этот вопрос, давайте вернёмся немного назад и поговорим о том, что означает история.
Одна из точек зрения заключается в том, что история коммитов в вашем репозитории — это запись того, что на самом деле произошло. Это исторический документ, ценный сам по себе, и его нельзя подделывать. С этой точки зрения изменение истории коммитов практически кощунственно; вы лжёте о том, что на самом деле произошло. Но что, если произошла путаница в коммитах слияния? Если это случается, репозиторий должен сохранить это для потомков.
Противоположная точка зрения заключается в том, что история коммитов — это история того, как был сделан ваш проект. Вы не публикуете первый черновик книги или инструкции по поддержке вашего программного обеспечения, так как это нуждается в тщательном редактировании. Сторонники этого лагеря считают использование инструментов rebase и filter-branch способом рассказать историю проекта наилучшим образом для будущих читателей.
Теперь к вопросу о том, что лучше — слияние или перебазирование: надеюсь, вы видите, что это не так просто. Git — мощный инструмент, позволяющий вам делать многое с вашей историей, однако каждая команда и каждый проект индивидуален. Теперь, когда вы знаете, как работают оба эти приёма, выбор — какой из них будет лучше в вашей ситуации — зависит от вас.
При этом, вы можете взять лучшее от обоих миров: использовать перебазирование для наведения порядка в истории ваших локальных изменений, но никогда не применять его для уже отправленных куда-нибудь изменений.
Git rebase — перебазирование коммитов и веток
Знакомим с git rebase: рассказываем о преимуществах команды и показываем, в каких случаях ее стоит использовать и почему.
Эта инструкция — часть курса «Введение в Git».
Смотреть весь курс
Введение
Rebase (перебазирование) — один из способов в git, позволяющий объединить изменения двух веток. У этого способа есть преимущество перед merge (слияние) — он позволяет переписать историю ветки, придав тот истории тот вид, который нам нужен.
В этой инструкции мы познакомим вас поближе с командой git rebase, расскажем о преимуществах и тонкостях работы с ней, покажем, в каких случаях ее стоит использовать и почему.
Git rebase — что это
Из документации — это наложение коммитов поверх другого базового коммита. Под базовым понимается тот коммит, к которому применяются коммиты выбранной ветки.
git rebase [ []]
Первый аргумент обязательный (upstream) — это базовый коммит, к которому применятся коммиты выбранной ветки. Второй аргумент можно не задавать, если HEAD указывает на ветку, которая будет нами перебазирована.
Как работает git rebase
Чтобы понимать процесс работы перебазирования, обратимся к рисунку 1.
У нас есть две ветки — master и my_branch. Мы находимся на ветке my_branch (HEAD указывает на ветку my_branch). Выполняем команду:
git rebase master
После этого git удалит и последовательно переместит коммиты C, D, F из ветки my_branch в ветку master — сначала C, затем D и F. Новые коммиты C’, D’, F’ полностью идентичны удаленным, меняется только хеш.
Сначала для ветки my_branch базовым коммитом был B, но после стал коммит E. Это и есть процесс под названием перебазирование.
Как использовать git rebase
Перебазирование в git используется для придания линейности истории ветки, чтобы удобно отслеживать изменения, или для обновления ветки разработки последними изменениями из основной ветки. Также есть и другие варианты использования — с помощью интерактивного режима и параметра —onto.
Линейная история — реинтеграция тематической ветки после выполнения git rebase master
После того как мы использовали команду git rebase, можно перемотать ветку master командой git merge:
git checkout master git merge my_branch
К команде слияния можно добавить флаг либо —ff (fast-forward merge), чтобы не создавать коммит слияния, или добавить —no-ff — для его создания. Создание коммита слияния помогает определить, когда ветки объединились, и какие коммиты тематической ветки были сделаны.
Когда работа с тематической веткой закончена, удаляем ее:
git branch -d my_branch
Это приведет к законченному виду истории, когда мы внедрили изменения из тематической ветки в основную часть проекта.
Конфликты
Так как git rebase последовательно переприменяет коммиты, то могут возникнуть конфликты слияния (merge conflicts). Первая причина появления конфликта — объединение коммитов, содержащих изменения в одних и тех же файлах. Вторая причина — несколько человек изменяют одинаковый файл на одной расшаренной ветке. Чтобы узнать, в каких файлах есть конфликтующие изменения, проверим статус.
git status
Нам будет предложено решить конфликтные коммиты, затем пометить их решенными:
git add/rm
Дальше нужно продолжить перебазирование:
git rebase --continue
Или еще откатить изменения — вернуться в состояние до использования команды rebase.
git rebase --abort
Есть и третий вариант с перезапуском шага и перезагрузкой процесса перебазирования:
git rebase --skip
Но будьте аккуратны, skip пропустит (удалит) конфликтный коммит.
Git rebase interactive
Интерактивный режим rebase используется для перезаписи истории посредством изменения самих коммитов, а также информации в них. Переход в интерактивный режим перебазирования делается при помощи флага -i или —interactive.
git rebase [-i | --interactive]
Выполнение этой команды создаст список коммитов в хронологическом порядке добавления, чтобы пользователь мог по своему желанию отредактировать их перед последующим перебазированием. Дальше мы рассмотрим подробнее, как это происходит.
Как пользоваться интерактивным режимом
Например, вот созданная ветка master с пятью коммитами:
2hqsibn selected new method in script.js (HEAD -> master) 4kq5jn2 changes to the script.j qk01ru3 resolved conflict fmjgyu6 added new files z2zgn0c initial commit (origin/master)
Поставлено две задачи:
- Поменять местами коммиты qk01ru3 и 4kq5jn2;
- исправить ошибку в комментарии четвертого коммита (4kq5jn2).
Для этого мы включаем интерактивный режим rebase. В нем можно указать определенное количество коммитов для изменения. Чтобы это сделать, необходимо передать в аргумент коммит, предшествующий тому, который мы будем изменять (в данном случае это fmjgyu6), либо задать “HEAD~[x]”, где вместо [x] — нужное нам число коммитов.
git rebase -i HEAD~3
Откроется текстовый редактор по умолчанию, где также будет приведено описание команд, используемых в интерактивном режиме rebase:
pick qk01ru3 resolved conflict pick 4kq5jn2 changes to the sсript.j pick 2hqsibn selected new method in script.js # Rebase fmjgyu6..2hqsibn onto fmjgyu6 # # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like "squash", but discard this commit's log message # x, exec = run command (the rest of the line) using shell # b, break = stop here (continue rebase later with 'git rebase --continue') # d, drop = remove commit
Обратите еще раз внимание, что коммиты в интерактивном режиме расположены в другом порядке, от более старого к новому. Приступим к решению задач.
- Для того чтобы поменять коммиты местами, мы просто меняем местами их строчки.
- Чтобы исправить ошибку в комментарии, мы меняем команду “pick” в 4kq5jn2 на “reword” и в следующем окне переписываем комментарий на “changes to the script.js”.
В итоге имеем следующий порядок коммитов:
reword 4kq5jn2 changes to the sсript.js pick qk01ru3 resolved conflict pick 2hqsibn selected new method in script.js
Не забываем сохранять изменения, как вы обычно делаете при сохранении в вашем текстовом редакторе. Выходим из интерактивного режима закрытием текстового редактора.
Теперь наша ветка имеет вид:
vh9xwf3 selected new method in script.js (HEAD -> master) s70zmpx resolved conflict b0jemdh changes to the script.js fmjgyu6 added new files z2zgn0c initial commit (origin/master)
Имейте в виду, что хеши коммитов, с которыми работал rebase, станут иными.
Какой режим выбрать: стандартный или интерактивный
Интерактивный rebase — продвинутая версия обычного rebase, которая дает возможность большего взаимодействия с коммитами. Если нужно разделить, объединить, удалить коммиты, изменить их описание и порядок, интерактивный режим справится на отлично. Если же такой потребности нет, быстрее будет использовать стандартный режим rebase, в котором от пользователя не требуются лишние действия до возникновения конфликтов.
Git rebase —onto
Onto относится к возможностям, раскрывающих rebase во всей красе.
git rebase --onto [ []]
Эта опция позволяет указать коммит, с которого будут перебазироваться коммиты (первый аргумент), иначе это называется новая база. Третий аргумент (branch) можно не указывать, если HEAD указывает на ветку, которая будет нами перебазирована.
Пример
У нас есть три ветки: master, feature-1, develop.
Нам нужно интегрировать изменения из ветки develop в master. Воспользуемся обычным rebase:
git checkout develop
git rebase master
Тогда получим вот такой результат с двумя одинаковыми коммитами D:
Это произошло потому, что перебазирование переприменило коммиты D, G, H, I, так как по отношению к ветке master коммиты ветки develop начинаются с коммита D как связывающего коммита. Теперь решим задачу с интеграцией изменений из develop немного по-другому. Чтобы избежать таких случаев как с коммитом D, воспользуемся новой командой:
git rebase --onto master feature-1 develop
Результат проиллюстрирован на рисунке ниже.
Rebase удаленного репозитория
При работе с удаленным репозиторием в тематической ветке, например на GitHub, следует быть осторожным, используя rebase. Как вы уже знаете, rebase перезаписывает историю, в процессе изменяются хеши коммитов, а это может привести к конфликтам в работе с веткой у других членов команды. Поэтому, если вы работаете над тематической веткой не одни, стоит прибегнуть к нескольким правилам для предотвращения возможных проблем.
- Синхронизировать изменения. Перед тем как вы будете заливать свой код на тот же GitHub, выполните git pull изменений, чтобы избежать конфликтных ситуаций.
- Не перебазировать давно созданные ветки. Количество шагов в rebase равно количеству коммитов на перебазируемой ветке, если не указаны иные опции. Поэтому с увеличением количества неперебазированных коммитов, растет и вероятность появления конфликта.
- Чтобы внедрить изменения в мастер, стоит создать свою локальную ветку и перебазировать ее поверх origin/master. Тогда останется лишь сделать перемотку или бесконфликтное слияние для владельца.
- Не проводить rebase уже отправленных коммитов в публичный репозиторий. Коллегам придется выполнить слияние, что приведет к путанице.
Pull rebase
Чтобы запушить свою ветку, когда git не знает, как объединить ветки, используется режим force:
git push origin --force
С этим режимом будут скопированы родительские коммиты feature на origin, указатель перемещается, как он установлен на локальном репозитории. Важно указать идентификатор ветки в , иначе запушатся все локальные ветки ориджина.
А чтобы извлечь изменения из удаленного репозитория, вместо обычного pull можно использовать режим rebase:
git pull --rebase origin
Локальные merge коммиты не образуются, а история будет выглядеть линейно.
Дополнительные опции перебазирования
Ниже приведена таблица некоторых опций, которые могут быть полезны для работы с rebase.
| Опции | Пояснение |
| -s —strategy= | Использовать стратегию слияния вместо дефолтного “ort”, что изменит поведение rebase. Подробнее в документации. |
| -X —strategy-option= | Эта опция для применения более одной стратегии в порядке, заданном пользователем. |
| -x —exec | Выполнение одной или более shell-команд после каждого шага rebase в интерактивном режиме. Если выполнение команды неудачно, перебазирование остановится. |
| —no-keep-empty | Не оставлять пустые коммиты. То есть убирать те коммиты, которые ничего не меняют по отношению к родителю. |
| —allow-empty-message | Позволяет перебазировать пустые коммиты с пустым сообщением. |
| —autosquash | В интерактивном режиме берет коммиты, которые начинаются с fixup! или squash! и ставит соответствующую команду, чтобы объединить коммит с предыдущим.Чтобы это значение всегда работало по умолчанию, можно прописать следующее:“git config —global rebase.autosquash true” |
Заключение
В этой инструкции мы рассмотрели, как сделать rebase ветки в git, узнали про возможность более продвинутой манипуляции с коммитами в режиме interactive и onto, а также тонкости, которые следует соблюдать при работе с rebase.
Работа с ветками в Git (git branch)
Как переместить ветку на следующий коммит?
Возможно из-за нехватки знаний я неправильно объяснил суть задания, но ответ от @Komdosh мне помог. Как правильно переформулировать вопрос не знаю. Если вы сможете с помощью этого ответа задать правильно вопрос — буду рад.
16 сен 2021 в 11:03
@InDevX, ровно с тех пор, как была написана программа git. в ней «ветка» («branch») и «метка» («tag») — это всего лишь указатели на коммит. // а вопрос отлично сформуилирован. ну, конечно, спрашивается в нём о самом базовом и элементарном, но у нас тут не клуб знатоков, а база знаний.
16 сен 2021 в 11:15
git merge c6 не работает?
16 сен 2021 в 15:29
2 ответа 2
Сортировка: Сброс на вариант по умолчанию
git reset c6
где c6 это хэш коммита. Если надо запушить результат, то при выполнении операции reset лучше использовать git push origin —force
Отслеживать
ответ дан 16 сен 2021 в 10:41
12.1k 2 2 золотых знака 25 25 серебряных знаков 47 47 бронзовых знаков
по поводу «push —force» в совместно используемое хранилище, думаю, лучше всё-таки делать оговорку о возможных проблемах. я в таком случае, чтоб не переписывать, просто ссылаюсь на свой ответ, ну а вы можете сослаться и на любое другое описане «подводных камней».
16 сен 2021 в 15:21
Лучше поздно, чем никогда 😉
Передвинуть указатель ветки main на коммит c6 :
git branch -f main c6
Где находиться HEAD неизвестно, поэтому, на всякий случай, его можно передвинуть туда же:
git checkout c6
При этом, или уже, у вас состояние detached HEAD (отделенный HEAD)
Это когда HEAD не указывает на указетель ветки, хотя они могут быть на одном коммите. При создании новых коммитов, HEAD перемещается на новый коммит, а main нет.
Что бы вернуть HEAD на main выполните следующую команду:
git checkout main
Или, не важно где сейчас находяться main и HEAD , возможно на разных коммитах, вы можете передвинуть main на с6 , а HEAD на main одной командой:
git checkout -B main с6