263

以前、git リポジトリで最初の 2 つのコミットをつぶす方法について質問したことがあります。

解決策はかなり興味深いものであり、git の他のいくつかのものほど気が遠くなるようなものではありませんが、プロジェクトの開発中に手順を何度も繰り返す必要がある場合は、まだ少し厄介です。

したがって、私は一度だけ苦労して、標準のインタラクティブなリベースを永遠に使用できるようにしたいと考えています。

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

私の質問は、既存のリポジトリがある場合、最初のコミットの前に新しい空のコミットを挿入し、他のすべての人を前に移動するにはどうすればよいですか?

4

15 に答える 15

371

これを達成するには 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'

--orphanへのスイッチがない非常に古いバージョンの Gitcheckoutでは、最初の行を次のように置き換える必要があることに注意してください。

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 番目のステップで行われます。説明が必要な最初のステップです。git replaceつまり、置き換えたいオブジェクトへの参照が見つかった場合は常に、Git は代わりにそのオブジェクトの置き換えを調べる必要があることを Git に伝えます。

スイッチを使用する--graftと、通常とは少し異なることを伝えます。あなたはまだ置換オブジェクトを持っていないと言っていますが、<currentroot>コミットオブジェクトをそれ自体の正確なコピーで置き換えたいと思っていますが、置換の親コミットはリストしたものでなければなりません(つまり、newrootコミット)。次にgit replace、このコミットを作成し、そのコミットを元のコミットの代わりとして宣言します。

ここで を実行するgit logと、すでに希望どおりに見えることがわかります。ブランチは から始まりnewrootます。

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

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

于 2009-03-15T07:45:24.160 に答える
34

Aristotle Pagaltzis と Uwe Kleine-König の回答と Richard Bronosky のコメントをマージします。

git symbolic-ref HEAD refs/heads/newroot
git rm --cached -r .
git clean -f -d
# touch .gitignore && git add .gitignore # if necessary
git commit --allow-empty -m 'initial'
git rebase --onto newroot --root master
git branch -d newroot

(すべてを 1 か所にまとめるため)

于 2012-03-16T11:02:53.263 に答える
12

私はアリストテレスの答えが好きです。しかし、大規模なリポジトリ (コミット数が 5000 を超える) の場合、filter-branch は、1) 高速である、2) マージの競合が発生した場合に人間の介入を必要としないという理由で、rebase よりもうまく機能することがわかりました。3) タグを書き換えることができます -- タグを保存します。各コミットの内容に疑問がないため、filter-branch が機能することに注意してください。これは、この「リベース」の前とまったく同じです。

私の手順は次のとおりです。

# first you need a new empty branch; let's call it `newroot`
git symbolic-ref HEAD refs/heads/newroot
git rm --cached -r .
git clean -f -d

# then you apply the same steps
git commit --allow-empty -m 'root commit'

# then use filter-branch to rebase everything on newroot
git filter-branch --parent-filter 'sed "s/^\$/-p <sha of newroot>/"' --tag-name-filter cat master

「--tag-name-filter cat」オプションは、新しく作成されたコミットを指すようにタグが書き換えられることを意味することに注意してください。

于 2013-03-29T15:55:35.817 に答える
7

「git init」の直後に空のコミットを作成するのを忘れた場合に、リポジトリの開始時に空のコミットを追加するには:

git rebase --root --onto $(git commit-tree -m 'Initial commit (empty)' 4b825dc642cb6eb9a060e54bf8d69288fbee4904)
于 2017-12-09T21:00:41.557 に答える
5

私はアリストテレスとケントの答えの一部をうまく使いました:

# first you need a new empty branch; let's call it `newroot`
git checkout --orphan newroot
git rm -rf .
git commit --allow-empty -m 'root commit'
git filter-branch --parent-filter \
'sed "s/^\$/-p <sha of newroot>/"' --tag-name-filter cat -- --all
# clean up
git checkout master
git branch -D newroot
# make sure your branches are OK first before this...
git for-each-ref --format="%(refname)" refs/original/ | \
xargs -n 1 git update-ref -d

masterこれにより、タグに加えてすべてのブランチ ( だけでなく) も書き換えられます。

于 2014-04-17T22:44:10.883 に答える
4

私は興奮して、この素敵なスクリプトの「冪等」バージョンを書きました...常に同じ空のコミットを挿入し、2 回実行しても毎回コミット ハッシュが変更されることはありません。だから、これがgit-insert-empty-rootに対する私の見解です:

#!/bin/sh -ev
# idempotence achieved!
tmp_branch=__tmp_empty_root
git symbolic-ref HEAD refs/heads/$tmp_branch
git rm --cached -r . || true
git clean -f -d
touch -d '1970-01-01 UTC' .
GIT_COMMITTER_DATE='1970-01-01T00:00:00 +0000' git commit \
  --date='1970-01-01T00:00:00 +0000' --allow-empty -m 'initial'
git rebase --committer-date-is-author-date --onto $tmp_branch --root master
git branch -d $tmp_branch

余分な複雑さの価値はありますか? ないかもしれませんが、私はこれを使用します。

これにより、レポの複数の複製コピーに対してこの操作を実行できるようにする必要があり、最終的に同じ結果になるため、互換性があります...テスト...はい、動作しますが、削除して追加する必要もあります再びリモコン、例えば:

git remote rm origin
git remote add --track master user@host:path/to/repo
于 2013-02-06T12:28:56.370 に答える
4

git rebase --root --onto $emptyrootcommit

簡単にトリックを行う必要があります

于 2011-01-17T19:27:47.877 に答える
3

ルートコミットを切り替えるには:

まず、最初に必要なコミットを作成します。

次に、次を使用してコミットの順序を切り替えます。

git rebase -i --root

次のように、ルート コミットまでのコミットとともにエディターが表示されます。

1234 古いルート メッセージを選択

pick 0294 途中でコミット

ルートに配置する 5678 コミットを選択します

次に、最初の行に配置することで、必要なコミットを最初に配置できます。例では:

ルートに配置する 5678 コミットを選択します

1234 古いルート メッセージを選択

pick 0294 途中でコミット

エディターを終了すると、コミット順序が変更されます。

PS: git が使用するエディターを変更するには、次を実行します。

git config --global core.editor name_of_the_editor_program_you_want_to_use

于 2017-02-18T16:17:06.093 に答える
2

さて、これが私が思いついたものです:

# Just setting variables on top for clarity.
# Set this to the path to your original repository.
ORIGINAL_REPO=/path/to/original/repository

# Create a new repository…
mkdir fun
cd fun
git init
# …and add an initial empty commit to it
git commit --allow-empty -m "The first evil."

# Add the original repository as a remote
git remote add previous $ORIGINAL_REPO
git fetch previous

# Get the hash for the first commit in the original repository
FIRST=`git log previous/master --pretty=format:%H  --reverse | head -1`
# Cherry-pick it
git cherry-pick $FIRST
# Then rebase the remainder of the original branch on top of the newly 
# cherry-picked, previously first commit, which is happily the second 
# on this branch, right after the empty one.
git rebase --onto master master previous/master

# rebase --onto leaves your head detached, I don't really know why)
# So now you overwrite your master branch with the newly rebased tree.
# You're now kinda done.
git branch -f master
git checkout master
# But do clean up: remove the remote, you don't need it anymore
git remote rm previous
于 2009-03-14T08:08:44.007 に答える
0

Aristotle Pagaltzis などの回答に従いますが、より単純なコマンドを使用します

zsh% git checkout --orphan empty     
Switched to a new branch 'empty'
zsh% git rm --cached -r .
zsh% git clean -fdx
zsh% git commit --allow-empty -m 'initial empty commit'
[empty (root-commit) 64ea894] initial empty commit
zsh% git checkout master
Switched to branch 'master'
zsh% git rebase empty
First, rewinding head to replay your work on top of it...
zsh% git branch -d empty 
Deleted branch empty (was 64ea894).

リポジトリには、コミットされるのを待っているローカルの変更が含まれていてはならないことに注意してください。
Notegit checkout --orphanは git の新しいバージョンで動作すると思います。ほとんどの場合、有用なヒントが得られること
に注意してください。git status

于 2013-02-18T08:28:04.107 に答える
-6

新しいリポジトリを開始します。

日付を希望の開始日に戻します。

やりたいと思ったときにシステム時間を調整して、やりたいと思ったときにすべてを行います。多くの不要な入力を避けるために、必要に応じて既存のリポジトリからファイルをプルします。

今日になったら、リポジトリを交換すれば完了です。

あなたがただ頭がおかしい (確立されている) だけでなく、かなり頭が良い (おそらく、このようなクレイジーなアイデアを思いつくにはある程度の知性が必要なため) 場合は、プロセスをスクリプト化します。

それはまた、過去が今から1週間後に別の方法で起こったと決めたときにも、より良いものになります.

于 2009-03-14T05:49:02.907 に答える
-7

この投稿が古いことは知っていますが、このページは、グーグルで「コミット git の挿入」を行った最初の投稿です。

なぜ単純なことを複雑にするのですか?

ABC があり、ABZC が必要です。

  1. git rebase -i trunk(またはBの前のもの)
  2. Bラインでピックを編集に変更
  3. 変更を加えます:git add ..
  4. git commit(git commit --amendこれは B を編集し、Z を作成しません)

[git commitここで好きなだけ作成して、さらにコミットを挿入できます。もちろん、手順 5 で問題が発生する場合もありますが、git を使用してマージ競合を解決することは、必要なスキルです。そうでない場合は、練習してください! ]

  1. git rebase --continue

シンプルですね。

理解していればgit rebase、「ルート」コミットを追加しても問題ありません。

git を楽しんでください。

于 2011-06-27T09:40:24.530 に答える