Как вводить фиксацию между двумя произвольными коммитами в прошлом?

Предположим, у меня есть следующая история фиксации в моей локальной ветке:

A -- B -- C

Как добавить новую фиксацию между A и B?

Ответ 1

Это еще проще, чем в ответе OP.

  1. git rebase -i <any earlier commit>. Это отображает список коммитов в вашем сконфигурированном текстовом редакторе.
  2. Найдите фиксацию, которую вы хотите вставить после (предположим, что она a1b2c3d). В вашем редакторе для этой строки измените pick для edit.
  3. Начните rebase, закрыв свой текстовый редактор (сохраните изменения). Это оставляет вас в командной строке с фиксацией, которую вы выбрали ранее (a1b2c3d), как если бы она была только что совершена.
  4. Внесите свои изменения и git commit (НЕ изменяя, в отличие от большинства edit s). Это создает новую фиксацию после той, которую вы выбрали.
  5. 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

Еще проще:

  1. Создайте новый коммит в конце, D. Теперь у вас есть:

    A -- B -- C -- D
    
  2. Затем выполните:

    $ git rebase -i hash-of-A
    
  3. Git откроет ваш редактор, и он будет выглядеть так:

    pick 8668d21 B
    pick 650f1fc C
    pick 74096b9 D
    
  4. Просто переместите D в верхнюю часть, как это, затем сохраните и закройте

    pick 74096b9 D
    pick 8668d21 B
    pick 650f1fc C
    
  5. Теперь у вас будет:

    A -- D -- B -- C
    

Ответ 4

Предполагая, что история фиксации preA -- A -- B -- C, если вы хотите вставить фиксацию между A и B, выполните следующие шаги:

  1. git rebase -i hash-of-preA

  2. Git откроет ваш редактор. Содержимое может выглядеть так:

    pick 8668d21 A
    pick 650f1fc B
    pick 74096b9 C
    

    Измените первый pick для edit:

    edit 8668d21 A
    pick 650f1fc B
    pick 74096b9 C
    

    Сохранить и выйти.

  3. Измените свой код и затем git add. && git commit -m "I" git add. && git commit -m "I"

  4. 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.