web-dev-qa-db-ja.com

Gitを使って私の最後のXコミットをまとめて潰しましょう

Gitを使って最後のX個のコミットをまとめて1つのコミットにまとめるにはどうすればいいですか?

2727
markdorison

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

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

1541
Anomie

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をスカッシュしたくない最後のコミットに再設定するだけです。インデックスも作業ツリーもソフトリセットで触れられず、インデックスは新しいコミットに望ましい状態になります(つまり、「破棄」しようとしているコミットからのすべての変更が既にあります)。

2987
Chris Johnsen

これには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オプションをより詳細に説明しています。


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

633
Mark Longair

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

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

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

168
nobar

に基づくChris Johnsenの回答

Bash:からグローバルな "squash"エイリアスを追加します(または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を使うことができます。 (または、あなたの好みに合うようにエイリアスを編集してください。)

110
EthanB

おかげで この便利なブログ記事 私はあなたが最後の3つのコミットを潰すためにこのコマンドを使うことができるとわかりました:

git rebase -i HEAD~3

あなたが追跡情報/リモートレポのないローカルブランチにいる時でさえもそれが働くのでこれは便利です。

このコマンドは対話式リベースエディタを開きます。これにより、通常どおりに並べ替え、スカッシュ、書き換えなどを行うことができます。


対話式リベースエディタを使用します。

対話式リベースエディタは、最後の3つのコミットを表示します。この制約は、コマンドHEAD~3の実行時にgit rebase -i HEAD~3によって決定されました。

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

表示される文書はかなり明確です。どの行でも、コマンドをpickから選択したコマンドに変更できます。

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

1行目のコミットはHEADなので、ほとんどの場合、これはpickのままにします。 コミットを潰すためのコミットが他にないため、squashまたはfixupを使用することはできません。

interactive rebase editor

79
br3nt

TortoiseGitを使用している場合は、関数Combine to one commitを使用できます。

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

Combine commits

この関数は、必要なすべてのシングルgitステップを自動的に実行します。

52
Matthias M

に基づいて この記事 私は私のユースケースのためにこのメソッドが簡単だとわかりました。

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

変更を進める前に、これらのコミットを1つにまとめたいと思いました。私はブランチを 'Origin/dev'の状態にリセットすることを好み(これは96のコミットからの全ての変更をステージングされないままにしておくでしょう)それから一度に変更をコミットします:

git reset Origin/dev
git add --all
git commit -m 'my commit message'
39
trudolf

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

 git rebase -i HEAD~n

n(ここでは4)は最後のコミットの数です。それからあなたは以下のオプションを得ました、

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

次のように更新

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

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

がんばろう!!

32

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)Wordのpickabcd1234の前にあるsquashに名前変更します。名前を変更した後は、次のようになります。

....
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)を使用している場合、上記のコマンドはエディタ以外は同じです。あなたは別のエディタを手に入れるかもしれません。

29
rashok

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

ステップ0:git log

git logで現在地を確認してください。最も重要なのは、スカッシュするしてはいけない最初のコミットのコミットハッシュを見つけることです。だから:

enter image description here

ステップ1:git rebase

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

$ git rebase -i 2d23ea524936e612fae1ac63c95b705db44d937d

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

私の場合、時間内に最初に行ったコミットですべてをつぶします。順序は最初から最後までなので、git logの場合とまったく同じです。私の場合、私は欲しい:

enter image description here

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

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

enter image description here

それでおしまい。これを保存したら(:wq)完了です。 git logでご覧ください。

29
Martin Thoma

ゴールデンリポジトリ(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. 変更をコミットします(これがdev-branchで行われる唯一のコミットになります)。

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

    git Push Origin dev-branch
    
23
Sandesh Kumar

これは非常におしゃべりですが、ちょっとクールな方法ですので、それをリングに投げます。

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-rebase-todo(インタラクティブリベースプロンプト)の場合、最初の "pick"以外はすべて "squash"に変更し、それ以外の場合はvimを生成するように、gitに新しい "editor"を提供押しつぶされたコミットメッセージを編集するよう促されて、あなたはvimを得ます。 (そして明らかに私はブランチfooでの最後の5つのコミットを潰していましたが、あなたはそれを好きなように変更することができます。)

Mark Longairが提案したことをおそらくするでしょう .

15
Cascabel

ブランチで、コミットを結合したい場合は、次のように実行します。

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

これによりテキストエディタが開きます。これらのコミットをマージしたい場合は、各コミットの前にある 'pick'を 'squash'に切り替える必要があります。たとえば、すべてのコミットを1つにマージしようとしている場合は、 'pick'が最初のコミットであり、それ以降のすべてのコミットは 'squash'に設定する必要があります。 vimを使用している場合は、挿入モードで :x を使用してエディタを保存して終了します。

その後、リベースを続行します。

git rebase --continue

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

14
aabiro

本当に便利なことがあります。
d43e15のように、つぶしたいコミットハッシュを見つけます。

今すぐ使用

git reset d43e15
git commit -am 'new commit name'
14
Ariel Gabizon

every commitを1つのコミットにまとめたい場合(たとえば、初めてプロジェクトを公開したとき)、次のことを試してください。

git checkout --Orphan <new-branch>
git commit
13
William Denniss

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

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

これで、すべての変更をコミットする準備が整いました。

10
user1376350

最後の10個のコミットを1つのシングルコミットに圧縮するには

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

潰されたコミットでリモートブランチを更新したい場合は、

git Push -f
7
Ayan
git rebase -i HEAD^^

^の数はXです

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

4
Yoaz Menda

もっと一般的な解決策は 'N'コミットを指定するのではなく、むしろあなたが一番上に押しつぶしたいbranch/commit-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ユーザーはこれで問題ないはずです。)

4
Ethan

問題となるのは、「最後の」の意味が曖昧なことです。 

たとえば、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
|

P.S単にBO:にリセットするだけの場合(たとえば、ここで詳しく説明されているreset --mixedを使用する https://stackoverflow.com/a/18690845/2405850 ):

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

それからB0のH0、H1、H2の変更に潰します(分岐の後でマージの前に完全に変更をコミットします)。

4
littlewhywhat

他の優れた答えに加えて、私はgit rebase -iが常にコミット順と私を混同していることを付け加えたいと思います - 古いものから新しいもの、またはその逆でしょうか。これが私のワークフローです。

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

情報源と追加の読み取り: #1#2

3
Ignorant

中間コミットのコミットメッセージを気にしない場合は、次を使用できます。

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

⚠️警告:「私の最後の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コミットにマージするためのpull requestを開きました。 。

プルリクエストを開くと、GitHubでこれがわかります。

4つのコミット

  • Danny Kirwanを追加
  • クリスティンパーフェクトを追加
  • LA1974
  • ビル・クリントン 

だれもリポジトリの全履歴を読んでも構わないと考える人はいません。 (実際にはリポジトリがあります。上のリンクをクリックしてください!)これらのコミットを潰すことにしました。 git reset --soft HEAD~4 && git commitを実行してください。それからあなたのPRをクリーンアップするためにGitHubにそれをgit Push --forceします。

そして何が起こりますか? FritzからBill Clintonへのシングルコミットを完了しました。昨日あなたがこのプロジェクトのバッキンガムニックス版に取り組んでいたことを忘れたので。そしてgit logはGitHubで見たものと一致しません。

????この話の教訓

  1. 必要なファイルを正確に見つけて、それらをgit checkout
  2. あなたが歴史の中で残しておきたい正確な事前コミットを見つけてください、そしてそれgit reset --soft
  3. Fromからtoに直接ワープするgit commitを作る
3

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

  1. 多くのローカルコミット、 マスターからの複数のマージとの混在
  2. ついにリモートへのプッシュ、
  3. PRとマージしてTOマスター レビューアによる 。(はい、PR後に開発者がmerge --squashを入力するほうが簡単ですが、チームはプロセスを遅くすると考えました。)

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

だから、これは私たちのために働くようだ。

  1. git pull master
  2. git checkout -b new-branch
  3. git checkout -b new-branch-temp
  4. ローカルでたくさん編集してコミットし、定期的にmasterをマージする
  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してマスターにマージします。
3
allenjom

まず、私の機能ブランチと現在のマスターブランチの間のコミット数を調べます。

git checkout master
git rev-list master.. --count

それから、私は自分の機能のブランチに基づいて別のブランチを作成し、my-featureブランチはそのまま残します。

最後に、私は走ります

git checkout my-feature
git checkout -b my-rebased-feature
git checkout master
git checkout my-rebased-feature
git rebase master
git rebase head^x -i
// fixup/pick/rewrite
git Push Origin my-rebased-feature -f // force, if my-rebased-feature was ever pushed, otherwise no need for -f flag
// make a PR with clean history, delete both my-feature and my-rebased-feature after merge

お役に立てば幸いです。

2
Alan Dong

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

enter image description here

2
arush436

あなたが現在あなたが潰したいブランチにいて、masterがそれが由来するブランチであり、そして最新のコミットがあなたが使いたいコミットメッセージと作者を含んでいるならば、いつもうまくいく単純なワンライナー

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

GitUp を使用している場合は、親とマージしたいコミットを選択して _ s _ を押します。コミットごとに1回実行する必要がありますが、正しいコマンドラインの呪文を思いつくよりもずっと簡単です。特にそれがあなたがたまにしかしないものであるならば。

1
SSteve

たとえば、最後の3つのコミットをブランチ(リモートリポジトリ)内の1つのコミットにまとめたい場合は、次のようにします。 https://bitbucket.org

私がしたことは

  1. git reset - ソフトヘッド〜3 &&
  2. git commit
  3. gitプッシュオリジン(branch_name)--force
1
Albert Ruelan

このbash関数をbashの.zshrcファイルに追加するだけです。

# Squash last X commits with a Commit message.
# Usage: squash X 'COMMIT_MSG'
# where X= Number of last commits.
# where COMMIT_MSG= New commit msg.
function squash() {
    if [ -z "${1}" -o -z "${2}" ]; then
        echo "Usage: \`squash X COMMIT_MSG\`"
        echo "X= Number of last commits."
        echo "COMMIT_MSG= New commit msg."
        return 1
    fi

    git reset --soft HEAD~"$1"
    git add . && git ci -m "$2" # With 100 emoji
    git Push --force
}

それから走りなさい

squash X 'New Commit Message'

そして、これで終わりです。

0
Ahmad Awais