gitでコミットをスカッシュするとはどういう意味ですか?


117

Squashingがgitでコミットするとはどういう意味ですか。Githubでコミットをスカッシュするにはどうすればよいですか?

私はGitを初めて使用し、coala-analyzerの新しいバグに割り当てられるように要求しました。バグを修正しましたが、今度はコミットを潰すように言われました。どうすればいいのですか?


@Lakshmanさん、投票数の多い回答を自由に受け入れてください。それはあなたの質問に適切に答えます。
Mostafiz Rahman 2018

回答:


180

Gitは、作業ディレクトリのスナップショットの高度なデータベースと考えることができます。

Gitの非常に優れた機能の1つは、コミットの履歴を書き直す機能です。
これを行う主な理由は、そのような履歴の多くはそれを生成した開発者にのみ関連するため、共有リポジトリに送信する前に、簡略化するか、より優れたものにする必要があるためです。

コミット押しつぶすとは、慣用的な観点から、コミットに導入された変更をその親に移動して、2つ(またはそれ以上)ではなく1つのコミットになるようにすることを意味します。
このプロセスを複数回繰り返す場合、nコミットを1つに減らすことができます。

視覚的に、Startとタグ付けされたコミットで作業を開始した場合、これは

Gitがつぶしをコミットする

新しいコミットの色が少し濃い青色になっていることに気付くでしょう。これは意図的なものです。

Gitでの押しつぶしは、インタラクティブリベースと呼ばれる特別な形式のリベースで実現されます。 一連のコミットをブランチBにリベースするときに簡素化すると、元の祖先ではなくBから始めて、それらのコミットによって行われた変更がすべて適用されます。

視覚的な手がかり

ここに画像の説明を入力してください

青色の異なる色合いに再度注意してください。

インタラクティブなリベースでは、コミットをリベースする方法を選択できます。このコマンドを実行すると:

 git rebase -i branch

あなたはリベースされるコミットをリストしたファイルで終わるでしょう

 pick ae3...
 pick ef6...
 pick 1e0...
 pick 341...

私はコミットに名前を付けませんでしたが、これらの4つのものは、開始からヘッドへのコミットを意図しています

このリストの良いところは、編集可能であることです
コミットを省略したり、コミットすることができます
最初の単語をsquashに変更するだけです。

 pick ae3...
 squash ef6...
 squash 1e0...
 squash 341...

エディターを閉じてもマージの競合が見つからない場合は、次の履歴になります。

ここに画像の説明を入力してください

あなたの場合、別のブランチにリベースするのではなく、以前のコミットにリベースする必要があります。
最初の例に示すように履歴を変換するには、次のようなものを実行する必要があります

git rebase -i HEAD~4

最初のコミット以外のすべてのコミットについて「コマンド」をスカッシュに変更し、エディターを閉じます。


履歴の変更に関する注意

Gitでは、コミットは編集されません。それらは剪定、到達不可能、クローン作成は可能ですが変更はできません。
リベースすると、実際には新しいコミットが作成されます。
古いものは参照から到達できなくなっているため、履歴には表示されませんが、まだ残っています!

これは、実際にリベースで得られるものです。

ここに画像の説明を入力してください

すでにそれらをどこかにプッシュしている場合、履歴を書き換えると実際にブランチが作成されます!


ニース-元のコミットコメントはどうなりますか-それらは1つの大きなコミットコメントに結合されますか、それとも失われますか?
Paul R

From man git rebase折りたたまれたコミットに対して推奨されるコミットメッセージは、最初のコミットのコミットメッセージと「squash」コマンドを使用したコミットメッセージを連結したものです
Margaret Bloom

1
これは私がリベースについて見た中で最高の説明です、ありがとう。
ケリージョーンズ

最初の画像のスカッシュについて:スカッシュの前(水色)と後(水色)でHEADの作業ディレクトリが異なる例を作成できますか?すべての例で、押しつぶしてみたところ、STARTとHEADの間の3つのコミットが削除されたように見えました。
actual_panda

@actual_pandaフォローしているかわかりません。HEADはコミットのリファレンスです。ストアデルタをコミットします。作業ディレクトリは、全体としてリポジトリのプロパティであり、チェックアウトしたコミットによって異なります。一般に、2つの参照(HEADやSTARTなど)は、チェックアウト時に常に2つの異なるワークディレクトリを提供します。同じブランチでスカッシュをリベースする場合、効果は中間コミットを「失う」ことですが、実際には、gitはすべてのデルタを使用して新しいコミットを作成しました。git diff何が起こったかを示すのに役立つかもしれません。
マーガレットブルーム

22

rebaseコマンドには、その--interactive(または-i)モードで使用できる素晴らしいオプションがいくつかあり、最も広く使用されているものの1つは、コミットをスカッシュする機能です。これは、小さなコミットを取り、それらを大きなコミットに結合することです。これは、その日の作業を終える場合や、変更を別の方法でパッケージ化したい場合に役立ちます。これを簡単に行う方法について説明します。

注意:これは、外部リポジトリにプッシュされていないコミットでのみ実行してください。削除しようとしているコミットに基づいて他の人が作業を行っている場合、多くの競合が発生する可能性があります。他の人と共有している場合は、履歴を書き換えないでください。

それでは、いくつかの小さなコミットを作成し、それらから1つの大きなコミットを作成したいとします。リポジトリの履歴は現在、次のようになっています。

ここに画像の説明を入力してください

最後の4つのコミットがまとめられていれば、はるかに幸せになります。そのため、インタラクティブなリベースを通じてそれを実行しましょう。

$ git rebase -i HEAD~4

pick 01d1124 Adding license
pick 6340aaa Moving license into its own file
pick ebfd367 Jekyll has become self-aware.
pick 30e0ccb Changed the tagline in the binary, too.

# Rebase 60709da..30e0ccb onto 60709da
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

したがって、ここでいくつかのことが起こりました。まず、Gitに、HEADがHEAD〜4である場所からの最後の4つのコミットを使用してリベースすることを伝えました。Gitは、上記のテキストと、何ができるかについての簡単な説明を含むエディターに私を入れました。この画面から利用できるオプションはたくさんありますが、今はすべてを1つのコミットに押しつぶすだけです。したがって、ファイルの最初の4行をこれに変更すると、トリックが実行されます。

pick 01d1124 Adding license
squash 6340aaa Moving license into its own file
squash ebfd367 Jekyll has become self-aware.
squash 30e0ccb Changed the tagline in the binary, too.

基本的に、これは4つのコミットすべてをリストの最初のコミットに結合するようにGitに指示します。これが完了して保存されると、別のエディターがポップアップして次のようになります。

# This is a combination of 4 commits.
# The first commit's message is:
Adding license

# This is the 2nd commit message:

Moving license into its own file

# This is the 3rd commit message:

Jekyll has become self-aware.

# This is the 4th commit message:

Changed the tagline in the binary, too.

    # Please enter the commit message for your changes. Lines starting
    # with '#' will be ignored, and an empty message aborts the commit.
    # Explicit paths specified without -i nor -o; assuming --only paths...
    # Not currently on any branch.
    # Changes to be committed:
    #   (use "git reset HEAD <file>..." to unstage)
    #
    #   new file:   LICENSE
    #   modified:   README.textile
    #   modified:   Rakefile
    #   modified:   bin/jekyll
    #

非常に多くのコミットを組み合わせるため、Gitでは、プロセスに関与する残りのコミットに基づいて、新しいコミットのメッセージを変更できます。必要に応じてメッセージを編集し、保存して終了します。それが完了すると、コミットは無事に潰されました!

Created commit 0fc4eea: Creating license file, and making jekyll self-aware.
 4 files changed, 27 insertions(+), 30 deletions(-)
  create mode 100644 LICENSE
    Successfully rebased and updated refs/heads/master.

そして、もう一度歴史を見れば… ここに画像の説明を入力してください

したがって、これはこれまでのところ比較的無痛でした。リベース中に競合に遭遇した場合、通常、それらは非常に簡単に解決でき、Gitは可能な限りユーザーを導きます。これの基本は、問題の競合git add、ファイルを修正してからgit rebase --continue、プロセスを再開します。もちろん、a git rebase --abortを実行すると、必要に応じて以前の状態に戻ります。何らかの理由でリベースでコミットを失った場合、reflogを使用してそれを取り戻すことができます。

詳細はこのリンクをご覧ください


3
ありがとうございました!誰もが私と同じようにVIMに不快な場合は...編集するアクションを入力するポイントに到達したら、「i」を押して編集モードに入ります。変更が終わったら、エスケープを押してから「:wq」と入力すると、次のページに進みます。(Mac)
Farasi78

いつこれを行うかについての注意とコンテキストについては+1。私の個人的な経験では、機能ブランチで単独で作業していて、「更新されたreadme」、「誰も気にしていない何かをした」などのささいなコミットがあり、ブランチをマージする準備ができているとよいでしょう。これらすべてを1つのコミットに押しつぶすだけです。誰もあなたの「誰も気にしていないことをしましたか」に戻したくはなく、コミット履歴を汚します
Adam Hughes

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