リポジトリ全体を最初のコミットまでどのように押しつぶしますか?
最初のコミットにリベースできますが、2つのコミットが残ります。最初のコミットの前にコミットを参照する方法はありますか?
リポジトリ全体を最初のコミットまでどのように押しつぶしますか?
最初のコミットにリベースできますが、2つのコミットが残ります。最初のコミットの前にコミットを参照する方法はありますか?
git 1.6.2以降、を使用できますgit rebase --root -i
。
最初のコミットを除くコミットごとに、に変更pick
しsquash
ます。
エイリアスを作成しました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
」は単なる例です。自由に自分の言語を使用してください。
押しつぶす必要はありませんgit commit-tree
。孤立したコミットを作成して使用するために使用します。
経由で単一のコミットを作成するgit commit-tree
は何ですかgit commit-tree HEAD^{tree} -m "A new start"
:
提供されたツリーオブジェクトに基づいて新しいコミットオブジェクトを作成し、stdoutで新しいコミットオブジェクトIDを発行します。-mまたは-Fオプションが指定されていない限り、ログメッセージは標準入力から読み取られます。
式HEAD^{tree}
は、に対応するツリーオブジェクトHEAD
、つまり現在のブランチの先端を意味します。Tree-ObjectsおよびCommit- Objectsを参照してください。
次にgit reset
、現在のブランチを新しく作成されたコミットオブジェクトにリセットするだけです。
このように、ワークスペースの何も触れられず、リベース/スカッシュの必要もありません。これにより、ワークスペースが非常に高速になります。また、必要な時間は、リポジトリのサイズや履歴の深さとは関係ありません。
これは、別のリポジトリをテンプレート/アーキタイプ/シード/スケルトンとして使用して、新しいプロジェクトで「初期コミット」を作成するのに役立ちます。例えば:
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
これにより、テンプレートリポジトリをリモート(またはその他)として追加することが回避され、テンプレートリポジトリの履歴が最初のコミットに折りたたまれます。
すべてのコミットをルートコミットまで押しつぶすだけの場合は、
git rebase --interactive --root
リベース操作は、インタラクティブなリベースエディタのコミットリストを生成し、リベース自体を実行するのに非常に時間がかかる可能性があるため、動作する可能性があります。多数のコミット(たとえば、数百のコミット)では実用的ではありません。
多数のコミットを潰す場合の、より迅速で効率的な2つのソリューションを次に示します。
現在のブランチの先端(つまり、最新のコミット)に新しい孤立したブランチを作成するだけです。この孤立したブランチは、まったく新しい別個のコミット履歴ツリーの最初のルートコミットを形成します。これは、すべてのコミットを潰すことと実質的に同等です。
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
ドキュメンテーション:
もう1つの効率的な解決策は、ルートコミットに対して混合リセットまたはソフトリセットを使用すること<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
ドキュメンテーション:
おそらく最も簡単な方法は、作業コピーの現在の状態で新しいリポジトリを作成することです。すべてのコミットメッセージを保持したい場合は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
echo "message" | git commit-tree HEAD^{tree}
これにより、HEADのツリーで孤立したコミットが作成され、その名前(SHA-1)がstdoutに出力されます。次に、そこでブランチをリセットします。
git reset SHA-1
上記を1つのステップで実行するには:
git reset $(git commit-tree HEAD^{tree} -m "Initial commit.")
これが、他の誰かのために機能する場合に備えて、私がこれを行うことになった方法です:
このようなことを行うことには常にリスクがあり、開始する前に保存ブランチを作成することは決して悪い考えではないことを忘れないでください。
ロギングから始めます
git log --oneline
最初のコミットまでスクロールし、SHAをコピーします
git reset --soft <#sha#>
<#sha#>
ログからコピーされたSHAで置き換えます
git status
すべてが緑色であることを確認してください。そうでない場合は、実行してくださいgit add -A
git commit --amend
現在のすべての変更を現在の最初のコミットに修正します
次に、このブランチを強制的にプッシュすると、そこにあるものが上書きされます。
私は移植片の使用について何かを読みましたが、それをあまり調査しませんでした。
とにかく、これらの最後の2つのコミットを次のように手動で押しつぶすことができます。
git reset HEAD~1
git add -A
git commit --amend
最も簡単な方法は、「plumbing」コマンドupdate-ref
を使用して現在のブランチを削除することです。
git branch -D
現在のブランチの削除を停止するための安全弁があるため、使用できません。
これにより、「初期コミット」状態に戻り、新しい初期コミットから開始できます。
git update-ref -d refs/heads/master
git commit -m "New initial commit"
6語の1行で:
git checkout --orphan new_root_branch && git commit
まず、を使用してすべてのコミットを1つのコミットに押しつぶしますgit rebase --interactive
。これで、スカッシュするための2つのコミットが残ります。これを行うには、いずれかをお読みください
バックアップを作成する
git branch backup
指定されたコミットにリセット
git reset --soft <#root>
次に、すべてのファイルをステージングに追加します
git add .
メッセージを更新せずにコミットする
git commit --amend --no-edit
押しつぶされたコミットで新しいブランチをリポジトリにプッシュする
git push -f
これを行うには、ローカルgitリポジトリを最初のコミットハッシュタグにリセットして、そのコミット後のすべての変更がステージングされないようにしてから、-amendオプションを使用してコミットできます。
git reset your-first-commit-hashtag
git add .
git commit --amend
次に、必要に応じて最初のコミット名を編集し、ファイルを保存します。
ファイルを追加し.git/info/grafts
、ルートにしたいコミットハッシュをそこに置きます
git log
そのコミットから開始します
それを「本物の」実行にするにはgit filter-branch
私は通常このようにします:
すべてがコミットされていることを確認し、問題が発生した場合に備えて最新のコミット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
して変更を強制的にプッシュします
ブランチに3つのコミットがあり、すでにリモートブランチにプッシュされているとします。
例:
git log -4
次のような結果が表示されます:
<your_third_commit_sha>
<your_second_commit_sha>
<your_first_commit_sha>
<master_branch_commit_sha - your branch created from master>
最後の3つのコミットを1つのコミットに押しつぶして、リモートブランチにプッシュします。手順は次のとおりです。
git reset --soft <master_branch_commit_sha>
これで、すべてのコミットの変更が統合されますが、コミットされません。確認方法:
git status
メッセージを使用してすべての変更をコミットします。
git commit -m 'specify details'
シングルコミットをリモートブランチに強制的にプッシュします。
git push -f
私にとっては、次のように機能しました。合計4つのコミットがあり、インタラクティブなリベースを使用しました。
git rebase -i HEAD~3
最初のコミットが残っており、最新のコミットを3回実行しました。
次に表示されるエディターでスタックしている場合は、次のようなsmthが表示されます。
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つのコミットがありました。最初のコミットは残り、2番目のコミットには「これは3つのコミットの組み合わせです」というメッセージが表示されます。
詳細はこちらで確認してください: https ://makandracards.com/makandra/527-squash-several-git-commits-into-a-single-commit
この回答は、1つのコミット(親なし、履歴なし)を作成するだけでなく、そのコミットのすべてのコミットデータも保持することを前提として、上記のいくつかを改善します(投票してください)。
もちろん、新しい/単一のコミットのcommit-SHAは変更されます。これは、新しい(非)履歴を表し、親なし/ルートコミットになるためです。
git log
これは、のいくつかの変数を読み取って設定することで実行できますgit commit-tree
。master
新しいブランチから単一のコミットを作成する場合one-commit
、上記のcommit-dataを保持します。
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
')
これは私にとって最もうまくいきました。
git rebase -X ours -i master
これにより、gitはマスターよりも機能ブランチを優先します。面倒なマージ編集を回避します。ブランチはマスターと最新である必要があります。
ours
This resolves any number of heads, but the resulting tree of the merge is always that of the current
branch head, effectively ignoring all changes from all other branches. It is meant to be used to
supersede old development history of side branches. Note that this is different from the -Xours
option to the recursive merge strategy.
ここで提案されたソリューションに問題があったので、本当に単純なソリューションを共有したいと思います(機能ブランチのすべてのコミットを1つにまとめる):
git merge origin/master && git reset --soft origin/master
前述のmergecmdは、コミット時にマスターからの最近の変更が頭に浮かばないことを保証します。その後、変更をコミットして実行しますgit push -f
私は通常、gitリポジトリからテンプレートを復元するときにツリー全体を潰して、よりクリーンな履歴を取得し、法令遵守を確保します。私のワークフローは次のようになります。
git clone https://git.invalid/my-awesome-template.git my-awesome-foo
cd !$
git branch -M master my-awesome-template/master
git checkout --orphan master
git rm -rf /
git commit --allow-empty --allow-empty-message -m 'Initial commit'
git merge --squash --allow-unrelated-histories my-awesome-template/master
git commit
git branch -D my-awesome-template/master
# you can now `drop' that "Initial commit":
git rebase -i --root
これにより、履歴全体が1つの大きなコミットメッセージにまとめられます。
この特定の例では:
master
ワーキングブランチですmy-awesome-template/master
中間ブランチです