Не создавая ветку и делая кучу фанковой работы над новой ветвью, можно ли разбить одно коммит на несколько разных коммитов после того, как он был привязан к локальному репозиторию?
Перерыв предыдущей фиксации на несколько коммитов
Ответ 1
git rebase -i
сделает это.
Во-первых, начните с чистого рабочего каталога: git status
должно отображать ожидающих изменений, удалений или дополнений.
Теперь вы должны решить, какой коммит вы хотите разделить.
А) Разделение самого последнего коммита
Чтобы разделить ваш последний коммит, сначала:
$ git reset HEAD~
Теперь фиксируйте фигуры индивидуально обычным способом, создавая столько коммитов, сколько вам нужно.
Б) Разделить коммит дальше назад
Это требует перебазирования, то есть переписывания истории. Чтобы найти правильный коммит, у вас есть несколько вариантов:
-
Если это было три коммитов назад, то
$ git rebase -i HEAD~3
где
3
- это сколько коммитов обратно. -
Если это было дальше в дереве, чем вы хотите сосчитать, то
$ git rebase -i 123abcd~
где
123abcd
- это SHA1 коммита, который вы хотите разделить. -
Если вы находитесь в другой ветке (например, ветке функций), которую вы планируете объединить в master:
$ git rebase -i master
Когда вы получите экран редактирования rebase, найдите коммит, который вы хотите разбить на части. В начале этой строки замените pick
на edit
(для краткости e
). Сохраните буфер и выйдите. Rebase теперь остановится сразу после коммита, который вы хотите отредактировать. Затем:
$ git reset HEAD~
Зафиксируйте фрагменты индивидуально обычным способом, выполнив столько коммитов, сколько вам нужно, затем
$ git rebase --continue
Ответ 2
Из git-rebase руководство (раздел SPLITTING COMMITS)
В интерактивном режиме вы можете отметить фиксацию действием "редактировать". Однако это не обязательно означает, что git rebase ожидает, что результат этого редактирования будет точно одним фиксатором. Действительно, вы можете отменить фиксацию или добавить другие коммиты. Это можно использовать для разделения фиксации на две части:
Запустите интерактивную перезагрузку с помощью
git rebase -i <commit>^
, где<commit>
- это фиксация, которую вы хотите разбить. Фактически, любой диапазон фиксации будет действовать, если он содержит это commit.Отметьте фиксацию, которую вы хотите разделить, с действием "редактировать".
Когда дело доходит до редактирования этого сообщения, выполните
git reset HEAD^
. Эффект заключается в том, что HEAD перематывается на один, и индекс следует примеру. Однако рабочее дерево остается прежним.Теперь добавьте изменения в индекс, который вы хотите иметь в первом коммите. Вы можете использовать
git add
(возможно, интерактивно) илиgit gui
(или оба) для этого.Зафиксировать текущий текущий индекс с любым сообщением о фиксации.
Повторите два последних шага, пока ваше рабочее дерево не станет чистым.
Продолжайте перезагрузку с помощью
git rebase --continue
.
Ответ 3
Используйте 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
показывает нам, что мы просто не выполнили фиксацию, сделанную для второй фиксации:
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
to
one
two and a third
two and two thirds
three
git add .; git rebase --continue
Теперь наш git log -p
выглядит так:
commit e59ca35bae8360439823d66d459238779e5b4892
Author: Rose Perrone <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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
Ответ 4
В предыдущих ответах рассматривалось использование git rebase -i
для редактирования коммита, который вы хотите разделить, и фиксации его по частям.
Это хорошо работает при разбиении файлов на разные коммиты, но если вы хотите разбить изменения на отдельные файлы, вам нужно знать больше.
Получив коммит, который вы хотите разделить, используя rebase -i
и отметив его для edit
, у вас есть два варианта.
-
После использования
git reset HEAD~
, пройдитесь поgit add -p
индивидуально, используяgit add -p
чтобы выбрать те, которые вы хотите в каждом коммите. -
Отредактируйте рабочую копию, чтобы удалить изменения, которые вы не хотите; совершить это временное состояние; и затем вытяните полный коммит для следующего раунда.
Вариант 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)
...
В этом случае редактируемый 65dfb6a
имеет sha1 65dfb6a
. Зная это, я могу проверить содержимое этого коммита в моем рабочем каталоге, используя форму git checkout
которая принимает как коммит, так и местоположение файла. Здесь я использую .
в качестве местоположения файла для замены всей рабочей копии:
git checkout 65dfb6a .
Не пропустите точку на конце!
После этого будут проверены и отредактированы файлы, какими они были после коммита, который вы редактируете, но относительно предыдущего коммита, который вы сделали, поэтому любые изменения, которые вы уже зафиксировали, не будут частью коммита.
Вы можете либо пойти дальше и зафиксировать его как -i, чтобы завершить разделение, либо повторить попытку, удалив некоторые части фиксации перед выполнением другой временной фиксации.
Если вы хотите повторно использовать исходное сообщение о коммите для одного или нескольких коммитов, вы можете использовать его прямо из рабочих файлов rebase:
git commit --file .git/rebase-merge/message
Наконец, как только вы совершите все изменения,
git rebase --continue
продолжит и завершит операцию перебазирования.
Ответ 5
git rebase --interactive
может использоваться для разделения фиксации на более мелкие коммиты. В Git docs на rebase есть краткий обзор процесса - Разделение комманд:
В интерактивном режиме вы можете отметить фиксацию действием "редактировать". Однако это не обязательно означает, что
git rebase
ожидает, что результат этого редактирования будет точно одним фиксатором. Действительно, вы можете отменить фиксацию или добавить другие коммиты. Это можно использовать для разделения фиксации на две части:
Запустите интерактивную перезагрузку с помощью
git rebase -i <commit>^
, где<commit>
- это фиксация, которую вы хотите разбить. Фактически, любой диапазон фиксации будет действовать, если он содержит это commit.Отметьте фиксацию, которую вы хотите разделить, с действием "edit".
Когда дело доходит до редактирования этого commit, выполните
git reset HEAD^
. Эффект заключается в том, что HEAD перематывается на один, и индекс следует примеру. Однако рабочее дерево остается прежним.Теперь добавьте изменения в индекс, который вы хотите иметь в первом коммите. Вы можете использовать
git add
(возможно, интерактивно) или git gui (или оба) для этого.Зафиксируйте текущий текущий код с любым сообщением о фиксации.
Повторите последние два шага, пока ваше рабочее дерево не станет чистым.
Продолжайте rebase с
git rebase --continue
.Если вы не абсолютно уверены в том, что промежуточные версии согласованы (они компилируются, передают testuite и т.д.), вы должны использовать
git stash
, чтобы отменить еще не совершенные изменения после каждого фиксации, проверки и изменения фиксация, если исправления необходимы.
Ответ 6
Вы можете выполнить интерактивную перезагрузку git rebase -i
. Страница пользователя имеет именно то, что вы хотите:
Ответ 7
Теперь в последнем TortoiseGit в Windows вы можете сделать это очень легко.
Откройте диалог восстановления, настройте его и выполните следующие шаги.
- Щелкните правой кнопкой мыши фиксацию, которую вы хотите разбить, и выберите "
Edit
" (среди выбора, сквош, удаление...). - Нажмите "
Start
", чтобы начать перезагрузку. - Как только он придет к фиксации для разделения, проверьте кнопку "
Edit/Split
" и нажмите "Amend
" напрямую. Откроется диалоговое окно фиксации. - Отмените выбор файлов, которые вы хотите разместить отдельно.
- Отредактируйте сообщение фиксации и нажмите "
commit
". - До тех пор, пока не будут файлы для фиксации, диалог фиксации откроется снова и снова. Когда больше нет файла для фиксации, он все равно спросит вас, хотите ли вы добавить еще одну фиксацию.
Очень полезно, спасибо TortoiseGit!
Ответ 8
Обратите внимание, что там также git reset --soft HEAD^
. Он похож на git reset
(который по умолчанию равен --mixed
), но сохраняет содержимое индекса. Так что, если вы добавили/удалили файлы, у вас их уже есть в индексе.
оказывается очень полезным в случае гигантских коммитов.
Ответ 9
Легче всего обойтись без интерактивной переустановки (возможно), чтобы создать новую ветку, начиная с фиксации до той, которую вы хотите разделить, cherry-pick -n commit, reset, stash, зафиксировать перемещение файла, повторно приложите квитанцию и зафиксируйте изменения, а затем либо объединитесь с прежним филиалом, либо вишневым захватом совершаемых коммитов. (Затем переключите прежнее название ветки на текущую голову.) (Вероятно, лучше следовать рекомендациям MBOs и выполнять интерактивную перезагрузку.)
Ответ 10
Я думаю, что лучший способ использовать git rebase -i
. Я создал видео, чтобы показать шаги для разделения фиксации: https://www.youtube.com/watch?v=3EzOz7e1ADI
Ответ 11
Если у вас есть это:
A - B <- mybranch
Если вы зафиксировали некоторый контент в фиксации B:
/modules/a/file1
/modules/a/file2
/modules/b/file3
/modules/b/file4
Но вы хотите разбить B на C-D и получить этот результат:
A - C - D <-mybranch
Вы можете разделить содержимое, например, (содержимое из разных каталогов в разных записях)...
Reset ветвь вернется к фиксации перед разделом:
git checkout mybranch
git reset --hard A
Создать первый фиксатор (C):
git checkout B /modules/a
git add -u
git commit -m "content of /modules/a"
Создать вторую фиксацию (D):
git checkout B /modules/b
git add -u
git commit -m "content of /modules/b"
Ответ 12
Прошло уже более 8 лет, но, возможно, кто-то найдет это полезным в любом случае. Я смог сделать трюк без rebase -i
. Идея состоит в том, чтобы привести git в то же состояние, в котором он находился до того, как вы сделали git commit
:
# first rewind back (mind the dot,
# though it can be any valid path,
# for instance if you want to apply only a subset of the commit)
git reset --hard <previous-commit> .
# apply the changes
git checkout <commit-you-want-to-split>
# we're almost there, but the changes are in the index at the moment,
# hence one more step (exactly as git gently suggests):
# (use "git reset HEAD <file>..." to unstage)
git reset
После этого вы увидите эти блестящие Unstaged changes after reset:
и ваш репо находится в состоянии, как будто вы собираетесь зафиксировать все эти файлы. Отныне вы можете легко совершить это снова, как обычно. Надеюсь, поможет.