it-roy-ru.com

Как разделить последний коммит на два в Git

У меня есть две рабочие ветки, master и forum , и я только что внес некоторые изменения в forum branch, которые я хотел бы выбрать в master . Но, к сожалению, коммит, который я хочу выбрать из вишни, также содержит некоторые модификации, которые мне не нужны.

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

Я пытался сделать

git reset --hard HEAD^

который удалил все изменения, поэтому мне пришлось вернуться с

git reset ORIG_HEAD

Итак, мой вопрос каков наилучший способ разделить последний коммит на два отдельных коммита?

271
Jakub Arnold

Вы должны использовать индекс. После смешанного сброса (" git reset HEAD ^") добавьте Первый набор изменений в индекс, а затем передайте их. Затем совершите rest.

Вы можете использовать " git add ", чтобы поместить все изменения, сделанные в файле, в индекс. Если вы Не хотите ставить все изменения, сделанные в файле, только некоторые из них, вы Можете использовать «git add -p».

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

something
something else
something again

Я изменил его в моем последнем коммите, так что теперь это выглядит так:

1
something
something else
something again
2

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

Сначала я возвращаюсь к родителю HEAD, но я хочу сохранить изменения в файловой системе, Поэтому я использую «git reset» без аргументов (что будет делать так называемый «mixed» Reset):

$ git reset HEAD^
myfile: locally modified
$ cat myfile
1
something
something else
something again
2

Теперь я использую «git add -p» для добавления изменений, которые я хочу зафиксировать в индексе (= I Этап их внесения). «git add -p» - это интерактивный инструмент, который спрашивает вас о том, какие изменения в файле следует добавить в индекс.

$ git add -p myfile
diff --git a/myfile b/myfile
index 93db4cb..2f113ce 100644
--- a/myfile
+++ b/myfile
@@ -1,3 +1,5 @@
+1
 something
 something else
 something again
+2
Stage this hunk [y,n,a,d,/,s,e,?]? s    # split this section into two!
Split into 2 hunks.
@@ -1,3 +1,4 @@
+1
 something
 something else
 something again
Stage this hunk [y,n,a,d,/,j,J,g,e,?]? y  # yes, I want to stage this
@@ -1,3 +2,4 @@
 something
 something else
 something again
+2
Stage this hunk [y,n,a,d,/,K,g,e,?]? n   # no, I don't want to stage this

Затем я фиксирую это первое изменение:

$ git commit -m "Added first line"
[master cef3d4e] Added first line
 1 files changed, 1 insertions(+), 0 deletions(-)

Теперь я могу зафиксировать все остальные изменения (а именно цифру «2», вставленную в последнюю строку):

$ git commit -am "Added last line"
[master 5e284e6] Added last line
 1 files changed, 1 insertions(+), 0 deletions(-)

Давайте проверим журнал, чтобы увидеть, какие коммиты у нас есть:

$ git log -p -n2 | cat
Commit 5e284e652f5e05a47ad8883d9f59ed9817be59d8
Author: ...
Date: ...

    Added last line

Diff --git a/myfile b/myfile
Index f9e1a67..2f113ce 100644
--- a/myfile
+++ b/myfile
@@ -2,3 +2,4 @@
 something
 something else
 something again
+2

Commit cef3d4e0298dd5d279a911440bb72d39410e7898
Author: ...
Date: ...

    Added first line

Diff --git a/myfile b/myfile
Index 93db4cb..f9e1a67 100644
--- a/myfile
+++ b/myfile
@@ -1,3 +1,4 @@
+1
 something
 something else
 something again
325
hcs42

Цели:

  • Я хочу разделить прошлый коммит (splitme) на два.
  • Я хочу сохранить сообщение о коммите .

План:

  1. перебазировать интерактив от одного до splitme
  2. редактировать splitme
  3. Сбросьте файлы, чтобы разделить их на второй коммит. 
  4. Исправьте коммит, сохраняя сообщение, измените при необходимости.
  5. Добавьте обратно файлы, выделенные из первого коммита.
  6. Зафиксируйте с новым сообщением. 
  7. Продолжайте ребаз. 

Шаги по перебазированию (1 и 7) можно пропустить, если splitme - самый последний коммит.

git rebase -i splitme^
# mark splitme commit with 'e'
git reset HEAD^ -- $files
git commit --amend
git add $files
git commit -m "commit with just some files"
git rebase --continue

Если бы я хотел, чтобы сплит-файлы были зафиксированы в первую очередь, я бы затем перебазировал -i и изменил порядок

git rebase -i splitme^
# swap order of splitme and 'just some files'
81
spazm

Чтобы изменить текущий коммит на два коммита, вы можете сделать что-то вроде следующего.

Или:

git reset --soft HEAD^

Это отменяет последний коммит, но оставляет все готовым. Затем вы можете удалить некоторые файлы:

git reset -- file.file

При желании переназначить части этих файлов:

git add -p file.file

Сделайте новый первый коммит:

git commit

Этап и зафиксируйте остальные изменения во втором коммите:

git commit -a

Или же:

Отмените и удалите все изменения из последнего коммита:

git reset HEAD^

Выборочно этап первого раунда изменений:

git add -p

Commit:

git commit

Зафиксируйте остальные изменения:

git commit -a

(На любом этапе, если вы отменили коммит, в который был добавлен новый файл, и хотите добавить его во второй коммит, вам придется добавить его вручную, так как commit -a только вносит изменения в уже отслеженные файлы.)

52
CB Bailey

Запустите git gui, выберите переключатель "Изменить последний коммит" и отмените удаление (Фиксация> Отключить от фиксации или Ctrl-U) изменения, которые вы не хотите вводить в первый коммит. Я думаю, что это самый простой способ сделать это.

Еще одна вещь, которую вы можете сделать, это выбрать изменения без фиксации (git cherry-pick -n), а затем вручную или с помощью git gui выбрать нужные изменения перед фиксацией.

22
Michael Krelin - hacker
git reset HEAD^

- жесткое то, что убивает ваши изменения.

15
semanticart

Я удивлен, что никто не предложил git cherry-pick -n forum. Это отменит изменения от последней фиксации forum, но не зафиксирует их - вы можете reset удалить изменения, которые вам не нужны, и зафиксировать то, что вы хотите сохранить.

13
dahlbyk

Метод двойного обратного сквоша

  1. Сделайте еще один коммит, который удалит нежелательные изменения. (Если это для каждого файла, это действительно просто: git checkout HEAD~1 -- files with unwanted changes и git commit. Если нет, файлы со смешанными изменениями могут быть частично подготовлены git reset file и git add -p file как промежуточный шаг.) Назовите это revert .
  2. git revert HEAD - сделать еще один коммит, который добавляет нежелательные изменения. Это двойной возврат
  3. Из 2 коммитов, которые вы сейчас сделали, squash первый на коммит для разделения (git rebase -i HEAD~3). Этот коммит теперь освобождается от нежелательных изменений, так как они находятся во втором коммите.

Выгоды

  • Сохраняет сообщение коммита
  • Работает, даже если коммит для split не последний. Требуется только, чтобы нежелательные изменения не конфликтовали с последующими коммитами
2
user2394284

Так как вы выбираете вишню, вы можете:

  1. cherry-pick это с добавленной опцией --no-commit.
  2. reset и используйте add --patch, add --edit или просто add, чтобы поставить то, что вы хотите сохранить.
  3. commit поэтапные изменения .
    • Чтобы повторно использовать исходное сообщение о фиксации, вы можете добавить опции --reuse-message=<old-commit-ref> или --reedit-message=<old-commit-ref> к команде commit.
  4. Сдавите неустановленные изменения с помощью reset --hard.

Другой способ - сохранить или отредактировать исходное сообщение о коммите:

  1. cherry-pick оригинальный коммит как обычно.
  2. Отмените изменения, которые вам не нужны, и используйте add для постановки аннулирования .
    • Этот шаг будет легким, если вы удаляете то, что вы добавили, но немного сложнее, если вы добавляете то, что удалили, или отменяете изменение.
  3. commit --amend, чтобы произвести реверс на выбранном вишне коммите .
    • Вы снова получите то же самое сообщение о коммите, которое вы можете сохранить или изменить при необходимости.
1
ADTC

Это может быть другое решение, предназначенное для случаев, когда существует огромный коммит, и небольшое количество файлов необходимо перенести в новый коммит. Это будет работать, если набор файлов <path> будет извлечен из последнего коммита в HEAD и все перемещены в новый коммит. Если требуется несколько коммитов, могут использоваться другие решения.

Сначала внесите исправления в поэтапную и непостановленную области, в которых будут содержаться изменения, к которым должен быть возвращен код до модификации и после модификации соответственно:

git reset HEAD^ <path>

$ git status
On branch <your-branch>
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   <path>

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   <path>

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

git diff --cached   -> show staged changes to revert <path> to before HEAD
git diff            -> show unstaged changes to add current <path> changes

Отменить изменения <path> в последнем коммите:

git commit --amend  -> reverts changes on HEAD by amending with staged changes

Создать новый коммит с изменениями <path>:

git commit -a -m "New Commit" -> adds new commit with unstaged changes

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

0
Jose Cifuentes