Разбить предыдущий коммит на несколько коммитов - PullRequest
1038 голосов
/ 02 июня 2011

Не создавая ветку и не выполняя прикольную работу над новой веткой, возможно ли разбить один коммит на несколько разных коммитов после его фиксации в локальном репозитории?

Ответы [ 12 ]

1581 голосов
/ 02 июня 2011

git rebase -i сделает это.

Сначала начните с чистого рабочего каталога: git status не должно показывать ожидающих изменений, удалений или дополнений.

Теперь вам нужно решить, какой коммит (ы) вы хотите разделить.

A) Разделение самого последнего коммита

Чтобы разделить ваш последний коммит, сначала:

$ git reset HEAD~

Теперь фиксируйте фигуры индивидуально обычным способом, создавая столько коммитов, сколько вам нужно.

B) Разделение коммита дальше назад

Для этого требуется перебазировка переписывание истории.Чтобы найти правильный коммит, у вас есть несколько вариантов:

  • Если было три коммита назад, то

    $ git rebase -i HEAD~3
    

    , где 3 - это количество коммитов обратноis.

  • Если в дереве он был дальше, чем вы хотите сосчитать, то

    $ git rebase -i 123abcd~
    

    где 123abcd - SHA1 коммита, который вы хотитеразделить.

  • Если вы находитесь в другой ветке (например, в ветви функций), которую вы планируете объединить в master:

    $ git rebase -i master
    

Когда вы получите экран редактирования rebase, найдите коммит, который вы хотите разбить на части.В начале этой строки замените pick на edit (для краткости e).Сохраните буфер и выйдите.Rebase теперь остановится сразу после коммита, который вы хотите редактировать.Затем:

$ git reset HEAD~

Произведите отдельные фрагменты обычным способом, производя столько коммитов, сколько вам нужно, затем

$ git rebase --continue
289 голосов
/ 22 января 2010

Из git-rebase manual (раздел SPLITTING COMMITS)

В интерактивном режиме вы можете пометить коммиты с помощью действия «edit».Однако это не обязательно означает, что git rebase ожидает, что результатом этого редактирования будет ровно один коммит.Действительно, вы можете отменить коммит или добавить другие коммиты.Это можно использовать для разделения коммита на две части:

  • Начните интерактивную перебазировку с git rebase -i <commit>^, где <commit> - коммит, который вы хотите разделить.Фактически подойдет любой диапазон фиксации, если он содержит этот коммит.

  • Отметьте коммит, который вы хотите разделить, с помощью действия «редактировать».

  • Когда дело доходит до редактирования этого коммита, выполните git reset HEAD^.В результате HEAD перематывается на единицу, и индекс следует за ним.Однако рабочее дерево остается прежним.

  • Теперь добавьте изменения в индекс, которые вы хотите иметь при первом коммите.Для этого вы можете использовать git add (возможно, в интерактивном режиме) или git gui (или оба).

  • Зафиксируйте текущий текущий индекс с любым соответствующим сообщением.

  • Повторяйте последние два шага, пока ваше рабочее дерево не станет чистым.

  • Продолжите перебазировку с помощью git rebase --continue.

39 голосов
/ 08 июля 2013

Используйте git rebase --interactive, чтобы отредактировать этот предыдущий коммит, запустите git reset HEAD~, а затем git add -p, чтобы добавить некоторые, затем сделайте коммит, затем добавьте еще несколько и сделайте другой коммит столько раз, сколько захотите. Когда вы закончите, запустите git rebase --continue, и у вас будут все коммиты разделения ранее в вашем стеке.

Важно : обратите внимание, что вы можете поиграть и внести все необходимые изменения, и вам не нужно беспокоиться о потере старых изменений, потому что вы всегда можете запустить git reflog, чтобы найти точку в вашем проекте содержит необходимые изменения (назовем это a8c4ab), а затем git reset a8c4ab.

Вот серия команд, чтобы показать, как это работает:

mkdir git-test; cd git-test; git init

Теперь добавьте файл A

vi A

добавить эту строку:

one

git commit -am one

затем добавьте эту строку в A:

two

git commit -am two

затем добавьте эту строку в A:

three

git commit -am three

теперь файл A выглядит так:

one
two
three

и наш git log выглядит следующим образом (ну, я использую git log --pretty=oneline --pretty="%h %cn %cr ---- %s"

bfb8e46 Rose Perrone 4 seconds ago ---- three
2b613bc Rose Perrone 14 seconds ago ---- two
9aac58f Rose Perrone 24 seconds ago ---- one

Допустим, мы хотим разделить второй коммит, two.

git rebase --interactive HEAD~2

Это вызывает сообщение, которое выглядит так:

pick 2b613bc two
pick bfb8e46 three

Измените первый pick на e для редактирования этого коммита.

git reset HEAD~

git diff показывает нам, что мы только что сделали unstaged обязательство, которое мы сделали для второго коммита:

diff --git a/A b/A
index 5626abf..814f4a4 100644
--- a/A
+++ b/A
@@ -1 +1,2 @@
 one
+two

Давайте внесем это изменение и добавим «и третий» к этой строке в файле A.

git add .

Обычно это точка во время интерактивного перебазирования, когда мы запускаем git rebase --continue, потому что мы обычно просто хотим вернуться в наш стек коммитов, чтобы отредактировать более ранний коммит. Но на этот раз мы хотим создать новый коммит. Итак, мы запустим git commit -am 'two and a third'. Теперь мы редактируем файл A и добавляем строку two and two thirds.

git add . git commit -am 'two and two thirds' git rebase --continue

У нас есть конфликт с нашим коммитом, three, поэтому давайте разрешим его:

Мы изменим

one
<<<<<<< HEAD
two and a third
two and two thirds
=======
two
three
>>>>>>> bfb8e46... three

до

one
two and a third
two and two thirds
three

git add .; git rebase --continue

Теперь наш git log -p выглядит так:

commit e59ca35bae8360439823d66d459238779e5b4892
Author: Rose Perrone <roseperrone@fake.com>
Date:   Sun Jul 7 13:57:00 2013 -0700

    three

diff --git a/A b/A
index 5aef867..dd8fb63 100644
--- a/A
+++ b/A
@@ -1,3 +1,4 @@
 one
 two and a third
 two and two thirds
+three

commit 4a283ba9bf83ef664541b467acdd0bb4d770ab8e
Author: Rose Perrone <roseperrone@fake.com>
Date:   Sun Jul 7 14:07:07 2013 -0700

    two and two thirds

diff --git a/A b/A
index 575010a..5aef867 100644
--- a/A
+++ b/A
@@ -1,2 +1,3 @@
 one
 two and a third
+two and two thirds

commit 704d323ca1bc7c45ed8b1714d924adcdc83dfa44
Author: Rose Perrone <roseperrone@fake.com>
Date:   Sun Jul 7 14:06:40 2013 -0700

    two and a third

diff --git a/A b/A
index 5626abf..575010a 100644
--- a/A
+++ b/A
@@ -1 +1,2 @@
 one
+two and a third

commit 9aac58f3893488ec643fecab3c85f5a2f481586f
Author: Rose Perrone <roseperrone@fake.com>
Date:   Sun Jul 7 13:56:40 2013 -0700

    one

diff --git a/A b/A
new file mode 100644
index 0000000..5626abf
--- /dev/null
+++ b/A
@@ -0,0 +1 @@
+one
32 голосов
/ 31 марта 2017

В предыдущих ответах рассматривалось использование git rebase -i для редактирования коммита, который вы хотите разделить, и фиксации его по частям.

Это хорошо работает при разбиении файлов на разные коммиты, но если вы хотите разбить изменения на отдельные файлы, вам нужно знать больше.

Добравшись до коммита, который вы хотите разделить, используя rebase -i и отметив его как edit, у вас есть два варианта.

  1. После использования git reset HEAD~, проходите патчи по отдельности, используя git add -p, чтобы выбрать те, которые вы хотите в каждом коммите

  2. Отредактируйте рабочую копию, чтобы удалить изменения, которые вам не нужны; совершить это временное состояние; и затем верните полный коммит для следующего раунда.

Вариант 2 полезен, если вы разбиваете большой коммит, поскольку он позволяет вам проверить, что промежуточные версии создаются и работают правильно как часть слияния. Это происходит следующим образом.

После использования rebase -i и edit с фиксацией используйте

git reset --soft HEAD~

, чтобы отменить фиксацию, но оставить зафиксированные файлы в индексе. Вы также можете сделать смешанный сброс, пропустив --soft, в зависимости от того, насколько близок к конечному результату ваш первоначальный коммит. Разница лишь в том, начинаете ли вы со всех этапов внесения изменений или со всеми без изменений.

Теперь зайдите и отредактируйте код. Вы можете удалить изменения, удалить добавленные файлы и сделать все, что хотите, чтобы создать первый коммит из серии, которую вы ищете. Вы также можете собрать его, запустить и подтвердить, что у вас есть согласованный набор источников.

Как только вы будете довольны, поставьте / разархивируйте файлы по мере необходимости (мне нравится использовать git gui для этого) и зафиксируйте изменения через пользовательский интерфейс или командную строку

git commit

Это первый сделанный коммит. Теперь вы хотите восстановить свою рабочую копию в состояние, которое она имела после разделения коммита, чтобы вы могли принять больше изменений для вашего следующего коммита. Чтобы найти sha1 коммита, который вы редактируете, используйте git status. В первых нескольких строках состояния вы увидите команду rebase, которая выполняется в данный момент, в которой вы можете найти sha1 вашего исходного коммита:

$ git status
interactive rebase in progress; onto be83b41
Last commands done (3 commands done):
   pick 4847406 US135756: add debugging to the file download code
   e 65dfb6a US135756: write data and download from remote
  (see more in file .git/rebase-merge/done)
...

В этом случае редактируемый мной коммит имеет sha1 65dfb6a. Зная это, я могу проверить содержимое этого коммита в моем рабочем каталоге, используя форму git checkout, которая принимает как коммит, так и местоположение файла. Здесь я использую . в качестве местоположения файла для замены всей рабочей копии:

git checkout 65dfb6a .

Не пропустите точку на конце!

Это будет проверять и обрабатывать файлы такими, какими они были после коммита, который вы редактируете, но относительно предыдущего коммита, который вы сделали, поэтому любые изменения, которые вы уже зафиксировали, не будут частью коммита.

Вы можете либо пойти дальше и зафиксировать его как есть, чтобы завершить разделение, либо снова обойти, удалив некоторые части коммита перед выполнением другого временного коммита.

Если вы хотите повторно использовать исходное сообщение о коммите для одного или нескольких коммитов, вы можете использовать его прямо из рабочих файлов rebase:

git commit --file .git/rebase-merge/message

Наконец, после внесения всех изменений

git rebase --continue

продолжит и завершит операцию перебазирования.

17 голосов
/ 15 июля 2013

git rebase --interactive может использоваться для разделения коммита на коммиты меньшего размера. В Git-документах по rebase есть краткое пошаговое описание процесса - Разделение коммитов :

В интерактивном режиме вы можете пометить коммиты с помощью действия «изменить». Однако это не обязательно означает, что git rebase ожидает, что результатом этого редактирования будет ровно один коммит. Действительно, вы можете отменить коммит или добавить другие коммиты. Это можно использовать для разделения коммита на две части:

  • Начните интерактивную перебазировку с git rebase -i <commit>^, где <commit> - коммит, который вы хотите разделить. Фактически подойдет любой диапазон фиксации, если он содержит этот коммит.

  • Отметьте коммит, который вы хотите разделить, с помощью действия «edit».

  • Когда дело доходит до редактирования этого коммита, выполните git reset HEAD^. В результате HEAD перематывается на единицу, и индекс следует за ним. Однако рабочее дерево остается прежним.

  • Теперь добавьте изменения в индекс, которые вы хотите иметь при первом коммите. Для этого вы можете использовать git add (возможно, в интерактивном режиме) или git gui (или оба).

  • Зафиксируйте текущий текущий индекс с тем сообщением, которое сейчас подходит.

  • Повторяйте последние два шага, пока ваше рабочее дерево не станет чистым.

  • Продолжить перебазировку с помощью git rebase --continue.

Если вы не совсем уверены, что промежуточные ревизии согласуются (они компилируются, проходят тестовый набор и т. Д.), Вам следует использовать git stash, чтобы скрыть еще не принятые изменения после каждой фиксации, тестирования и исправления. фиксация, если необходимы исправления.

10 голосов
/ 02 июня 2011

Вы можете сделать интерактивную перебазировку git rebase -i.Страница man имеет именно то, что вы хотите:

http://git -scm.com / docs / git-rebase # _splitting_commits

9 голосов
/ 22 апреля 2016

Теперь в последней версии TortoiseGit для Windows вы можете сделать это очень легко.

Откройте диалоговое окно rebase, настройте его и выполните следующие шаги.

  • Щелкните правой кнопкой мыши на коммите, который вы хотите разделить, и выберите «Edit» (среди пика, сквоша, удаления ...).
  • Нажмите «Start», чтобы начать перебазирование.
  • Как только он прибудет в коммит для разделения, проверьте кнопку "Edit/Split" и нажмите "Amend" напрямую.Откроется диалоговое окно фиксации.
    Edit/Split commit
  • Отмените выбор файлов, которые вы хотите поместить в отдельный коммит.
  • Отредактируйте сообщение фиксации, а затем нажмите "commit».
  • Пока не появятся файлы для фиксации, диалог фиксации будет открываться снова и снова.Когда больше нет файла для фиксации, он все равно спросит вас, хотите ли вы добавить еще один коммит.

Очень полезно, спасибо TortoiseGit!

8 голосов
/ 27 ноября 2014

Обратите внимание, что есть также git reset --soft HEAD^.Он похож на git reset (по умолчанию --mixed), но сохраняет содержимое индекса.Так что, если вы добавили / удалили файлы, они уже есть в индексе.

Оказывается очень полезным в случае гигантских коммитов.

2 голосов
/ 25 июля 2016

Я думаю, что лучше всего использовать git rebase -i.Я создал видео, чтобы показать шаги для разделения коммита: https://www.youtube.com/watch?v=3EzOz7e1ADI

2 голосов
/ 22 января 2010

Проще всего обойтись без интерактивного перебазирования - это (вероятно) создать новую ветвь, начинающуюся с коммита до того, который вы хотите разделить, cherry-pick -n за коммит, сбросить, спрятать, зафиксировать перемещение файла, повторно применить сохраните и зафиксируйте изменения, а затем либо объединитесь с прежним ответвлением, либо выберите нужные коммиты. (Затем переключите прежнее имя ветки на текущий заголовок.) (Вероятно, лучше последовать совету MBO и сделать интерактивную перебазировку.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...