すでにリベースを開始している場合、2つのコミットを1つにマージするにはどうすればよいですか?


1158

2つのコミットを1つにマージしようとしているので、git readyから「コミットをリベースしてスカッシュ」しました

私は走った

git rebase --interactive HEAD~2

結果のエディターで、に変更picksquashてから保存を終了しますが、リベースはエラーで失敗します

以前のコミットがないと「スカッシュ」できません

作業ツリーがこの状態に達したので、回復に問題があります。

コマンドはgit rebase --interactive HEAD~2失敗します:

インタラクティブリベースはすでに開始されています

そしてgit rebase --continue失敗する

以前のコミットがないと「スカッシュ」できません


22
私もこれを叩きました。私のミスは、git rebase -iがコミットをgit logとは逆の順序でリストするという事実が原因でした。最新のコミットは一番下にあります!
lmsurprenant 2014


1
またチェックアウト:git-scm.com/book/en/Git-Tools-Rewriting-History
nha

回答:


1733

概要

エラーメッセージ

以前のコミットがないと「スカッシュ」できません

「下に押しつぶそう」とした可能性があります。Gitは常に、インタラクティブなリベースのToDoリストで表示されるように、新しいコミットを古いコミットまたは「上向き」に、つまり前の行のコミットに押しつぶします。todoリストの最初の行のコマンドをに変更するsquashと、最初のコミットで押しつぶすものがないため、常にこのエラーが発生します。

修正

最初に、最初の場所に戻ります

$ git rebase --abort

あなたの歴史は

$ git log --pretty=oneline
a931ac7c808e2471b22b5bd20f0cad046b1c5d0d c
b76d157d507e819d7511132bdb5a80dd421d854f b
df239176e1a2ffac927d8b496ea00d5488481db5 a

つまり、aが最初のコミット、次にb、最後にcでした。cをコミットした後、bとcを一緒にスカッシュすることにします。

(注:実行git logすると、lessほとんどのプラットフォームでは、デフォルトでその出力がページャーにパイプされます。ページャーを終了してコマンドプロンプトに戻るには、qキーを押します。

実行git rebase --interactive HEAD~2すると、エディターが

pick b76d157 b
pick a931ac7 c

# Rebase df23917..a931ac7 onto df23917
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

(このtodoリストは、の出力とは逆の順序になっていることに注意してくださいgit log。)

bをに変更pickするsquashと、表示されたエラーが発生しますが、代わりに、todoリストを次のように変更して、cをbに押しつぶす(新しいものを古いものにコミットするか、「上方に押しつぶす」)

pick   b76d157 b
squash a931ac7 c

エディタを保存して終了すると、内容が別のエディタが表示されます

# This is a combination of 2 commits.
# The first commit's message is:

b

# This is the 2nd commit message:

c

保存して終了すると、編集されたファイルの内容は、新しく結合されたコミットのコミットメッセージになります。

$ git log --pretty=oneline
18fd73d3ce748f2a58d1b566c03dd9dafe0b6b4f b and c
df239176e1a2ffac927d8b496ea00d5488481db5 a

履歴の書き換えに関する注意

インタラクティブなリベースは履歴を書き換えます。古い履歴を含むリモートにプッシュしようとすると、早送りではないため失敗します。

リベースしたブランチが、自分作業しているトピックまたは機能ブランチである場合、大したことはありません。別のリポジトリへのプッシュには--forceオプションが必要ですが、リモートリポジトリの権限に応じて、最初に古いブランチを削除してからリベースバージョンをプッシュすることもできます。作業を破壊する可能性のあるコマンドの例は、この回答の範囲外です。

パスワードやその他の機密情報の漏えいなどの正当な理由なし他のユーザーと作業しているブランチで、すでに公開されている履歴を書き換えると、協力者に働きかけ、反社会的であり、他の開発者を困らせます。セクション「上流リベースからの回復」git rebaseの文書は、追加重点を置いて、説明しています。

他の人が作業に基づいて作成したブランチのリベース(または他の形式の書き換え)は悪い考えです。その下流の誰もが手動で履歴を修正する必要があります。このセクションでは、ダウンストリームの観点からの修正方法を説明します。ただし、本当の解決策は、そもそも上流をリベースしないことです。


リベースを使用してコミットを押しつぶすと、2つのチェンジセットを含む新しい「結合」コミットが作成されますが、ハッシュが異なります。元のコミットもgitによって保持されますか?
fabsenet 2013

@fabsenetはい、いいえ。元のコミットには引き続きアクセスできますが、おそらく履歴からは到達できません(履歴の詳細によって異なります)。参照されていないコミットは、ガベージコレクションプロセスによって最終的に消去されます。
Greg Bacon

私はちょうど...周りに遊んでいた私はgit log hashoftheoldcommit、それが働いたが、私が見て興味があったgit log --graphこれらの到達不能コミットの全てでは付属
fabsenet

このスカッシュは、プッシュする前にコミットを整理するのに適したツールですが、コミットをプッシュすると、スカッシュできません。gitは言う:正常にリベースし、デタッチされたヘッドを更新しました。
セルジオ

2番目のインスタンスであるgit bashでエディターを取得できませんが、いくつかのプロセスにフリーズしているようです。何をすべきか?

411

複数のコミットがある場合は、を使用git rebase -iして2つのコミットを1つにまとめることができます。

マージするコミットが2つしかなく、それらが「最新の2つ」である場合、次のコマンドを使用して2つのコミットを1つに結合できます。

git reset --soft "HEAD^"
git commit --amend

6
リベースと比較した場合の欠点は何ですか?使用する方がずっと簡単だと思います。
Guillaume86 14

17
任意の順序で参加することはできません。最後の2つのコミットのみです。
dr0i 14

50
@ dr0i 最後のXコミットであり、途中ではない限り、必要な数のコミットをマージできます。実行するだけですgit reset --soft HEAD~10。10はマージしたいコミットの数です。
fregante 2015

2
これは、リモートオリジンセットがなく、コミットが2つしかない場合に使用できます。
atedja

8
あなたはそこからどのくらいあるか数えたくない場合にも、特定のコミットにリセットすることができますHEAD使用してgit reset --soft 47b5c5...いる47b5c5...コミットのSHA1のIDです。
dguay

112

リベース:あなたはそれを必要としないでしょう:

最も頻繁なシナリオのためのより簡単な方法。

ほとんどの場合:

実際に必要なのは、いくつかの最近のコミットを1つに結合するだけdropreword、他のリベース作業 は必要ない場合だけです。

あなたは単に行うことができます:

git reset --soft "HEAD~n"
  • 仮定すると、~nそっとアンコミットするコミットの数(すなわちであります~1~2、...)

次に、次のコマンドを使用してコミットメッセージを変更します。

git commit --amend

これはかなり長い間の範囲と同じであるsquashと1 pick

そして、それはn個のコミットに対して機能しますが、上記の回答が要求する2つのコミットだけではありません。


3
これは、コミットを破棄するだけでなく、途中で1つのコミットを削除したり、コードの行を変更したりするなど、追加のクリーンアップを行う場合に便利です。
スタイル

1
仮定すると、~nそっとアンコミットするコミットの数(すなわちである~1~2、...)
レイ

1
n最後のコミットではなくn途中のコミットをマージしたい場合はどうなりますか?これは簡単にできますか?
chumakoff 2017

1
次にgit rebase -i、あなたがsquash仕事をする必要があるものです。@chumakoff
pambda

3
だから、参加するn1、最初の使用に最新のコミットをしてgit reset --soft @~mm = n - 1
ルカシュRajchel

55

最初に、コミットの数を確認する必要があります。

git log

次の2つのステータスがあります。

1つは、コミットが2つしかないことです。

例えば:

commit A
commit B

(この場合、git rebaseを使用して実行することはできません)以下を実行する必要があります。

$ git reset --soft HEAD^1

$ git commit --amend

もう1つは、2つ以上のコミットがあることです。コミットCとDをマージしたい。

例えば:

commit A
commit B
commit C
commit D

(この条件下では、git rebaseを使用できます)

git rebase -i B

そして、「スカッシュ」を使用するよりも。残りのシンは非常に簡単です。それでもわからない場合は、http://zerodie.github.io/blog/2012/01/19/git-rebase-i/をお読みください。


リセット--softおよびcommit --amendは、すでにリベースが進行中の場合(およびこのコミットに「スカッシュ」ではなく「編集」を選択した場合)に機能する唯一の方法です。+1
Jacek Lach

1
リポジトリで最初と2つだけのコミットをマージする、まさに私のエッジケース:-)
Chris Huang-Leaver

1
git push -f origin master必要に応じて追加してください。
Rishabh Agrahari 2017年

33

あなたがあなた自身のトピックブランチにいたと仮定します。最後の2つのコミットを1つにマージしてヒーローのように見える場合は、最後の2つのコミットを行う直前にコミットを分岐します。

git checkout -b temp_branch HEAD^2

次に、この新しいブランチで他のブランチをスカッシュコミットします。

git merge branch_with_two_commits --squash

それは変更をもたらしますが、それらをコミットしません。だから、それらをコミットすれば完了です。

git commit -m "my message"

これで、この新しいトピックブランチをメインブランチにマージできます。


5
手動でリベースする必要がなく、ブランチ全体のすべてのコミットを1つのコミットにまとめるだけなので、これは実際に私にとって最も役立つ回答でした。非常に素晴らしい。
ロバート

これをありがとう!これは、Gitに頭の中で押しつぶすコミットをどのように描くかを示す方法です!
Marjan Venema 2016

驚くべき答え、他の選択肢よりもはるかに簡単

どうやら、この答えは場合に適していないときac一緒にマージしておくことが必要となるbことがあるよう。
Talha Ashraf

2
gitの最近のバージョンで何か変更はありますか?私は(最初のコマンドをしようとするとgit checkout -b combine-last-two-commits "HEAD^2"gitのバージョン2.17で)、私はエラーを取得:fatal: 'HEAD^2' is not a commit and a branch 'combine-last-two-commits' cannot be created from it
mhucka

23

リベースをキャンセルできます

git rebase --abort

そして、インタラクティブなrebaseコマンドを再度実行すると、「スカッシュ」。コミットはリストのピックコミットの下にある必要があります


16

私はよくgit reset --mixedを使用して、マージする複数のコミットの前にベースバージョンを元に戻し、次に新しいコミットを作成します。これにより、コミットが最新になり、サーバーにプッシュした後、バージョンがHEADになることが保証されます。

commit ac72a4308ba70cc42aace47509a5e
Author: <me@me.com>
Date:   Tue Jun 11 10:23:07 2013 +0500

    Added algorithms for Cosine-similarity

commit 77df2a40e53136c7a2d58fd847372
Author: <me@me.com>
Date:   Tue Jun 11 13:02:14 2013 -0700

    Set stage for similar objects

commit 249cf9392da197573a17c8426c282
Author: Ralph <ralph@me.com>
Date:   Thu Jun 13 16:44:12 2013 -0700

    Fixed a bug in space world automation

ヘッド2つのコミットを1つにマージしたい場合は、最初に次のコマンドを使用します。

git reset --mixed 249cf9392da197573a17c8426c282

「249cf9392da197573a17c8426c282」は3番目のバージョンでしたが、マージする前のベースバージョンでもあります。その後、新しいコミットを作成します。

git add .
git commit -m 'some commit message'

それはすべてです、希望は誰にとっても別の方法です。

参考までにgit reset --help

 --mixed
     Resets the index but not the working tree (i.e., the changed files are
     preserved but not marked for commit) and reports what has not been
     updated. This is the default action.

私は '--mixed'のドキュメントを読んでいませんが、他の人が投稿を読んで同じことを疑問に思っていると思います:--mixedを使用する利点は何ですか?投稿を改善して、manページのスニペットを含めることができます。
funroll 2014

@funroll私は知りませんでした-この答えを書く前に非常に混合しました、私の経験によれば、混合操作はリポジトリHEADバージョンとして引数として渡すバージョンを指定し、そのバージョン以降は何も失われることはありません、そのため、これらの変更を処理できます。
VinceStyling 2014

14

$ git rebase --abort

git rebaseを元に戻したい場合は、いつでもこのコードを実行してください

$ git rebase -i HEAD~2

最後の2つのコミットを再適用します。上記のコマンドはコードエディターを開きます

  • [ 最新のコミットは一番下になります ]。最後のコミットをスカッシュに変更します。スカッシュは以前のコミットと融合するため。
  • 次に、Escキーを押し、:wqと入力して保存して閉じます。

:wqの後、アクティブなリベースモードになります

:警告/エラーメッセージがない場合は、別のエディターが表示されます。エラーまたは警告が表示され、別のエディターが表示され$ git rebase --abortない場合は、エラーまたは警告が表示された場合は、実行によって中止 できます。$ git rebase --continue

2つのコミットメッセージが表示されます。いずれかを選択するか、独自のコミットメッセージを書いて、保存して終了します[:wq]

注2: rebaseコマンドを実行する場合、リモートリポジトリに変更を強制的にプッシュする必要がある場合があります。

$ git push -f

$ git push -f origin master


1
注2:git push -f origin/master他の答えが欠けているものです。+1
リシャブ・アグラハリ2017年

2

私はgit cherry-pickほぼすべてのものを使用しているので、ここでもそうするのは自然なことです。

私がbranchXチェックアウトし、その先端に2つのコミットがあり、それらのコンテンツを組み合わせて1つのコミットを作成したい場合は、次のようにします。

git checkout HEAD^ // Checkout the privious commit
git cherry-pick --no-commit branchX // Cherry pick the content of the second commit
git commit --amend // Create a new commit with their combined content

branchXも更新したい場合(そして、これがこのメソッドのマイナス面だと思います)、次のことも行う必要があります。

git checkout branchX
git reset --hard <the_new_commit>

1

あなたのマスターブランチgit logが次のようなものなら:

commit ac72a4308ba70cc42aace47509a5e
Author: <me@me.com>
Date:   Tue Jun 11 10:23:07 2013 +0500

    Added algorithms for Cosine-similarity

commit 77df2a40e53136c7a2d58fd847372
Author: <me@me.com>
Date:   Tue Jun 11 13:02:14 2013 -0700

    Set stage for similar objects

commit 249cf9392da197573a17c8426c282
Author: Ralph <ralph@me.com>
Date:   Thu Jun 13 16:44:12 2013 -0700

    Fixed a bug in space world automation

上位2つのコミットをマージするには、次の簡単な手順に従ってください。

  1. 最初は安全な側にいて、最後の2番目のコミットを別のブランチでチェックアウトします。ブランチには任意の名前を付けることができます。git checkout 77df2a40e53136c7a2d58fd847372 -b merged-commits
  2. ここで、最後のコミットからの変更をこの新しいブランチに次のようにチェリーピックしますgit cherry-pick -n -x ac72a4308ba70cc42aace47509a5e。(競合が発生した場合は解決します)
  3. したがって、最後のコミットでの変更は、2番目の最後のコミットに存在します。ただし、まだコミットする必要があるため、最初に選択した変更を追加してから実行しgit commit --amendます。

それでおしまい。必要に応じて、このマージされたバージョンをブランチ「merged-commits」にプッシュできます。

また、マスターブランチの連続する2つのコミットを破棄することもできます。masterブランチを次のように更新するだけです:

git checkout master
git reset --hard origin/master (CAUTION: This command will remove any local changes to your master branch)
git pull

0

最新の2つのコミットを組み合わせて、古いコミットのメッセージのみを使用する場合は、を使用してプロセスを自動化できますexpect

私が想定し:

  • エディターとしてviを使用しています
  • あなたのコミットはそれぞれ1行です

でテストしましたgit version 2.14.3 (Apple Git-98)


#!/usr/bin/env expect
spawn git rebase -i HEAD~2

# change the second "pick" to "squash"
# down, delete word, insert 's' (for squash), Escape, save and quit
send "jdwis \033:wq\r"

expect "# This is a"

# skip past first commit message (assumed to be one line), delete rest of file
# down 4, delete remaining lines, save and quit
send "4jdG\r:wq\r"

interact

スクリプトが何をするかは不明です。
buhtz

@buhtzいくつかコメントを追加しました。それでもまだ混乱している場合は、私に知らせてください。
erwaman

あなたのスクピットが何をするかはまだ不明です。またexpect、記述されていません。
buhtz

@buhtzどの部分が不明ですか?のドキュメントの多いページへのリンクを提供しましたexpect
erwaman

スクリプトの一般的な説明がありません。それが何をするかは不明です。不明な部分はありません。スクリプト自体の意図は不明です。
buhtz
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.