回答:
を使用しgit rebase
ます。これは、Gitの一般的な「コミットを取得して、別の親(ベース)にコミットする」コマンドです。
ただし、いくつか知っておくべきこと:
コミットSHAにはその親が含まれるため、特定のコミットの親を変更すると、そのSHAが変更されます- 開発ラインで後に続くすべてのコミットのSHAも変更されます。
他の人と共同作業をしていて、問題のコミットをプルした場所にすでに公開している場合、コミットの変更はおそらくBad Idea™です。これは#1が原因であり、したがって、SHAが「同じ」コミットでSHAと一致しないために何が起こったかを理解しようとすると、他のユーザーのリポジトリが遭遇する混乱が生じます。(詳細については、リンクされたmanページの「UPSREAM REBASEからの回復」セクションを参照してください。)
そうは言っても、現在ブランチを使用していて、新しい親に移動したいコミットがいくつかある場合は、次のようになります。
git rebase --onto <new-parent> <old-parent>
これにより、現在のブランチの後の すべて<old-parent>
が<new-parent>
代わりに上に移動します。
--onto
使われているのです!この回答を読んだ後でも、ドキュメントは常に完全に不明瞭でした!
git rebase --onto <new-parent> <old-parent>
を使用する方法についてすべて私に言っrebase --onto
た。
rebase this commit on that one
、またはgit rebase --on <new-parent> <commit>
。ここで古い親を使用しても意味がありません。
git rebase <new-parent>
ます。
後続のコミットのリベースを回避する必要があることが判明した場合(たとえば、履歴の書き換えができないため)、git replaceを使用できます(Git 1.6.5以降で使用可能)。
# …---o---A---o---o---…
#
# …---o---B---b---b---…
#
# We want to transplant B to be "on top of" A.
# The tree of descendants from B (and A) can be arbitrarily complex.
replace_first_parent() {
old_parent=$(git rev-parse --verify "${1}^1") || return 1
new_parent=$(git rev-parse --verify "${2}^0") || return 2
new_commit=$(
git cat-file commit "$1" |
sed -e '1,/^$/s/^parent '"$old_parent"'$/parent '"$new_parent"'/' |
git hash-object -t commit -w --stdin
) || return 3
git replace "$1" "$new_commit"
}
replace_first_parent B A
# …---o---A---o---o---…
# \
# C---b---b---…
#
# C is the replacement for B.
上記の置換が確立されると、オブジェクトBに対する要求は実際にはオブジェクトCを返します。Cの内容は、最初の親(同じ親(最初のものを除く))、同じツリーを除いて、Bの内容とまったく同じです。同じコミットメッセージ)。
置換はデフォルトでアクティブですが、gitの--no-replace-objects
オプション(コマンド名の前)を使用するか、環境変数を設定することで無効にできます。(通常に加えて)押すことで交換が共有できます。GIT_NO_REPLACE_OBJECTS
refs/replace/*
refs/heads/*
commit-munging(上記のsedで実行)が気に入らない場合は、より高レベルのコマンドを使用して、置換コミットを作成できます。
git checkout B~0
git reset --soft A
git commit -C B
git replace B HEAD
git checkout -
大きな違いは、Bがマージコミットの場合、このシーケンスは追加の親を伝達しないことです。
refs/replace/
ref階層をプッシュ(およびフェッチ)するように調整する必要があります。一度だけ実行する必要がある場合git push yourremote 'refs/replace/*'
は、ソースリポジトリとgit fetch yourremote 'refs/replace/*:refs/replace/*'
宛先リポジトリで実行できます。複数回行う必要がある場合は、代わりにそれらのrefspecを構成remote.yourremote.push
変数(ソースリポジトリ内)および構成remote.yourremote.fetch
変数(宛先リポジトリ内)に追加できます。
git replace --grafts
です。これがいつ追加されたかはわかりませんが、その目的は、コミットBと同じであるが指定された親を持つ置換を作成することです。
Gitでコミットを変更するには、それに続くすべてのコミットも変更する必要があることに注意してください。歴史のこの部分を公開した場合、これはお勧めできません。誰かが変更前の歴史に基づいて作品を作成している可能性があります。
代替ソリューションgit rebase
で述べた琥珀の応答が使用される移植片メカニズム(Gitリポジトリの定義がで移植片見るGitの用語集との文書.git/info/grafts
内のファイルのGitリポジトリレイアウトコミットの変更親へのドキュメント)、それはいくつかの歴史ビューアで正しいことをしたことを確認し(gitk
、git log --graph
など)、次にgit filter-branch
(そのマンページの「例」セクションで説明されているように)使用して永続化します(その後、graftを削除し、オプションで、によってバックアップされた元の参照を削除するgit filter-branch
か、リポジトリを再クローンします)。
echo "$ commit-id $ graft-id" >> .git / info / grafts git filter-branch $ graft-id..HEAD
注意 !!!このソリューションは、変更をリベース/移植するという点でリベースソリューションとは異なりますが、移植ベースのソリューションは、古い親と新しい親の違いを考慮せずに、コミットをそのまま親に変更します。git rebase
git replace
gitグラフトに取って代わっています(git 1.6.5以降を使用している場合)。
git-replace
+ を使用できないのはなぜgit-filter-branch
ですか?私のテストでfilter-branch
は、ツリー全体をリベースして、置換を尊重しているように見えました。
git filter-branch
移植片と置換を尊重して、履歴を書き換えます(ただし、書き換えられた履歴では、置換は無効です)。pushはfasf-forward変更になります。git replace
インプレースの転送可能な置換を作成します。プッシュは早送りされますが、refs/replace
履歴を修正するには、置換を転送するためにプッシュする必要があります。HTH
上記の答えを明確にし、恥ずかしくないように私自身のスクリプトをプラグインするには:
これは、「リベース」するか「リペアレント」するかによって異なります。Amberによって提案されたリベースは、差分を移動します。JakubとChrisが示唆するように、親はツリー全体のスナップショットを移動します。親を変更したい場合は、手動で作業するのではなく、使用することをお勧めします。git reparent
左側の画像があり、右側の画像のようにしたいとします。
C'
/
A---B---C A---B---C
リベースと親変更の両方で同じ画像が生成されますが、の定義はC'
異なります。ではgit rebase --onto A B
、C'
によって導入されB
た変更は含まれません。を使用するとgit reparent -p A
、C'
と同じになりますC
(ただしB
、履歴には含まれません)。
reparent
スクリプトを試してみました。美しく機能します。強くお勧めします。また、新しいbranch
ラベルを作成し、checkout
呼び出す前にそのブランチに追加することをお勧めします(ブランチラベルが移動するため)。
確かに、@ Jakubの回答は、数時間前、私がOPとまったく同じことを試みていたときに役に立ちました。
ただし、git replace --graft
現在は移植に関するより簡単な解決策です。また、その解決策の主な問題は、フィルターブランチによって、HEADのブランチにマージされなかったすべてのブランチが失われることでした。その後、git filter-repo
完全かつ完璧に仕事をしました。
$ git replace --graft <commit> <new parent commit>
$ git filter-repo --force
Fore more info:チェックアウトのセクションの「再移植履歴」のドキュメント