最後のXをGitを使用して一緒にコミットする


3589

Gitを使用して、最後のXコミットを1つのコミットにまとめる方法を教えてください。




2
@matt TortoiseGitはあなたのツールです。バックグラウンドですべてのステップを自動的に呼び出す単一の関数「Combine to one commit」を提供します。残念ながらWindowsでのみ利用可能です。以下の私の答えを参照してください。
Matthias M

1
-件まで退治のために、最初にこの参照コミットstackoverflow.com/questions/1657017/...
goelakash

1
力のプッシュを行うにスカッシュ1の必要性ポストstackoverflow.com/questions/10298291/...
vikramvi

回答:


2091

マニュアルでgit rebase -i <after-this-commit>説明されいるように 2回目以降のコミットで「pick」を使用し、「squash」または「fixup」で置き換えます

この例で<after-this-commit>は、SHA1ハッシュまたは現在のブランチのHEADからの相対位置のいずれかであり、rebaseコマンドでコミットが分析されます。たとえば、ユーザーが過去に現在のHEADから5つのコミットを表示したい場合、コマンドはgit rebase -i HEAD~5です。


260
これは、私が思うに、この質問に少し良く答えます。stackoverflow.com
Roy Truelove

92
はどういう意味<after-this-commit>ですか?
2540625 14年

43
<after-this-commit>X + 1をコミットします。つまり、スカッシュしたい最も古いコミットの親です。
joozek 2014年

339
この回答は簡潔すぎて、一義的に理解できませんでした。例が役立つでしょう。
Ian Ollmann

54
このrebase -iアプローチとの違いreset --softは、rebase -iコミットの作成者を保持reset --softできる一方で、再コミットできることです。時々私はプルリクエストのコミットを押しつぶしながら、作者の情報を維持する必要があります。自分のコミットでソフトをリセットする必要がある場合があります。とにかく両方の素晴らしい答えに賛成票を投じます。
zionyx

3832

あなたはせずに、かなり簡単にこれを行うことができますgit rebasegit merge --squash。この例では、最後の3つのコミットを押しつぶします。

新しいコミットメッセージを一から書きたい場合は、これで十分です。

git reset --soft HEAD~3 &&
git commit

既存のコミットメッセージを連結して新しいコミットメッセージの編集を開始する場合(つまり、pick / squash / squash /…/ squashのgit rebase -i指示リストから開始する場合と同様)、それらのメッセージを抽出して渡す必要があります。それらにgit commit

git reset --soft HEAD~3 && 
git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"

これらの方法はどちらも、最後の3つのコミットを同じ方法で1つの新しいコミットに押しつぶします。ソフトリセットは、スカッシュしたくない最後のコミットにHEADを再ポイントするだけです。インデックスも作業ツリーもソフトリセットの影響を受けず、インデックスは新しいコミットの望ましい状態のままになります(つまり、「破棄しようとしている」コミットからのすべての変更がすでにあります)。


163
ハ!私はこの方法が好きです。問題の精神に近いものです。それは多くのブードゥー教を必要とするのは残念です。このような何かを基本的なコマンドの1つに追加する必要があります。たぶんgit rebase --squash-recent、あるいはgit commit --amend-many
Adrian Ratnapala 2013年

13
@ABB:ブランチに「上流」セットがある場合、使用できる可能性がありますbranch@{upstream}(または@{upstream}現在のブランチのみ。どちらの場合も、最後の部分はに省略できます。gitrevisionsを@{u}参照してください)。これは「最後にプッシュされたコミット」とは異なる場合があります(たとえば、誰かが最新のプッシュの上に構築された何かをプッシュし、それをフェッチした場合)、それはあなたが望むものに近いようです。
クリスジョンセン2014

104
このちょっとしたpush -fことは私に必要でしたが、そうでなければそれは素敵でした、ありがとう。
2rs2ts 2014年

39
@ 2rs2ts git push -fは危険です。ローカルコミットのみを潰すように注意してください。プッシュされたコミットには触れないでください!
Matthias M

20
私も使用する必要があります git push --force、その後それがコミットかかること
ザックSaucier

740

git merge --squashこれに使用できますgit rebase -i。これは、よりも少しエレガントです。あなたがマスターであり、最後の12のコミットを1つに潰したいとします。

警告:最初に作業をコミットしてください— git statusクリーンであることを確認してください(git reset --hardステージングされた変更とアンステージングされた変更が破棄されるため)

次に:

# Reset the current branch to the commit just before the last 12:
git reset --hard HEAD~12

# HEAD@{1} is where the branch was just before the previous command.
# This command sets the state of the index to be as it would just
# after a merge from that commit:
git merge --squash HEAD@{1}

# Commit those squashed changes.  The commit message will be helpfully
# prepopulated with the commit messages of all the squashed commits:
git commit

ドキュメントでgit mergeは、--squashオプションについて詳しく説明しています。


更新:この方法の唯一の本当の利点git reset --soft HEAD~12 && git commit、Chris Johnsenが彼の回答で提案つぶしているすべてのコミットメッセージが事前に入力されたコミットメッセージが得られることです。


16
あなたはこれがよりエレガントであると言いgit rebase -iますが、理由を述べていません。これを暫定的に-1にすると、実際にはその逆が真実であり、これはハックであると思われます。特別に設計された機能のgit merge1つを強制するために、必要以上のコマンドを実行していませんgit rebaseか?
マークアメリー2013

77
@Mark Amery:これがよりエレガントだと言った理由はいくつかあります。たとえば、不必要にエディタを起動し、「to-do」ファイル内の文字列を検索して置換する必要はありません。使い方はgit merge --squashまた、スクリプトで使用する方が簡単です。基本的に、その理由はgit rebase -i、このための「対話性」はまったく必要ないということでした。
Mark Longair 2013

12
もう1つの利点はgit merge --squash、特にローカルブランチからマージする場合は、リベースと比較して、移動、削除、名前変更の前にマージの競合が発生する可能性が低いことです。(免責事項:唯一の経験に基づいて、これが一般的なケースでは当てはまらない場合は私を修正してください!)
Cheezmeister '27 / 02/27

2
それは、ハードリセットに来るとき、私はいつも非常に消極的だ-私は一時的なタグを使用したいの代わりに、HEAD@{1}あなたのワークフローが停電などにより時間中断されたときだけ、安全側の例えば上にあるように
トビアスKienzler

8
@BT:コミットを破棄しましたか?:(それが何を意味しているのかはわかりません。コミットしたものはすべて、gitのreflogから簡単に戻ることができます。コミットされていない作業があったが、ファイルがステージングされていたとしても、それらの内容は戻ってきますが、それはより多くの作業になります。ただし、あなたの作業が上演されなかった場合でも、私にはできることはほとんどないので、答えは前もって言っています:「最初にgitステータスがクリーンであることを確認してください(git reset --hardがステージングされた変更とアンステージングされた変更を破棄するため) "
Mark Longair

218

git reset特にGit初心者は、可能な限り回避することをお勧めします。本当に多くのコミットに基づいてプロセスを自動化する必要がない限り、それほど珍しい方法はありません...

  1. 押しつぶされるコミットを作業ブランチに配置します(まだコミットされていない場合)-これにはgitkを使用します
  2. 対象のブランチをチェックしてください(例: 'master')
  3. git merge --squash (working branch name)
  4. git commit

コミットメッセージはスカッシュに基づいて事前に入力されます。


4
これは最も安全な方法です:ソフト/ハード(!!)のリセットなし、またはreflogを使用しない!
TeChn4K 2016年

14
(1)を拡張するといいですね。
アダム

2
@Adam:基本的に、これはのGUIインターフェイスを使用して、つぶしているgitkコード行にラベルを付け、つぶしのベースにもラベルを付けることを意味します。通常の場合、これらのラベルは両方ともすでに存在するため、ステップ(1)はスキップできます。
nobar 2017

3
このメソッドは作業ブランチを完全にマージされたものとしてマークしないため、削除するには強制的に削除する必要があることに注意してください。:(
キルステレーヌ

2
(1)についてはgit branch your-feature && git reset --hard HEAD~N、最も便利な方法を見つけました。ただし、これにはgit resetが再度含まれるため、この回答は回避しようとしました。
e's

134

クリスジョンセンの答えに基づいて、

bash:(またはWindowsのGit Bash)からグローバルな「スカッシュ」エイリアスを追加します。

git config --global alias.squash '!f(){ git reset --soft HEAD~${1} && git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"; };f'

...またはWindowsのコマンドプロンプトを使用:

git config --global alias.squash "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"


あなた~/.gitconfigは今このエイリアスを含むべきです:

[alias]
    squash = "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"


使用法:

git squash N

...これは、最後のNコミットを包括的に自動的につぶします。

注:結果のコミットメッセージは、すべての押しつぶされたコミットを順番に組み合わせたものです。これに不満がある場合は、いつでもgit commit --amend手動で変更できます。(または、好みに合わせてエイリアスを編集します。)


6
興味深いですが、押しつぶされたコミットメッセージは、複数のコミットの説明的な要約として、自分で入力するよりも自分で入力する方がはるかに便利です。だから私はむしろプッシュされていないコミットの数として自動的に特定git squash -m "New summary."してN決定したいと思います。
Acumenus 2014

1
@ABB、これは別の質問のように聞こえます。(私はそれがOPが求めていたものとは正確には思いません
。gitsquash

3
これはかなり甘いです。個人的には、まとめられた最初のコミットからのコミットメッセージを使用するバージョンが欲しいです。ホワイトスペースの調整などに適しています。
funroll 2014

@funroll同意した。最後のコミットメッセージを単にドロップすることは、私にとって非常に一般的なニーズです。我々はそれを工夫することができるはずです...
スティーブクレイ

2
@ABBを使用git commit --amendしてメッセージをさらに変更できますが、このエイリアスを使用すると、コミットメッセージの内容を適切に開始できます。
ダッシュ1995

131

この便利なブログ投稿のおかげで、このコマンドを使用して最後の3つのコミットを押しつぶすことができることがわかりました。

git rebase -i HEAD~3

これは、追跡情報/リモートリポジトリのないローカルブランチにいる場合でも機能するので便利です。

このコマンドはインタラクティブなリベースエディターを開き、通常どおりに並べ替え、スカッシュ、リワードなどを行うことができます。


インタラクティブなリベースエディターの使用:

インタラクティブなリベースエディターは、最後の3つのコミットを表示します。この制約はHEAD~3、コマンドの実行時に決定されましたgit rebase -i HEAD~3

最新のコミットはHEAD、1行目に最初に表示されます。aで始まる行#はコメント/ドキュメントです。

表示されるドキュメントはかなり明確です。任意の行で、コマンドをから任意のコマンドに変更できpickます。

fixup上の行のコミットにコミットの変更を「押しつぶし」、コミットのメッセージを破棄するので、コマンドを使用することを好みます。

1行目のコミットはなのでHEAD、ほとんどの場合、そのままにしておきますpicksquashまたはfixupコミットを押しつぶす他のコミットがないので、使用することはできません。

コミットの順序を変更することもできます。これにより、時間的に隣接していないコミットをスカッシュまたは修正することができます。

インタラクティブなリベースエディター


実用的な日常の例

最近、新しい機能をコミットしました。それ以来、2つのバグ修正をコミットしました。しかし、私はコミットした新機能にバグ(またはおそらくスペルミス)を発見しました。なんてうるさい!コミット履歴を汚染する新しいコミットが必要ない!

私が最初にやることは間違いを修正し、コメントで新しいコミットをすることです squash this into my new feature!です。

次に、git logまたはgitkを実行して、新機能のコミットSHAを取得します(この場合1ff9460)。

次に、でインタラクティブなリベースエディターを起動しgit rebase -i 1ff9460~ます。~コミット後SHAは、エディタにコミットそれ含めるエディタに指示します。

次に、修正(fe7f1e0)を含むコミットを機能コミットの下に移動し、に変更pickfixupます。

エディターを閉じると、修正により機能のコミットが抑制され、コミット履歴がきれいに表示されます。

これはすべてのコミットがローカルである場合にうまく機能しますが、すでにリモートにプッシュされているコミットを変更しようとすると、同じブランチをチェックアウトしている他の開発者に本当に問題を引き起こす可能性があります!

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


5
あなたは一番上のものを選び、残りをつぶす必要がありますか?回答を編集して、インタラクティブリベースエディターの使用方法をより詳細に説明する必要があります
Kolob Canyon

2
はい、pick1行目のままにします。1行目のコミットを選択したsquash場合fixup、またはgitの場合、gitは「エラー:以前のコミットがないと「修正」できません」というメッセージを表示します。次に、それを修正するオプションが表示されます。「これは「git rebase --edit-todo」で修正してから、「git rebase --continue」を実行できます。」または、中止して最初からやり直すこともできます:「または、 'git rebase --abort'を使用してリベースを中止できます。」
br3nt 2018

56

TortoiseGitを使用すると、次の機能を実行できますCombine to one commit

  1. TortoiseGitコンテキストメニューを開く
  2. 選択する Show Log
  3. ログビューで関連するコミットをマークします
  4. Combine to one commitコンテキストメニューから選択

コミットを組み合わせる

この関数は、必要な単一のgitステップをすべて自動的に実行します。残念ながらWindowsでのみ利用できます。


私の知る限り、これはマージコミットでは機能しません。
Thorkil Holm-Jacobsen 2017

1
他の人からはコメントされていませんが、HEADにないコミットでも機能します。たとえば、私の必要性は、プッシュする前に、より健全な説明で行ったいくつかのWIPコミットを押しつぶすことでした。美しく働いた。もちろん、私はまだコマンドでそれを行う方法を学ぶことができることを願っています。
Charles Roberto Canato

55

これを行うには、次のgitコマンドを使用できます。

 git rebase -i HEAD~n

n(= 4 here)は最後のコミットの数です。次に、次のオプションがあります、

pick 01d1124 Message....
pick 6340aaa Message....
pick ebfd367 Message....
pick 30e0ccb Message....

以下のようにpick1つのコミットとsquash他のコミットを最新のものに更新します。

p 01d1124 Message....
s 6340aaa Message....
s ebfd367 Message....
s 30e0ccb Message....

詳細については、リンクをクリックしてください


48

この記事に基づいて私はこの方法が私のユースケースにとってより簡単であることを発見しました。

私の 'dev'ブランチは 'origin / dev'より96コミット進んでいました(これらのコミットはまだリモートにプッシュされていません)。

変更をプッシュする前に、これらのコミットを1つにまとめたいと思いました。ブランチを 'origin / dev'の状態にリセットし(これにより、96コミットからのすべての変更がステージングされないままになります)、変更を一度にコミットすることを好みます。

git reset origin/dev
git add --all
git commit -m 'my commit message'

1
ちょうど私が必要としたもの。私の機能ブランチからコミットをスカッシュダウンしてから、そのコミットをマスターにGit Cherry Pickします。
デビッドビクター

1
これは以前のコミットを破棄しません!
IgorGanapolsky 2015年

@igorGanapolskyについてもう少し詳しく説明してもらえますか?
trudolf 2015

3
@trudolfこれは、実際に押しつぶすことではありません(押しつぶすための個別のコミットを選択)。これは、すべての変更を一度にコミットすることを意味します。
IgorGanapolsky

15
はい、そのため、すべてのコミットを1つに押しつぶします。おめでとう!
trudolf

45

コミットを組み合わせるブランチで、次のコマンドを実行します。

git rebase -i HEAD~(n number of commits back to review)

例:

git rebase -i HEAD~1

これによりテキストエディターが開き、これらのコミットをマージする場合は、各コミットの前にある「ピック」を「スカッシュ」に切り替える必要があります。ドキュメントから:

p、pick =コミットを使用

s、squash =コミットを使用しますが、以前のコミットにマージします

たとえば、すべてのコミットを1つにマージする場合は、「ピック」が最初のコミットであり、将来のすべてのコミット(最初の下に配置)は「スカッシュ」に設定する必要があります。vimを使用している場合は、挿入モードで:xを使用して、エディターを保存して終了します。

次に、リベースを続行するには:

git rebase --continue

これとコミット履歴を書き換える他の方法の詳細については、この役立つ投稿を参照してください


2
--continueand vimの:x機能についても説明してください。
not2qubit 2018年

次のコミットに移動してマージを開始するためにgit add使用git rebase --continueするファイルの構成が正しい場合、ブランチのコミットを通過するときに、リベースがブロックで発生します。 :xvimを使用するときにファイルの変更を保存するコマンドの1つです。これを
aabiro

32

Anomiesの回答は良いですが、私はこれに不安を感じたので、スクリーンショットをいくつか追加することにしました。

ステップ0:gitログ

あなたがいる場所を参照してくださいgit log。最も重要なのは、スカッシュしたくない最初のコミットのコミットハッシュを見つけることです。だから:

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

ステップ1:git rebase

git rebase -i [your hash]私の場合、実行します:

$ git rebase -i 2d23ea524936e612fae1ac63c95b705db44d937d

ステップ2:必要なものを選択する/つぶす

私の場合、最初にコミットしたすべてのものを押しつぶしたいと思います。順序は最初から最後までなので、とまったく逆git logです。私の場合、私は欲しい:

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

ステップ3:メッセージを調整する

1つのコミットのみを選択して残りを押しつぶした場合は、1つのコミットメッセージを調整できます。

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

それでおしまい。これ(:wq)を保存すると、完了です。でそれを見てくださいgit log


2
最終結果を見るとよいでしょう。たとえば、git log
ティモシーLJスチュワート

2
結構です。これがまさに私がコミットをIostする方法です。
Axalix

@Axalixすべての行を削除しましたか?それはあなたがあなたのコミットを失う方法です。
a3y3

31

手順1

1)コミットの短いハッシュを特定する

# git log --pretty=oneline --abbrev-commit
abcd1234 Update to Fix for issue B
cdababcd Fix issue B
deab3412 Fix issue A
....

ここでもgit log --oneline短いハッシュを取得するために使用できます。

2)最後の2つのコミットをスカッシュ(マージ)する場合

# git rebase -i deab3412 

3)これによりnano、マージ用のエディターが開きます。そして、それは以下のように見えます

....
pick cdababcd Fix issue B
pick abcd1234 Update to Fix for issue B
....

4)ワードの名前を変更pickするsquash前に存在しているがabcd1234。名前を変更すると、次のようになります。

....
pick cdababcd Fix issue B
squash abcd1234 Update to Fix for issue B
....

5)次に、nanoエディターを保存して閉じます。を押してctrl + o、を押しEnterて保存します。次に、を押しctrl + xてエディターを終了します。

6)次にnano、必要に応じて更新して、コメントを更新するためのエディターを再度開きます。

7)これで正常につぶれたので、ログを確認することで確認できます。

# git log --pretty=oneline --abbrev-commit
1122abcd Fix issue B
deab3412 Fix issue A
....

8)リポジトリにプッシュします。+ブランチ名の前に記号を追加することに注意してください。これは強制プッシュを意味します。

# git push origin +master

注:これはubuntuシェルでのgitの使用に基づいています。異なるos(WindowsまたはMac)を使用している場合、上記のコマンドはエディターを除いて同じです。別のエディターが表示される場合があります。

手順2

  1. まず、コミットに必要なファイルを追加します
git add <files>
  1. 次に、--fixupオプションを使用してコミットすると、OLDCOMMITこのコミットをマージ(スカッシュ)する必要があります。
git commit --fixup=OLDCOMMIT

これで、HEADの上に新しいコミットが作成されfixup1 <OLDCOMMIT_MSG>ます。

  1. 次に、以下のコマンドを実行して、新しいコミットをにマージ(スカッシュ)しOLDCOMMITます。
git rebase --interactive --autosquash OLDCOMMIT^

ここでは、^以前はにコミットすることOLDCOMMIT。このrebaseコマンドは、エディター(vimまたはnano)で対話式ウィンドウを開き、保存するだけで終了するだけで十分です。これに渡されたオプションは、最新のコミットを古いコミットの隣に自動的に移動し、操作をfixup(スカッシュと同等)に変更するためです。その後、リベースが続行して終了します。

手順3

  1. 最後のコミットに新しい変更を追加する必要がある場合は、--amendで使用できますgit-commit
    # git log --pretty=oneline --abbrev-commit
    cdababcd Fix issue B
    deab3412 Fix issue A
    ....
    # git add <files> # New changes
    # git commit --amend
    # git log --pretty=oneline --abbrev-commit
    1d4ab2e1 Fix issue B
    deab3412 Fix issue A
    ....  

ここで--amendは、最後のコミットへの新しい変更をマージし、cdababcd新しいコミットIDを生成します1d4ab2e1

結論

  • 最初の手順の利点は、複数のコミットを押しつぶして並べ替えることです。しかし、非常に古いコミットの修正をマージする必要がある場合、この手順は困難になります。
  • したがって、2番目の手順は、コミットを非常に古いコミットに簡単にマージするのに役立ちます。
  • そして、3番目の手順は、最後のコミットに対する新しい変更を破棄する場合に役立ちます。

6番目の最後のコミットのコミットIDにリセットしても、最後の2つのコミットのみが更新されます。理由はわかりません
Carlos Liu

コミットの順序を並べ替えることもできます。正常に動作します。
rashok 2018

29

最後の10個のコミットを1つのコミットにつぶすには:

git reset --soft HEAD~10 && git commit -m "squashed commit"

破棄されたコミットでリモートブランチも更新する場合:

git push -f

--forceは、ローカルコピーでリモートで盲目的に更新するため、複数の人が共有ブランチで作業している場合は危険です。--force-with-leaseは、最後にフェッチしてからリモートが他のユーザーからのコミットを行わないようにするため、より優れている可能性があります。
バーラト

27

feature-branchゴールデンリポジトリ(golden_repo_name)から複製されたリモートブランチ(と呼ばれます)を使用している場合、コミットを1つにまとめる方法は次のとおりです。

  1. ゴールデンリポジトリをチェックアウトする

    git checkout golden_repo_name
    
  2. 次のように、そこから新しいブランチを作成します(ゴールデンレポ)。

    git checkout -b dev-branch
    
  3. すでに持っているローカルブランチとスカッシュマージ

    git merge --squash feature-branch
    
  4. 変更をコミットします(これは、開発ブランチで行われる唯一のコミットになります)

    git commit -m "My feature complete"
    
  5. ブランチをローカルリポジトリにプッシュする

    git push origin dev-branch
    

(git-svnを介してsvnブランチを同期するために)〜100のコミットを押しつぶしているだけなので、対話的にリベースするよりもはるかに高速です!
セージ

1
下を読むと、@ Chrisのコメントが表示されます。これは、私が以前使用していたものです(rebase --soft ...)-スタックオーバーフローが何百もの賛成票を上にして答えを返さないほど残念です...
sage

1
@sageに同意します。将来的にはそうなることを願っています
Sandesh Kumar

これは正しい方法です。リベースのアプローチは適切ですが、最後の手段としてスカッシュにのみ使用してください。
Axalix

20

本当に便利なこと:
スカッシュしたいコミットハッシュを見つけてくださいd43e15

今使う

git reset d43e15
git commit -am 'new commit name'

2
この。より多くの人々がこれを使用しないのはなぜですか?個々のコミットをリベースして押しつぶすよりもはるかに高速です。
a3y3

17

これは非常に粗雑ですが、一種のクールな方法なので、リングに投げ込みます。

GIT_EDITOR='f() { if [ "$(basename $1)" = "git-rebase-todo" ]; then sed -i "2,\$s/pick/squash/" $1; else vim $1; fi }; f' git rebase -i foo~5 foo

翻訳:gitに新しい「エディタ」を提供します。編集するファイル名が git-rebase-todo(インタラクティブなリベースプロンプト)が最初の「pick」以外のすべてを「squash」に変更し、それ以外の場合はvimを生成するため、プロンプトが表示されたときに押しつぶされたコミットメッセージを編集するには、vimを取得します。(そして明らかに私はブランチfooの最後の5つのコミットを押しつぶしていましたが、好きなように変更できます。)

でも、おそらく私はMark Longairが提案したことをするでしょう。


7
+1:GIT_EDITOR環境変数にプログラムの名前よりも複雑なものを置くことができるのは私にはまったく明らかではなかったので、それは楽しくて有益です。
Mark Longair

16

すべてのコミットを1つのコミットに圧縮する場合(たとえば、プロジェクトを初めて一般公開する場合)、次のことを試してください。

git checkout --orphan <new-branch>
git commit

15

2020リベースなしのシンプルなソリューション:

git reset --soft HEAD~2

git commit -m "new commit message"

git push --force

2は、最後の2つのコミットが押しつぶされることを意味します。あなたはそれを任意の数に置き換えることができます


14

これを行う最も簡単な方法は、masterから新しいブランチを作成し、機能ブランチのスカッシュをマージすることです。

git checkout master
git checkout -b feature_branch_squashed
git merge --squash feature_branch

その後、すべての変更をコミットする準備が整います。


12

スカッシュしたいブランチに現在いる場合、常に機能する単純なワンライナー、マスターはそのブランチの元のブランチであり、最新のコミットには使用するコミットメッセージと作成者が含まれます。

git reset --soft $(git merge-base HEAD master) && git commit --reuse-message=HEAD@{1}

4
私はコミットのスカッシュとそれがどれほど愚かに複雑であるかについての欲求不満に完全に熱心に取り組んでいます-最後のメッセージを使用してそれらをすべて1つのコミットにスカッシュするだけです!なんでそんなに難しいの???? このワンライナーは私のためにそれを行います。怒りの底からありがとう。
ロケーヌ

12

たとえば、最後の3つのコミットをブランチ(リモートリポジトリ)の単一のコミットに押しつぶしたい場合は、たとえば、https//bitbucket.org

私がしたことは

  1. git reset --soft Head〜3 &&
  2. git commit
  3. git push origin(branch_name)--force

3
あなたが力を使うならば、あなたはそれを削除するので、前のコミットを取得する方法がないので、単に、気をつけて
アルバートRuelan

12

⚠️警告:「最後のX回のコミット」はあいまいな場合があります。

  (MASTER)  
Fleetwood Mac            Fritz
      ║                    ║
  Add Danny  Lindsey     Stevie       
    Kirwan  Buckingham    Nicks                                              
      ║         ╚═══╦══════╝     
Add Christine       ║          
   Perfect      Buckingham
      ║           Nicks            
    LA1974══════════╝                                    
      ║                  
      ║                  
    Bill <══════ YOU ARE EDITING HERE
  Clinton        (CHECKED OUT, CURRENT WORKING DIRECTORY)              

https://github.com/fleetwood-mac/band-historyリポジトリのこの非常に簡略化された履歴では、Bill Clintonのコミットを元の(MASTER)Fleetwood Macコミット。

プルリクエストを開き、GitHubに次のように表示されます。

4つのコミット:

  • ダニー・カーワンを追加
  • クリスティンパーフェクトを追加
  • LA1974
  • ビル・クリントン

誰もリポジトリの完全な履歴を読もうとは思わないだろうと考えています。(実際にはリポジトリがあります。上のリンクをクリックしてください!)これらのコミットを破棄することにしました。だからあなたは行って実行しgit reset --soft HEAD~4 && git commitます。次にあなたgit push --force GitHubにそれを追加して、PRをクリーンアップします。

そして、何が起こりますか?FritzからBill Clintonへの単一コミットを作成しました。昨日、このプロジェクトのバッキンガムニックスバージョンで作業していたことを忘れたためです。そしてgit log、GitHubに表示されるものと一致しません。

🐻道徳の物語

  1. あなたが取得したい正確なファイル検索をして、git checkoutそれら
  2. 履歴に残したい正確な以前のコミットを見つけ、そして git reset --softすることを
  3. fromから toにgit commit直接ワープするa を作成します

1
これはこれを行う最も簡単な方法です。現在のHEAD 適切な状態であれば、1をスキップできます。
スタン

これは、最初のコミット履歴を書き換えることができる唯一の方法です。
Vadorequest

9

中間のコミットのコミットメッセージを気にしない場合は、

git reset --mixed <commit-hash-into-which-you-want-to-squash>
git commit -a --amend

7

GitLabを使用している場合は、以下に示すように、マージリクエストの[スカッシュ]オプションをクリックするだけです。コミットメッセージはマージリクエストのタイトルになります。

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


6
git rebase -i HEAD^^

ここで、^の数はXです。

(この場合、最後の2つのコミットを押しつぶします)


6

他の優れた答えに加えgit rebase -iて、コミット順序と常に混乱する方法を追加したいと思います-古いものから新しいものへ、またはその逆ですか?これが私のワークフローです:

  1. git rebase -i HEAD~[N]ここで、Nは、最新のコミットから始めて、参加したいコミットの数です。つまりgit rebase -i HEAD~5、「最後の5つのコミットを新しいものにつぶす」ことを意味します。
  2. エディターがポップアップし、マージしたいコミットのリストが表示されます。現在は逆の順序で表示されています。古いコミットが一番上にあります。最初/古いものを除いて、そこにあるすべてのコミットを「スカッシュ」または「s」としてマークしますます。これは開始点として使用されます。エディターを保存して閉じます。
  3. エディターがポップアップし、新しいコミットのデフォルトメッセージが表示されます。必要に応じて変更し、保存して閉じます。スカッシュ完成!

ソースと追加の読み取り:#1#2


6

このようなワークフローに関連する質問に対する回答はどうですか?

  1. マスターからの複数のマージと混合された多くのローカルコミット
  2. 最後に、リモートへのプッシュ、
  3. PRとレビュー担当者によるマスターへのマージ。(はい、開発者がmerge --squashPR を実行する方が簡単ですが、チームはそれによりプロセスが遅くなると考えました。)

このようなワークフローはこのページでは見ていません。(それが私の目かもしれません。)私がrebase正しく理解している場合、複数のマージには複数の競合解決が必要になります。そんなこと考えたくない!

したがって、これは私たちにとってはうまくいくようです。

  1. git pull master
  2. git checkout -b new-branch
  3. git checkout -b new-branch-temp
  4. ローカルでたくさん編集してコミットし、定期的にマスターをマージする
  5. git checkout new-branch
  6. git merge --squash new-branch-temp //すべての変更をステージに入れます
  7. git commit 'one message to rule them all'
  8. git push
  9. レビュー担当者がPRを行い、マスターにマージします。

多くの意見から私はあなたのアプローチが好きです。それは非常に便利で高速です
Artem Solovev '

5

より一般的な解決策は、「N」コミットを指定するのではなく、スカッシュしたいブランチ/コミットIDを指定することです。これは、特定のコミットまでのコミットをカウントするよりもエラーが発生しにくくなります。タグを直接指定するか、本当にカウントしたい場合はHEAD〜Nを指定できます。

私のワークフローでは、ブランチを開始し、そのブランチでの最初のコミットで目標を要約します(つまり、これは通常、機能の「最終」メッセージとしてパブリックリポジトリにプッシュするものです)。私はやりたいgit squash master最初のメッセージに戻った後、私がプッシュする準備ができています。

私はエイリアスを使用します:

squash = !EDITOR="\"_() { sed -n 's/^pick //p' \"\\$1\"; sed -i .tmp '2,\\$s/^pick/f/' \"\\$1\"; }; _\"" git rebase -i

これにより、破棄される前に履歴が破棄されます。元に戻す場合は、古いコミットIDをコンソールから取得することで、復元する機会があります。(Solarisユーザーは、GNU sed -iオプションを使用していることに注意してください。MacおよびLinuxユーザーは、これで問題ありません。)


私はエイリアスを試しましたが、sed replacesが効果を持っているかどうかはわかりません。彼らは何をすべきか?
2015年

最初のsedは単に履歴をコンソールにダンプします。2番目のsedは、すべての「pick」を「f」(fixup)に置き換え、エディターファイルをインプレースで書き換えます(-iオプション)。したがって、2つ目はすべての作業を行います。
イーサン

そうです、特定のコミットのN数をカウントすると、エラーが発生しやすくなります。それは私を何度も台無しにし、リベースを元に戻そうとする何時間も無駄にしています。
IgorGanapolsky

こんにちはイーサン、このワークフローがマージで起こりうる競合を隠すかどうか知りたいのですが。したがって、マスターとスレーブの2つのブランチがあるかどうかを検討してください。スレーブがマスターと競合しgit squash masterていて、スレーブでチェックアウトされているときに使用する場合。どうなりますか?紛争を隠しますか?
Sergio Bilello、2016

@Sergioこれは履歴を書き換える場合であるため、すでにプッシュされたコミットをスカッシュしてから、スカッシュされたバージョンをマージ/リベースしようとすると、おそらく競合が発生します。(いくつかの些細なケースはそれ
Ethan

5

問題は、「最後の」が何を意味するのか曖昧である可能性があります。

たとえばgit log --graph、以下を出力します(簡略化):

* commit H0
|
* merge
|\
| * commit B0
| |
| * commit B1
| | 
* | commit H1
| |
* | commit H2
|/
|

次に、時間までの最後のコミットはH0、マージ、B0です。それらを押しつぶすには、コミットH1でマージされたブランチをリベースする必要があります。

問題は、H0にH1とH2が含まれていること(および一般に、マージ前と分岐後のコミット数が多い)ですが、B0には含まれていません。したがって、少なくともH0、マージ、H1、H2、B0からの変更を管理する必要があります。

リベースを使用することは可能ですが、別の方法で他の言及された回答とは異なります:

rebase -i HEAD~2

これにより、(他の回答で述べたように)選択オプションが表示されます。

pick B1
pick B0
pick H0

ピックの代わりにスカッシュをH0に配置します。

pick B1
pick B0
s H0

保存して終了した後、リベースはH1の後にコミットを順番に適用します。つまり、再び競合を解決するように求められます(最初にHEADがH1になり、適用されるとコミットが累積されます)。

リベースが終了したら、押しつぶされたH0とB0のメッセージを選択できます。

* commit squashed H0 and B0
|
* commit B1
| 
* commit H1
|
* commit H2
|

PS BOへのリセットを行うだけの場合:(たとえば、それを使用するreset --mixedことについては、https//stackoverflow.com/a/18690845/2405850で詳しく説明しています):

git reset --mixed hash_of_commit_B0
git add .
git commit -m 'some commit message'

次に、H0、H1、H2のB0変更につぶします(分岐後、マージ前の変更に対するコミットを完全に失います)。


4

1)git reset --soft HEAD〜n

n-コミットの数、スカッシュする必要がある

2)git commit -m "新しいコミットメッセージ"

3)git push origin branch_name --force

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