すべてのgitコミットを1つにまとめる方法は?


480

リポジトリ全体を最初のコミットまでどうやって潰すのですか?

最初のコミットにリベースできますが、2つのコミットが残ります。最初のコミットの前にコミットを参照する方法はありますか?


8
「最初のコミットの前のコミット」?
innaM 2009年

31
@innaM- gitを生んだのは原始的なコミットです。(ユーモアはインターウェブを十分に通り抜けます)。
ripper234

11
この質問の後半に来る人は、より現代的な答えを使用してください
Droogans 2013

1
関連しますが、複製で--rootはありません(スカッシュするコミットがたくさんある場合、実際にはすべてのコミットをスカッシュする最善の解決策ではありません):Gitリポジトリの最初の2つのコミットを結合しますか?

2
Imo:これは@MrTux:stackoverflow.com/questions/30236694/…からの最高です。
J0hnG4lt

回答:


129

おそらく最も簡単な方法は、作業コピーの現在の状態で新しいリポジトリを作成することです。すべてのコミットメッセージを保持する場合は、最初に実行してgit log > original.logから、それを新しいリポジトリの最初のコミットメッセージ用に編集します。

rm -rf .git
git init
git add .
git commit

または

git log > original.log
# edit original.log as desired
rm -rf .git
git init
git add .
git commit -F original.log

62
しかし、あなたはこの方法で枝を
失って

150
この答えが出されてから、Gitは進化しました。いいえ、より簡単で優れた方法がありますgit rebase -i --root。参照:stackoverflow.com/a/9254257/109618
David J.

5
これはいくつかのケースではうまくいくかもしれませんが、本質的には質問に対する答えではありません。このレシピを使用すると、すべての構成と他のすべてのブランチも失われます。
iwein 2013年

3
これは、不必要に破壊的な恐ろしいソリューションです。使用しないでください。
Daniel Kamil Kozar、2015年

5
サブモジュールも壊れます。-1
2016

694

以下のようにgit 1.6.2は、使用することができますgit rebase --root -i

最初のコミットを除く各コミットについて、に変更picksquashます。


49
元の質問に答える完全で実用的なコマンドの例を追加してください。
ジェイク

29
受け入れられた答えが言うように私のリポジトリ全体を吹き飛ばす前にこれを読んだらいいのに:/
Mike Chamberlain

38
この答えはOK、しかし、あなたが対話的により、たとえば、20回のよりコミットをリベースしている場合、インタラクティブリベースはおそらく遅すぎると扱いにくい道になります。おそらく、数百または数千のコミットを押しつぶそうとするのに苦労するでしょう。その場合は、ルートコミットをソフトリセットまたは混合リセットしてから再コミットします。

20
@Pred squashすべてのコミットに使用しないでください。一番最初のものはする必要がありますpick
Geert、2015年

14
多くのコミットがある場合、手動で「選択」を「スカッシュ」に変更することは困難です。VIMコマンドラインで:%s / pick / squash / gを使用して、これを高速化します。
エイラス2017

314

更新

エイリアスを作成しましたgit squash-all
使用例git squash-all "a brand new start"

[alias]
  squash-all = "!f(){ git reset $(git commit-tree HEAD^{tree} -m \"${1:-A new start}\");};f"

警告:コメントを提供することを忘れないでください。そうしないと、デフォルトのコミットメッセージ「新しいスタート」が使用されます。

または、次のコマンドでエイリアスを作成できます。

git config --global alias.squash-all '!f(){ git reset $(git commit-tree HEAD^{tree} -m "${1:-A new start}");};f'

一発ギャグ

git reset $(git commit-tree HEAD^{tree} -m "A new start")

:ここでの「A new start」は単なる例です。自分の言語を自由に使用してください。

TL; DR

スカッシュする必要はありませんgit commit-tree。孤立したコミットを作成してそれを使用するために使用します。

説明する

  1. を介して単一のコミットを作成する git commit-tree

    git commit-tree HEAD^{tree} -m "A new start"が:

    指定されたツリーオブジェクトに基づいて新しいコミットオブジェクトを作成し、新しいコミットオブジェクトIDをstdoutに出力します。-mまたは-Fオプションが指定されていない限り、ログメッセージは標準入力から読み取られます。

    この式HEAD^{tree}は、に対応するツリーオブジェクトHEAD、つまり現在のブランチの先端を意味します。Tree-ObjectsおよびCommit-Objectsを参照してください。

  2. 現在のブランチを新しいコミットにリセットする

    次にgit reset、現在のブランチを新しく作成されたコミットオブジェクトにリセットします。

この方法では、ワークスペースの何も変更されず、リベース/スカッシュの必要もないため、非常に高速になります。また、必要な時間は、リポジトリのサイズや履歴の深さとは関係ありません。

バリエーション:プロジェクトテンプレートからの新しいリポジトリ

これは、別のリポジトリをtemplate / archetype / seed / skeletonとして使用して、新しいプロジェクトに「初期コミット」を作成するのに役立ちます。例えば:

cd my-new-project
git init
git fetch --depth=1 -n https://github.com/toolbear/panda.git
git reset --hard $(git commit-tree FETCH_HEAD^{tree} -m "initial commit")

これにより、テンプレートリポジトリをリモート(originまたはその他)として追加することが回避され、テンプレートリポジトリの履歴が最初のコミットに折りたたまれます。


6
gitリビジョン構文(HEAD ^ {tree})の構文は、他の誰かが疑問に思っている場合のためにここで説明されています:jk.gs/gitrevisions.html
Colin Bowern

1
これにより、ローカルリポジトリとリモートリポジトリの両方がリセットされますか?
aleclarson 2014年

4
@aleclarson、これはローカルリポジトリの現在のブランチのみをリセットし、git push -f伝播に使用します。
ライナス14年

2
関係のないプロジェクトテンプレートリポジトリから新しいプロジェクトを開始する方法を探しているときに、この答えを見つけましたgit clone。あなたが追加した場合--hardgit reset、スイッチHEADFETCH_HEADしてgit commit-tree、あなたは、最初に作成できるテンプレートのレポをフェッチした後にコミットします。これを示す最後のセクションで回答を編集しました。
toolbear 2015

4
あなたはその「警告」を取り除くことができますが、単に使用します${1?Please enter a message}
Elliot Cameron

172

すべてのコミットをルートコミットに押しつぶすだけの場合は、

git rebase --interactive --root

リベース操作はおそらく非常に遅く実行されてインタラクティブなリベースエディタのコミットリストを生成するだけでなく、リベース自体を実行するため、多数のコミット(たとえば、数百のコミット)には実用的ではありません。

多数のコミットを押しつぶしているときの2つのより速くより効率的なソリューションを次に示します。

代替ソリューション#1:孤立したブランチ

現在のブランチの先端(つまり、最新のコミット)に新しい孤立したブランチを作成するだけです。この孤立したブランチは、まったく新しい別のコミット履歴ツリーの最初のルートコミットを形成します。これは、すべてのコミットを押しつぶすことと実質的に同等です。

git checkout --orphan new-master master
git commit -m "Enter commit message for your new initial commit"

# Overwrite the old master branch reference with the new one
git branch -M new-master master

ドキュメンテーション:

代替ソリューション#2:ソフトリセット

別の効率的な解決策は、ルートコミットに対して混合リセットまたはソフトリセットを使用すること<root>です。

git branch beforeReset

git reset --soft <root>
git commit --amend

# Verify that the new amended root is no different
# from the previous branch state
git diff beforeReset

ドキュメンテーション:


22
代替ソリューション#1:孤立したブランチ-岩!
トーマス

8
代替ソリューション#1 FTW。追加するだけで、変更をリモートにプッシュする場合は、を実行しますgit push origin master --force
Eddy Verbruggen

1
忘れないでくださいgit push --force
NecipAllef

オープンなGithubプルリクエストにプッシュしようとしている場合は、コードで孤立ブランチを実行しないでください(つまり、上記の代替ソリューション#1を実行しないでください)。現在のヘッドは保存されているヘッドshaの子孫ではないため GithubはPRを閉じます。
アンドリューマッキー

代替ソリューション#1は、コミットの
スカッシュ

52
echo "message" | git commit-tree HEAD^{tree}

これにより、HEADのツリーで孤立したコミットが作成され、その名前(SHA-1)がstdoutに出力されます。次に、ブランチをリセットします。

git reset SHA-1

27
git reset $(git commit-tree HEAD^{tree} -m "commit message")簡単になります。
ryenus 2013

4
^これ!-答えになるはずです。それが作者の意図であるかどうかは完全にはわかりませんが、それは私のものでした(1回のコミットで元のリポジトリを必要とし、それによって仕事が完了します)。
chesterbr 2014年

@ryenus、あなたの解決策は私が探していたものとまったく同じでした。コメントを回答として追加していただければ、承諾します。
tldr 2014年

2
私がサブシェルバ​​リアントを自分で提案しなかった理由は、Windowsのcmd.exeでは機能しないためです。
kusma

Windowsのプロンプトでは、最後のパラメータを引用する必要がある場合があります: echo "message" | git commit-tree "HEAD^{tree}"
バーナード

41

他の誰かのためにうまくいく場合に備えて、これが私がこれをやった方法です:

このようなことには常にリスクがあり、開始する前に保存ブランチを作成することは決して悪い考えではないことを覚えておいてください。

ロギングから始める

git log --oneline

最初のコミットまでスクロールし、SHAをコピーします

git reset --soft <#sha#>

<#sha#>ログからコピーされたSHAを置き換えます

git status

すべてが緑色であることを確認します。それ以外の場合は実行します git add -A

git commit --amend

現在のすべての変更を現在の最初のコミットに修正します

このブランチを強制的にプッシュすると、そこにあるものが上書きされます。


1
優れたオプション!本当に簡単です。
Twicejr

1
これは素晴らしいことです!ありがとう!
Matt Komarnicki、2016年

1
これは実際には歴史を残しているように見えることに注意してください。孤立しましたが、まだ残っています。
Brad

非常に役立つ回答...しかし、修正コマンドの後で、特別な構文を使用してvimエディターにアクセスできることに注意してください。ESC、ENTER、:xはあなたの友達です。
Erich Kuester

優れたオプション!
danivicario

36

移植片の使用について何か読みましたが、あまり調べませんでした。

とにかく、あなたはこれらの最後の2つのコミットを手動で次のようなものでつぶすことができます:

git reset HEAD~1
git add -A
git commit --amend

4
これは実際に私が探していた答えです、それが受け入れられたものであるといいのですが!
ジェイ

これは、このような驚くべき答えである
マスターヨーダ

36

最も簡単な方法は、「配管」コマンドupdate-refを使用して現在のブランチを削除することです。

git branch -D現在のブランチの削除を停止する安全バルブがあるため、使用できません。

これにより、「初期コミット」状態に戻り、新しい初期コミットを開始できます。

git update-ref -d refs/heads/master
git commit -m "New initial commit"


15

6ワードの1行で

git checkout --orphan new_root_branch  &&  git commit

@AlexanderMillsは、あなたが読んでくださいgit help checkoutについて--orphan
KYB

そのドキュメントにリンクして、これを読む全員が手動で検索する必要がないようにできますか?
Alexander Mills

1
簡単です。こちらですgit help checkout --orphan
kyb

8

バックアップを作成する

git branch backup

指定したコミットにリセット

git reset --soft <#root>

次に、すべてのファイルをステージングに追加します

git add .

メッセージを更新せずにコミット

git commit --amend --no-edit

押しつぶされたコミットで新しいブランチをリポジトリにプッシュする

git push -f

これは以前のコミットメッセージを保持しますか?
not2qubit 2018年

1
@ not2qubitいいえこれは以前のコミットメッセージを保持しません。コミット#1、コミット#2、コミット#3の代わりに、これらのコミットのすべての変更を単一のコミット#1にパックします。コミット#1は<root>リセットしたコミットになります。git commit --amend --no-edit現在のコミットに対するすべての変更を<root>コミットします。コミットメッセージを編集する必要はありません。
デビッドモートン

5

グラフトを使用してスカッシュするには

ファイルを追加し、.git/info/graftsルートにしたいコミットハッシュをそこに置きます

git log 今、そのコミットから始まります

「本当の」実行にするには git filter-branch


1

この回答は、1つのコミット(no-parents no-history)の作成に加えて、そのコミットのすべてのcommit-data 保持したい場合を想定して、上記の2つを改善します(投票してください)。

  • 著者(名前と電子メール)
  • 作成日
  • コミッター(名前と電子メール)
  • 認定日
  • 通信ログメッセージ

もちろん、新規/単一コミットのcommit-SHAは変更されます。これは、新しい(非)履歴を表し、親なし/ root-commitになるためです。

これはgit log、のいくつかの変数を読み取って設定することで実行できますgit commit-tree。上記のcommit-dataを保持したまま、master新しいブランチから単一のコミットを作成するとしますone-commit

git checkout -b one-commit master ## create new branch to reset
git reset --hard \
$(eval "$(git log master -n1 --format='\
COMMIT_MESSAGE="%B" \
GIT_AUTHOR_NAME="%an" \
GIT_AUTHOR_EMAIL="%ae" \
GIT_AUTHOR_DATE="%ad" \
GIT_COMMITTER_NAME="%cn" \
GIT_COMMITTER_EMAIL="%ce" \
GIT_COMMITTER_DATE="%cd"')" 'git commit-tree master^{tree} <<COMMITMESSAGE
$COMMIT_MESSAGE
COMMITMESSAGE
')

1

これを行うには、ローカルのgitリポジトリを最初のコミットハッシュタグにリセットします。そのため、そのコミット後のすべての変更はステージング解除され、次に--amendオプションを使用してコミットできます。

git reset your-first-commit-hashtag
git add .
git commit --amend

そして、必要に応じて最初のコミットナムを編集し、ファイルを保存します。


1

私にとっては、次のように機能しました。合計4つのコミットがあり、インタラクティブなリベースを使用しました。

git rebase -i HEAD~3

一番最初のコミットが残っており、私は最新のコミットを3つ取りました。

次に表示されるエディターで立ち往生している場合は、次のように表示されます。

pick fda59df commit 1
pick x536897 commit 2
pick c01a668 commit 3

あなたは最初のコミットを取り、それに他の人をつぶす必要があります。あなたが持っているべきものは:

pick fda59df commit 1
squash x536897 commit 2
squash c01a668 commit 3

そのためには、INSERTキーを使用して「挿入」モードと「編集」モードを変更します。

エディタを保存して終了するには、を使用します:wq。カーソルがこれらのコミット行の間または他の場所にある場合は、ESCを押して再試行してください。

結果として、2つのコミットがありました。1つ目は残ったコミット、もう1つは「これは3つのコミットの組み合わせです」というメッセージが表示されます。

詳細はこちらで確認してください:https : //makandracards.com/makandra/527-squash-several-git-commits-into-a-single-commit


0

私は通常、次のようにします。

  • すべてがコミットされていることを確認し、問題が発生した場合に備えて最新のコミットIDを書き留めるか、バックアップとして別のブランチを作成します

  • 実行git reset --soft `git rev-list --max-parents=0 --abbrev-commit HEAD`して頭を最初のコミットにリセットしますが、インデックスは変更しません。最初のコミット以降のすべての変更は、コミットの準備ができているように見えます。

  • 実行してgit commit --amend -m "initial commit"、あなたにコミットを修正する最初のコミットとのメッセージをコミットするか、既存のコミットメッセージを維持したい場合は、あなたが実行することができます変更git commit --amend --no-edit

  • 実行git push -fして変更を強制的にプッシュします

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