歴史に埋もれたGitコミットを分割するにはどうすればよいですか?


292

私は自分の歴史を一掃し、それにいくつかの変更を加えたいと思っています。問題は、2つの無関係な変更を伴うコミットがあり、このコミットがローカル(プッシュされていない)履歴の他のいくつかの変更に囲まれていることです。

このコミットをプッシュする前に分割したいのですが、私が目にしているガイドのほとんどは、最新のコミットまたはコミットされていないローカル変更の分割に関係しています。それ以降、自分のコミットを「再実行」する必要なく、少し歴史に埋もれているコミットに対してこれを行うことは可能ですか?


回答:


450

リベースのマンページに、コミットを分割するためのガイドがあります。簡単な要約は次のとおりです。

  • ターゲットコミット(例:)を含むインタラクティブなリベースを実行し、git rebase -i <commit-to-split>^ branch編集対象としてマークします。

  • リベースがそのコミットに達したら、を使用git reset HEAD^してコミット前にリセットしますが、作業ツリーはそのままにしておきます。

  • 変更を段階的に追加してコミットし、必要なだけコミットします。add -p特定のファイルに変更の一部のみを追加するのに役立ちます。commit -c ORIG_HEAD特定のコミットで元のコミットメッセージを再利用する場合に使用します。

  • コミットしているものをテストしたい場合は(良いアイデアです!)コミットしgit stashていない(またはstash --keep-indexコミットする前に)部分を非表示にするには、テストしてgit stash popから、残りを作業ツリーに戻します。すべての変更がコミットされるまで、つまりクリーンな作業ツリーになるまで、コミットを続けます。

  • 実行git rebase --continueして、分割されたコミットの後でコミットの適用を続行します。


17
...しかし、分割のコミット以降の履歴をすでにプッシュしている場合は、それを行わないでください。
wilhelmtell 2010年

29
@wilhelmtell:OPがこの履歴をプッシュしていないことをOPが明示的に述べたため、私はいつもの「潜在的に危険です。「上流のリベースからの回復」を参照してください」というボイラープレートを省略しました。
Cascabel 2010年

2
そしてあなたは完璧な読書をしました。まだ履歴が共有されていないことを指定したとき、私は「ボイラープレート」を回避しようとしていました:)とにかく、私はあなたの提案で成功しました。しかし、事後にこのようなことをするのは大きな痛みです。私はここでレッスンを学びました、そしてそれはコミットが最初に正しく入れられることを確認することです!
ベンは

2
最初のステップは、としてより適切に記述できますgit rebase -i <sha1_of_the_commit_to_split>^ branch。またgit gui、分割タスクの優れたツールであり、ファイルのさまざまな部分をさまざまなコミットに追加するために使用できます。
Qiang Xu

3
@QiangXu:最初は合理的な提案です。2番目はまさに私が提案した理由ですgit add -p。これはgit gui、この部門でできる以上のことを実行できます(特に、ハンクの編集、現在のハンクから始まるすべてのステージング、正規表現によるハンクの検索)。
Cascabel 2012

3

Magitでこれを行う方法は次のとおりです。

変更したいのはed417aeコミットだと言ってください。2つの無関係な変更が含まれており、1つ以上のコミットの下に埋もれています。ヒットllしてログを表示し、ed417aeに移動します。

初期ログ

次に、ヒットrしてリベースポップアップを開きます

ポップアップをリベース

そしてm時点でコミット変更します。

@分割したいコミットがどのようになっているのかに注意してください。つまり、HEADは現在そのコミットにいます。

コミットの変更

HEADを親に移動したいので、親(47e18b3)に移動し、xmagit-reset-quicklyo使用している場合にバインドされているevil-magit)を押して、「はい、ポイントでのコミットを意味します」と入力します。ログは次のようになります。

リセット後のログ

さて、ヒットq後、定期的なunstage使用し、定期的なMagit状態に行くためにu、最初のコミットに行くコミットしていないものをunstageにコマンドをcいつものように残りの部分を、その後、s田下とcOMMITで何が起こっ二コミット、および完了時:ヒットrしてリベースポップアップを開く

ポップアップをリベース

もう1つr続行すると、完了です。ll今示しています:

すべて完了したログ


1

コミットを分割して、このコミットの前に新しいコミット<commit>を追加し、作成者の日付を保存するには、以下の手順に従います。<commit>

  1. にコミットを編集 <commit>

    git rebase -i <commit>^^
    

    注:おそらく編集も必要になります<commit>

  2. <commit>インデックスにチェリーピック

    git cherry-pick -n <commit>
    
  3. インデックスからインタラクティブに不要な変更をリセットし、作業ツリーをリセットします

    git reset -p && git checkout-index -f -a
    

    別の方法として、不要な変更をインタラクティブに隠しておくだけです。 git stash push -p -m "tmp other changes"

  4. その他の変更(ある場合)を行い、新しいコミットを作成する

    git commit -m "upd something" .
    

    オプションで、項目2〜4を繰り返して、中間コミットをさらに追加します。

  5. リベースを続行

    git rebase --continue
    

0

1つのファイルのみからコンテンツを抽出する場合は、より高速なバージョンがあります。インタラクティブなリベースは実際にはインタラクティブではないため、より高速です(もちろん、最後のコミットから抽出したい場合はさらに高速になり、リベースする必要がまったくありません)。

  1. エディタを使用して、抽出したい行を削除しますthe_file。閉じるthe_file。それだけが必要なエディションで、残りはすべてgitコマンドです。
  2. インデックスでその削除をステージングします。

    git  add  the_file
    
  3. インデックスに影響を与えずに、削除した行をファイルに復元します

    git show HEAD:./the_file > the_file
    
  4. 「SHA1」は、行を抽出するコミットです。

    git commit -m 'fixup! SHA1' 
    
  5. ステップ3で復元された抽出するコンテンツを使用して、2番目の新しいコミットを作成します。

    git commit -m 'second and new commit' the_file 
    
  6. 編集しないでください、停止/続行しないでください-すべてを受け入れてください:

    git rebase --autosquash -i SHA1~1
    

もちろん、抽出するコミットが最後のコミットである場合はさらに速くなります。

4. git commit -C HEAD --amend
5. git commit -m 'second and new commit' thefile
6. no rebase, nothing

使用する場合magit、ステップ4、5、6は単一のアクションです:コミット、即時修正


-2

まだプッシュしていない場合は、を使用してくださいgit rebase。さらに、git rebase -iコミットをインタラクティブに移動するために使用します。問題のコミットを前面に移動してから、必要に応じて分割し、必要に応じてパッチを元に戻すことができます。


14
どこにでも移動する必要はありません。それをある場所で分割します。
Cascabel 2010年

1
残念ながら、これは私にはうまくいきません。コミット後の履歴の一部はそれに依存しているため、少し制限されています。しかし、これが私の最初の選択でした。
ベン

@ベン:それは大丈夫です-コミットは後で変更する必要はまったくありません(変更の一部を破棄するのではなく、すべての変更を保持する場合)。ここでは詳細な情報- stackoverflow.com/questions/1440050/...
イーサ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.