以前のコミットを複数のコミットに分割する


1224

ブランチを作成し、新しいブランチで一連のファンキーな作業を行わずに、ローカルリポジトリにコミットした後で、単一のコミットをいくつかの異なるコミットに分割することはできますか?


36
これを行う方法を学ぶための良い情報源は、「コミットの分割」セクションにあるPro Git§6.4Gitツール-履歴の書き換えです。

2
上記のコメントでリンクされているドキュメントは優れており、以下の回答よりも詳しく説明されています。
Blaisorblade

2
このエイリアスstackoverflow.com/a/19267103/301717の使用をお勧めします。これは、使用してコミット分割することができますgit autorebase split COMMIT_ID
ジェローム・Pouiller

インタラクティブなリベースなしで実行する最も簡単なことは、(おそらく)分割するブランチの前のコミットで新しいブランチを作成し、チェリーピック-nコミット、リセット、スタッシュ、ファイル移動のコミット、スタッシュの再適用、変更をコミットしてから、以前のブランチとマージするか、その後のコミットを選択します。(そして、現在のヘッドに元支店名を切り替える。)(それはMBOsのアドバイスに従うと、対話型リベースを行う方が良いでしょうです。)(以下2010の答えからコピー)
ウィリアム・Pursell

1
以前のコミットのリベース中に2つのコミットを誤って押しつぶした後、この問題に遭遇しました。それを修正するための私の方法は、コミット押しつぶさチェックアウトすることでした、git reset HEAD~git stash、その後、git cherry-pickまず、スカッシュ以内にコミットgit stash pop。私のチェリーピックのケースはここではかなり具体的ですがgit stashgit stash pop他の人にとっては非常に便利です。
SOFe

回答:


1800

git rebase -i それを行います。

まず、クリーンな作業ディレクトリから始めgit statusます。保留中の変更、削除、追加が表示されないはずです。

ここで、分割するコミットを決定する必要があります。

A)最新のコミットを分割する

最新のコミットを分割するには、最初に:

$ git reset HEAD~

次に、通常の方法で各部分を個別にコミットし、必要な数のコミットを生成します。

B)コミットをさらに分割する

これには、リベース、つまり履歴の書き換えが必要です。正しいコミットを見つけるには、いくつかの選択肢があります。

  • それが3回のコミットであった場合、

    $ git rebase -i HEAD~3
    

    3コミットの数はどこにありますか。

  • カウントしたいよりもツリーの奥にある場合は、

    $ git rebase -i 123abcd~
    

    ここ123abcdで、分割するコミットのSHA1です。

  • マスターにマージする予定の別のブランチ(機能ブランチなど)を使用している場合:

    $ git rebase -i master
    

リベース編集画面が表示されたら、分解したいコミットを見つけます。その行の先頭で、置き換えるpickedite略して)。バッファを保存して終了します。編集したいコミットの直後にリベースが停止します。次に:

$ git reset HEAD~

通常の方法で各部分を個別にコミットし、必要な数のコミットを作成します。

$ git rebase --continue

2
この回答をありがとうございます。以前にコミットしたファイルをステージング領域に置きたかったので、手順は少し異なりました。私ができる前にgit rebase --continue、私は実際に持っていたgit add (files to be added)git commit、その後、git stash(残りのファイル用)。後git rebase --continue、私git checkout stash .は残りのファイルを取得するために使用しました
Eric Hu

18
manojldsの答えには実際にはgit-scmに関するドキュメントへのリンクがあり、コミットを分割するプロセスも非常に明確に説明されています。

56
またgit add -p、ファイルの一部のみを追加することも利用できます。おそらく、ediffを編集して、一部のハンクのみをコミットするオプションを使用できます。git stash作業を進めたいが現在のコミットから削除したい場合にも役立ちます。
クレイグリンガー、

2
コミットを分割して並べ替える場合は、最初に分割してから、別のgit rebase -i HEAD^3コマンドを使用して個別に並べ替えます。この方法では、分割がうまくいかなくても、それほど多くの作業を元に戻す必要はありません。
デビッドM.ロイド

4
@kralyk HEADで新たにコミットされたファイルは、ディスク上に残りgit reset HEAD~ます。それらは失われません。
ウェインコンラッド

312

以下からのgit-リベースマニュアル(分割コミットセクション)

対話モードでは、アクション「編集」でコミットをマークできます。ただし、これは、必ずしもgit rebaseがこの編集の結果を1つのコミットであると想定していることを意味するわけではありません。実際、コミットを取り消すことも、他のコミットを追加することもできます。これは、コミットを2つに分割するために使用できます。

  • 対話的なリベース起動しgit rebase -i <commit>^<commit>分割したいコミットされています。実際、そのコミットが含まれている限り、どのコミット範囲でも問題ありません。

  • アクション「編集」を使用して、分割するコミットをマークします。

  • そのコミットの編集については、を実行してくださいgit reset HEAD^。その効果は、HEADが1つ巻き戻され、インデックスがそれに追随することです。ただし、作業ツリーは同じままです。

  • ここで、最初のコミットで使用するインデックスに変更を追加します。あなたは使用することができますgit add(おそらく、対話形式)またはgit guiそれを行うために(あるいはその両方)。

  • 現在適切なコミットメッセージで現在のインデックスをコミットします。

  • 作業ツリーがきれいになるまで、最後の2つの手順を繰り返します。

  • でリベースを続行しgit rebase --continueます。


12
Windows ~では、の代わりに使用してください^
Kevin Kuszyk 16

13
注意:このアプローチでは、コミットメッセージを失いました。
user420667 16

11
@ user420667はい、もちろんです。reset結局のところ、私たちはコミットをティンティングしています-メッセージが含まれています。コミットを分割しようとしているのにそのメッセージの一部またはすべてを保持したい場合は、そのメッセージのコピーを取得するのが賢明です。したがって、git showコミットする前にrebase、またはこれを忘れた、または好む場合は、後でを介してコミットしてくださいreflog。2週間などでガベージコレクションされるまで、実際に「失われる」ことはありません。
underscore_d 2016

4
~そして、^さえWindows上で、異なるものです。まだキャレット^が必要なので、シェルに合わせてエスケープするだけで済みます。PowerShellではHEAD`^です。cmd.exeを使用すると、2倍にしてエスケープできますHEAD^^。ほとんどの(すべての)シェルでは、のように引用符で囲むことができます"HEAD^"
AndrewF 2018年

7
あなたも行うことができますgit commit --reuse-message=abcd123。短いオプションは-Cです。
j0057

41

使用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ウェル(以下、Iの使用のように見えます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

2番目のコミットを分割したいとしましょう。 two

git rebase --interactive HEAD~2

これにより、次のようなメッセージが表示されます。

pick 2b613bc two
pick bfb8e46 three

最初のものpickをに変更して、eそのコミットを編集します。

git reset HEAD~

git diff 2番目のコミットのために行ったコミットのステージングを解除したことを示しています。

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

その変更をステージングして、「と3番目」をfileのその行に追加します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

38

以前の回答では、を使用git rebase -iして分割するコミットを編集し、部分的にコミットする方法を説明しました。

これは、ファイルを異なるコミットに分割するときにうまく機能しますが、個々のファイルへの変更を分解したい場合は、知っておくべきことがさらにあります。

分割したいコミットに到達し、rebase -iそれをに使用してマークするとedit、2つのオプションがあります。

  1. を使用した後、各コミットで必要なgit reset HEAD~パッチgit add -pを選択するためにを使用して、パッチを個別に調べます

  2. 作業コピーを編集して、不要な変更を削除します。その中間状態をコミットします。そして、次のラウンドのコミット全体を引き戻します。

オプション2は、大きなコミットを分割する場合に役立ちます。これにより、中間バージョンがマージの一部として正しくビルドおよび実行されることを確認できます。これは次のように進行します。

コミットを使用rebase -iしてeditINGした後、

git reset --soft HEAD~

コミットを取り消すが、コミットされたファイルはインデックスに残します。また、最初のコミットが最終結果にどの程度近づくかに応じて、-softを省略して混合リセットを実行することもできます。唯一の違いは、ステージングされたすべての変更から始めるか、ステージングされていないすべての変更から始めるかです。

さあ、コードを編集してください。変更を削除したり、追加したファイルを削除したり、探しているシリーズの最初のコミットを構築するために何をしたりしてもかまいません。また、ビルドして実行し、一貫したソースのセットがあることを確認することもできます。

満足したら、必要に応じてファイルをステージング/ステージング解除し(私git guiはこれに使用します)、UIまたはコマンドラインから変更をコミットします

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 .

最後のドットをお見逃しなく!

これにより、編集中のコミット後のファイルがチェックアウトされ、ステージングされますが、以前に行ったコミットと比較して、すでにコミットした変更はコミットの一部にはなりません。

ここで先に進んでそのままコミットして分割を完了するか、もう一度やり直して別の暫定コミットを行う前にコミットの一部を削除することができます。

元のコミットメッセージを1つまたは複数のコミットに再利用する場合は、リベースの作業ファイルから直接使用できます。

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

最後に、すべての変更をコミットしたら、

git rebase --continue

リベース操作を続行して完了します。


3
ありがとうございました!!!これは受け入れられる答えになるはずです。今日、私に多くの時間と苦痛を救わせたでしょう。最終的なコミットの結果が編集中のコミットと同じ状態になる唯一の答えです。
Doug Coburn 2017年

1
元のコミットメッセージの使い方が気に入っています。
サラマンダー

オプション2を使用する場合、私がgit checkout *Sha I'm Editing* .それを行うときは常にUpdated 0 paths from *Some Sha That's Not In Git Log*、変更はありません。
Noumenon

18

git rebase --interactiveコミットを小さなコミットに分割するために使用できます。リベースGitドキュメントには、プロセスの簡潔なウォークスルーがあります-コミットの分割

対話モードでは、アクション「編集」でコミットをマークできます。ただし、これは必ずしもgit rebase、この編集の結果が1つのコミットになることを期待していることを意味するものではありません。実際、コミットを取り消すことも、他のコミットを追加することもできます。これは、コミットを2つに分割するために使用できます。

  • 対話的なリベース起動しgit rebase -i <commit>^<commit>分割したいコミットされています。実際、そのコミットが含まれている限り、どのコミット範囲でも問題ありません。

  • アクション「編集」を使用して、分割するコミットをマークします。

  • そのコミットの編集については、を実行してくださいgit reset HEAD^。その効果は、HEADが1つ巻き戻され、インデックスがそれに追随することです。ただし、作業ツリーは同じままです。

  • ここで、最初のコミットで使用するインデックスに変更を追加します。これを行うにはgit add(おそらくインタラクティブに)またはgit gui(またはその両方)を使用できます。

  • 現在適切なコミットメッセージで現在のインデックスをコミットします。

  • 作業ツリーがきれいになるまで、最後の2つの手順を繰り返します。

  • でリベースを続行しgit rebase --continueます。

中間リビジョンの整合性が確実にわからない場合(コンパイル、テストスイートに合格するなど)、git stashコミットのたびに、まだコミットされていない変更を隠し、テストし、修正が必要な場合はコミットを修正する必要があります。 。


Windowsでは、^コマンドラインのエスケープ文字を忘れないでください。2倍にする必要があります。たとえば、git reset HEAD^^ではなくを発行しますgit reset HEAD^
フレデリック

@Frédéric:s私はこれに遭遇したことがありません。少なくともPowerShellではそうではありません。次に、^2回使用すると、現在のHEADより上の2つのコミットがリセットされます。
ファーウェイ2018年

@Farway、従来のコマンドラインで試してください。PowerShellはまったく別の獣であり、そのエスケープ文字はバックチルトです。
フレデリック

要約すると"HEAD^"、cmd.exeまたはPowerShell、HEAD^^cmd.exe、HEAD`^PowerShellです。シェル(および特定のシェル)がどのように機能するか(つまり、コマンドがプログラムに渡される個々の部分にどのように変換されるか)を理解しておくと、特定のシェルに適した文字にコマンドをオンラインで適合させることができます。(Windowsに固有ではありません。)
AndrewF 2018年

11

Windows上の最新のTortoiseGitでは、非常に簡単に実行できます。

リベースダイアログを開いて設定し、次の手順を実行します。

  • 分割するコミットを右クリックし、「Edit」を選択します(選択、スカッシュ、削除など)。
  • Start」をクリックして、リベースを開始します。
  • 分割するコミットに到達したら、「Edit/Split」ボタンをチェックして「Amend」を直接クリックします。コミットダイアログが開きます。
    コミットの編集/分割
  • 別のコミットに配置するファイルの選択を解除します。
  • コミットメッセージを編集して、「commit」をクリックします。
  • コミットするファイルができるまで、コミットダイアログが何度も開きます。コミットするファイルがなくなっても、コミットをもう1つ追加するかどうかを尋ねられます。

TortoiseGitに感謝します。



8

もございますのでご了承くださいgit reset --soft HEAD^。これはgit reset(デフォルトでは--mixed)いますが、インデックスの内容を保持します。そのため、ファイルを追加/削除した場合、それらはすでにインデックスに含まれています。

巨大なコミットの場合に非常に役立つことがわかりました。


3

IntelliJ IDEAPyCharmPhpStormなどで1つのコミットを分割する方法は次のとおりです

  1. バージョン管理のログウィンドウで、分割するコミットを選択し、右クリックして、 Interactively Rebase from Here

  2. 分割するものにマークを付けedit、クリックしますStart Rebasing

  3. 黄色のタグが配置され、HEADがそのコミットに設定されていることを確認します。そのコミットを右クリックして選択しますUndo Commit

  4. これらのコミットはステージング領域に戻ったので、個別にコミットできます。すべての変更がコミットされると、古いコミットは非アクティブになります。


2

インタラクティブなリベースなしで実行する最も簡単なことは、(おそらく)分割するブランチの前のコミットで新しいブランチを作成し、チェリーピック-nコミット、リセット、スタッシュ、ファイル移動のコミット、スタッシュの再適用、変更をコミットしてから、以前のブランチとマージするか、その後のコミットを選択します。(その後、以前のブランチ名を現在のヘッドに切り替えます。)(MBOのアドバイスに従い、インタラクティブなリベースを実行することをお勧めします。)


最近のSOの標準によれば、これは回答なしと見なされます。しかし、これはまだ他の人にとって役立つ可能性があるので、よろしければ、これを元の投稿のコメントに移動してください
YakovL

@YakovL妥当なようです。最小限のアクションの原則として、私は答えを削除しませんが、他の誰かがそうしたとしても私は反対しません。
ウィリアムパーセル

これはすべてのrebase -i提案よりもはるかに簡単です。ただし、書式が不足しているため、あまり注目されていなかったと思います。126kポイントがあり、おそらくSOの方法を知っているので、たぶんそれをレビューするかもしれません。;)
erikbwork


1

これがある場合:

A - B <- mybranch

コミットBでコンテンツをコミットした場所:

/modules/a/file1
/modules/a/file2
/modules/b/file3
/modules/b/file4

しかし、BをC-Dに分割して、次の結果を得たいとします。

A - C - D <-mybranch

たとえば、次のようにコンテンツを分割できます(異なるコミットの異なるディレクトリのコンテンツ)...

分岐する前に、ブランチをコミットにリセットします。

git checkout mybranch
git reset --hard A

最初のコミットを作成(C):

git checkout B /modules/a
git add -u
git commit -m "content of /modules/a"

2番目のコミットを作成(D):

git checkout B /modules/b
git add -u
git commit -m "content of /modules/b"

Bを超えるコミットがある場合はどうなりますか?
CoolMind 2017

1

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:が表示され、リポジトリがこれらすべてのファイルをコミットしようとしているような状態になります。これからは、通常どおり、簡単に再度コミットできます。それが役に立てば幸い。


0

必要なコマンドのクイックリファレンス。基本的に何をすべきかはわかっていますが、常に正しい構文を忘れているためです。

git rebase -i <sha1_before_split>
# mark the targeted commit with 'edit'
git reset HEAD^
git add ...
git commit -m "First part"
git add ...
git commit -m "Second part"
git rebase --continue

Emmanuel Bernardのブログ投稿のクレジット。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.