1. ホーム
  2. ギット

[解決済み】Gitでルートコミットの前にコミットを挿入する?

2022-04-06 03:17:11

質問

以前にも質問したことがあるのですが 最初の2つのコミットをつぶす を git リポジトリで使用することができます。

この解決策はなかなか面白いもので、git の他の機能ほど頭を悩ませるものではありませんが、それでも、プロジェクトの開発中に何度もこの手順を繰り返す必要がある場合は、ちょっとした痛手となります。

だから、一回だけ痛い目にあって、あとはずっと標準の対話型リベースを使えるようにしたいんです。

そこで私がやりたいのは、最初のコミットという目的だけのために存在する、空の初期コミットです。コードも何もありません。ただ、リベースのベースとなるようにスペースを取っておくだけです。

そこで質問ですが、既存のリポジトリがある場合、最初のコミットの前に新しい空のコミットを挿入し、他の全員を前に移動させるにはどうすればいいのでしょうか?

どのように解決するのですか?

これを実現するには、2つのステップがあります。

  1. 空のコミットを新規に作成する
  2. この空のコミットから始まるように履歴を書き換える

新しい空のコミットを一時的なブランチに置くことにします。 newroot 便宜上

1. 新しい空のコミットを作成する

これにはいくつかの方法があります。

配管のみの使用

最もクリーンな方法は、Git の配管を使ってコミットを直接作成することです。これは、作業コピーやインデックス、どのブランチがチェックアウトされているかなどを触ることを避けるためです。

  1. 空のディレクトリのツリーオブジェクトを作成します。

    tree=`git hash-object -wt tree --stdin < /dev/null`
    
    
  2. コミットを巻き付ける。

    commit=`git commit-tree -m 'root commit' $tree`
    
    
  3. 参照を作成します。

    git branch newroot $commit
    
    

もちろん、シェルのことをよく理解していれば、この手順全体をワンライナーにアレンジすることもできます。

配管なし

通常のポーセリンコマンドの場合、空のコミットを作成するためには、チェックアウトせずに newroot ブランチを作成し、インデックスと作業コピーを繰り返し更新するという、意味不明なことをしています。しかし、この方が分かりやすいと思う人もいるでしょう。

git checkout --orphan newroot
git rm -rf .
git clean -fd
git commit --allow-empty -m 'root commit'

Git の非常に古いバージョンで --orphan に切り替えることができます。 checkout の場合、最初の行をこれに置き換えてください。

git symbolic-ref HEAD refs/heads/newroot

2. この空のコミットから始まるように履歴を書き換える

リベースするか、履歴をきれいに書き直すか、2つの選択肢があります。

リベース

git rebase --onto newroot --root master

これはシンプルであるという長所があります。しかし、この方法ではブランチの最後のコミットごとにコミッター名と日付を更新してしまいます。

また、いくつかのエッジケース・ヒストリーでは、何も含まないコミットにリベースしているにもかかわらず、マージの競合が原因で失敗することさえあります。

履歴の書き換え

よりクリーンな方法は、ブランチを書き換えることです。とは異なり git rebase ブランチがどのコミットから始まっているかを調べる必要があります。

git replace <currentroot> --graft newroot
git filter-branch master

書き換えが行われるのは明らかに第2段階であり、説明が必要なのは第1段階である。何 git replace は、置き換えたいオブジェクトへの参照を見つけたらいつでも、代わりにそのオブジェクトの置き換えを見るようにGitに伝えます。

を使用すると --graft スイッチでは、通常とは少し異なることを指示しています。置換オブジェクトはまだないけれども <currentroot> コミットオブジェクトをそれ自身の完全なコピーで ただし 置換の親コミットには、リストアップしたもの (すなわち newroot コミット)。次に git replace が先に進んでこのコミットを作成し、そのコミットを元のコミットの代わりとすることを宣言します。

今、もしあなたが git log ブランチの始まりは newroot .

ただし、以下の点に注意してください。 git replace は実際には履歴を変更しません。 - また、リポジトリから外に伝搬することもありません。単に、あるオブジェクトから別のオブジェクトへのローカルリダイレクトをリポジトリに追加するだけです。これが意味するのは、この置換の効果を他の誰も見ていないということです。

そのため filter-branch のステップが必要です。とは git replace を実行すると、ルートコミットの親コミットを調整した完全なコピーが作成されます。 git filter-branch を実行し、それに続くすべてのコミットに対してこのプロセスを繰り返します。ここで実際に歴史が書き換えられ、それを共有できるようになります。