Как вводить фиксацию между двумя произвольными коммитами в прошлом?
Предположим, у меня есть следующая история фиксации в моей локальной ветке:
A -- B -- C
Как добавить новую фиксацию между A
и B
?
Ответы
Ответ 1
Это еще проще, чем в ответе OP.
-
git rebase -i <any earlier commit>
. Это отображает список коммитов в вашем сконфигурированном текстовом редакторе. - Найдите фиксацию, которую вы хотите вставить после (предположим, что она
a1b2c3d
). В вашем редакторе для этой строки измените pick
для edit
. - Начните rebase, закрыв свой текстовый редактор (сохраните изменения). Это оставляет вас в командной строке с фиксацией, которую вы выбрали ранее (
a1b2c3d
), как если бы она была только что совершена. - Внесите свои изменения и
git commit
(НЕ изменяя, в отличие от большинства edit
s). Это создает новую фиксацию после той, которую вы выбрали. -
git rebase --continue
. Это повторяет последовательные коммиты, оставляя ваш новый фиксатор вставленным в нужное место.
Остерегайтесь, что это перепишет историю и сломает любого другого, кто попытается потянуть.
Ответ 2
Оказывается, довольно просто, ответ найден здесь. Предположим, вы находитесь на ветке branch
. Выполните эти шаги:
-
создайте временную ветвь из коммита после того, как вы хотите вставить новый коммит (в данном случае коммит A
):
git checkout -b temp A
-
выполнить изменения и зафиксировать их, создав коммит, назовем его N
:
git commit -a -m "Message"
(или git add
последующим git commit
)
-
перебазировать коммиты, которые вы хотите получить после нового коммита (в данном случае коммиты B
и C
), на новый коммит:
git rebase temp branch
(возможно, вам нужно использовать -p
для сохранения слияний, если они были - благодаря уже не существующему комментарию ciekawy)
-
удалить временную ветку:
git branch -d temp
После этого история выглядит следующим образом:
A -- N -- B -- C
Конечно, возможно, что некоторые конфликты возникнут при перебазировании.
Если ваша ветвь не локальная, только это приведет к переписыванию истории, что может вызвать серьезные проблемы.
Ответ 3
Еще проще:
-
Создайте новый коммит в конце, D. Теперь у вас есть:
A -- B -- C -- D
-
Затем выполните:
$ git rebase -i hash-of-A
-
Git откроет ваш редактор, и он будет выглядеть так:
pick 8668d21 B
pick 650f1fc C
pick 74096b9 D
-
Просто переместите D в верхнюю часть, как это, затем сохраните и закройте
pick 74096b9 D
pick 8668d21 B
pick 650f1fc C
-
Теперь у вас будет:
A -- D -- B -- C
Ответ 4
Предполагая, что история фиксации preA -- A -- B -- C
, если вы хотите вставить фиксацию между A
и B
, выполните следующие шаги:
-
git rebase -i hash-of-preA
-
Git откроет ваш редактор. Содержимое может выглядеть так:
pick 8668d21 A
pick 650f1fc B
pick 74096b9 C
Измените первый pick
для edit
:
edit 8668d21 A
pick 650f1fc B
pick 74096b9 C
Сохранить и выйти.
-
Измените свой код и затем git add. && git commit -m "I"
git add. && git commit -m "I"
-
git rebase --continue
Теперь ваша история preA -- A -- я -- B -- C
Git - preA -- A -- я -- B -- C
Если вы столкнулись с конфликтом, Git остановится при этом фиксации. Вы можете использовать git diff
для определения маркеров конфликтов и их устранения. После разрешения всех конфликтов вам нужно использовать git add <filename>
чтобы сообщить Git, что конфликт был разрешен, а затем повторно git rebase --continue
.
Если вы хотите отменить rebase, используйте git rebase --abort
.
Ответ 5
Вот стратегия, которая избегает "взлома редактирования" во время ребазинга, как в других ответах, которые я прочитал.
Используя git rebase -i
вы получаете список коммитов с момента этого коммита. Просто добавьте "break" вверху файла, это приведет к тому, что rebase прервется в этой точке.
break
pick <B hash> <B commit message>
pick <C hash> <C commit message>
После запуска git rebase
остановится в точке "перерыва". Теперь вы можете редактировать свои файлы и создавать свои коммиты как обычно. Затем вы можете продолжить перебазирование с помощью git rebase --continue
. Это может привести к конфликтам, которые вам придется исправить. Если вы заблудились, не забывайте, что вы всегда можете прервать использование git rebase --abort
.
Эту стратегию можно обобщить так, чтобы вставить коммит куда угодно, просто поместите "разрыв" в то место, куда вы хотите вставить коммит.
После переписывания истории не забудьте git push -f
. Применяются обычные предупреждения о других людях, выбирающих вашу ветку.
Ответ 6
Здесь уже много хороших ответов. Я просто хотел добавить решение "без перебазирования" в 4 этапа.
Одним из преимуществ этого решения является то, что вы не касаетесь своей ветки до последнего шага, когда вы на 100% уверены, что с конечным результатом все в порядке, поэтому у вас есть очень удобный "предварительный шаг подтверждения, позволяющий проводить тестирование AB.
Начальное состояние (я принял master
в качестве названия вашей ветки)
A -- B -- C <<< master <<< HEAD
1) Начните, указав ГОЛОВУ в нужном месте
git checkout A
B -- C <<< master
/
A <<< detached HEAD
(Опционально здесь, вместо отсоединения HEAD, мы могли бы создать временную ветвь с git checkout -b temp A
, которую нам нужно было бы удалить в конце процесса. Оба варианта работают так, как вам нравится, поскольку все остальное остается то же самое)
2) Создайте новый коммит D для вставки
# at this point, make the changes you wanted to insert between A and B, then
git commit -am "Message for commit D"
B -- C <<< master
/
A -- D <<< detached HEAD (or <<< temp <<< HEAD)
3) Затем принесите копии последних отсутствующих коммитов B и C (будет той же строкой, если было больше коммитов)
git cherry-pick A..C
# (if any, resolve any potential conflicts between D and these last commits)
B -- C <<< master
/
A -- D -- B' -- C' <<< detached HEAD (or <<< temp <<< HEAD)
(удобное тестирование AB здесь, если необходимо)
Сейчас самое время проверить ваш код, протестировать все, что нужно протестировать, и вы также можете проверить/сравнить/проверить, что у вас было и что вы получите после операций.
4) В зависимости от ваших тестов между C
и C'
, либо все в порядке, либо KO.
(ИЛИ) 4-OK) Наконец, переместите ссылку master
git branch -f master HEAD
B -- C <<< (B and C are candidates for garbage collection)
/
A -- D -- B' -- C' <<< master
(ИЛИ) 4-KO) Просто оставьте master
без изменений
Если вы создали временную ветку, просто удалите ее с помощью git branch -d <name>
, но если вы пошли по отключенному маршруту HEAD, на этом этапе никаких действий не требуется, новые коммиты будут иметь право на сборку мусора сразу после повторного подключения HEAD
с git checkout master
В обоих этих случаях (OK или KO), на этом этапе просто снова проверьте master
, чтобы снова присоединить HEAD
.