Git 1.7.11で追加された新しい git-subtree コマンドを学習しようとしています。サブツリーを追加した後、リベースする機能を失ったようです。 READMEファイルを含むプライマリリポジトリと、READMEファイルを含むライブラリリポジトリがあります。subtree add
を使用してlibディレクトリに追加します。
$ git subtree add -P lib/mylib myliborigin master
これは正常に機能しますが、履歴は次のようになります。
* 22c1fe6 (HEAD, master) Merge commit 'b6e698d9f4985825efa06dfdd7bba8d2930cd40e' as 'lib/mylib' -
|\
| * b6e698d Squashed 'lib/mylib/' content from commit d7dbd3d
* b99d55b Add readme
* 020e372 Initial
ここで、Origin/master
に対してリポをリベースしたいが、スカッシュコミットが適用されない親コミットに対して直接適用されるため、失敗します。これは、リポのルートではなく、リポのルートに適用されるためですサブツリーを追加するときにそれに。
この理由は、スカッシュコミットを見るとかなり明確です。プレフィックスに関する情報はありません。これは、元のmylibコミットが押しつぶされただけです。次のマージコミットだけがそれについて知っていますが、リベースはそれをここでは考慮しません。
回避策はありますか(サブツリーのコミットをリベースしないことを除いて)?
これは解決策ではありませんが、私が使用する現在の回避策は...
最初の例を使用します。
* 22c1fe6 (HEAD, master) Merge commit 'b6e698d9f4985825efa06dfdd7bba8d2930cd40e' as 'lib/mylib' -
|\
| * b6e698d Squashed 'lib/mylib/' content from commit d7dbd3d
* b99d55b Add readme
* 020e372 Initial
サブツリーを追加する前に、2番目のコミットにインタラクティブにリベースします。
$ git rebase -i 020e372
2つのサブツリーエントリを削除し、前のコミットの編集をマークします。
e b99d55b Add readme
ファイルを保存して閉じると、「Readmeの追加」コミットに到達したら、amendコマンドを実行します。
$ git commit --amend
次に、新しいサブツリーを再度追加します。
$ git subtree add -P lib/mylib myliborigin master
リベースを続行します。
$ git rebase --continue
その後、ブランチはマスターからリベースする必要があり、サブツリーはスカッシュ+マージがそのままの状態で「通常」になります。
* 22c1fe6 (HEAD, master) Merge commit 'b6e698d9f4985825efa06dfdd7bba8d2930cd40e' as 'lib/mylib' -
|\
| * b6e698d Squashed 'lib/mylib/' content from commit d7dbd3d
これは古い質問ですが、リポジトリに同じ問題があっただけで、ついにすべてのサブツリーのメタデータを(できれば)保持する完全なソリューションが見つかりました。
次のコミットツリーがあるとします。
B (master) Add README.md
|
A Initial commit
そして、lib/
にあるサブツリーでfeature
ブランチをフォークしました:
git remote add -f githublib https://github.com/lib/lib.git
git subtree add --prefix lib/ githublib master --squash
これは、2つの親を持つマージコミットDを作成します。現在のmaster
(B)と、外部リポジトリの押しつぶされた履歴を持つ無関係なコミットF
です。このコミットには、コミットメッセージにgit subtree
メタデータも含まれています(つまり、git-subtree-dir
およびgit-subtree-split
)。
D (feature) Merged commit 'F' as 'lib/'
/ \
/ F Squashed 'lib/' content from GGGGGG
B (master) Add README.md
|
A Initial commit
その後、いくつかのコミットを両方のブランチに個別に追加します。
E (feature) Remove .gitignore from lib/
C | (master) Add LICENSE.md
| D Merged commit 'F' as 'lib/'
| / \
|/ F Squashed 'lib/' content from GGGGGG
B Add README.md
|
A Initial commit
次に、feature
をmaster
にリベースします。方法は次のとおりです。
feature
から1つずつコミットをチェリーピックして、feature
の上にmaster
ブランチの新しいコピーを作成します。git branch -f feature C
git checkout feature
git cherry-pick D E
E' (feature) Remove .gitignore from lib/
|
D' Merged commit 'F' as 'lib/'
|
| E Remove .gitignore from lib/
C | (master) Add LICENSE.md
| D Merged commit 'F' as 'lib/'
| / \
|/ F Squashed 'lib/' content from GGGGGG
B Add README.md
|
A Initial commit
これでリベースに相当するものがありますが、git subtree
に必要な外部リポジトリに関するすべての情報が失われました。それを復元するには:
feature
の履歴を書き換えて永続化します。git checkout feature
git replace --graft D' C F
git filter-branch --tag-name-filter cat -- master..
これで、最初の状態とまったく同じ画像が表示されます。古いコミットDとEはまだ残っていますが、後でガベージコレクションすることができます。
E' (feature) Remove .gitignore from lib/
|
D' Merged commit 'F' as 'lib/'
|\
| \
C \ (master) Add LICENSE.md
| \
| \
| F Squashed 'lib/' content from GGGGGG
B Add README.md
|
A Initial commit
警告:これによりfeature
の履歴が書き換えられるため、このブランチで他の誰かがあなたと協力している場合は、公開しないように注意してください。ただし、最初にリベースを作成したかったので、おそらくそれに気づいているでしょう:-)
これは単純な場合に機能します。
git rebase --preserve-merges master
コメントで@Techlive Zhengに感謝します。
見えるかも
fatal: refusing to merge unrelated histories
Error redoing merge a95986e...
つまり、gitはサブツリーを自動的に適用できませんでした。これにより、@ ericpetersが彼の回答で説明した状況になります。解決:
サブツリーを再度追加します(最初に使用したのと同じコマンドを使用します)。
git subtree add -P lib lib-Origin master
リベースを続行します。
git rebase --continue
これで準備は完了です。
うまくいったかどうか疑問に思っている場合は、リベース後に元のバージョンと比較して、何も変更していないことを確認できます。
git diff <ref-before-rebase> <ref-after-rebase> -- .
(末尾の-- .
は、現在のディレクトリ内のファイルのみdiff
になるようにgitに指示します)。
他のすべてが失敗し、コミット自体を保持する必要がない場合は、元のサブツリーコミットを単純にgit cherry-pick
できます。
コミットメッセージはAdd 'lib/' from commit '9767e6...'
のようになります-これが必要なメッセージです。
どうやらこれは予期された動作です(「予期された動作」の誤った定義については)。参照: http://git.661346.n2.nabble.com/subtree-merges-lose-prefix-after-rebase-td7332850 .html 。
これは誰にとっても大きな助けになるわけではありません。私もこれの回避策を見つけたいです。
私は同様の問題を抱えていました:サブツリーの追加を行った後にリベースしたかったのですが、--preserve-mergesを使用すると、マージの競合が残りました(.gitignore
ファイルの競合などにより)。
私の場合、サブツリー機能を使用することを必ずしも計画していませんでした。私は、もともとスーパープロジェクトの一部であるはずのリポジトリを単に引き込んでいました。それが他の誰かを助ける場合に備えて、他の 関連する回答 に基づいて、私が最終的に何をしたかを見つけました。
同じディレクトリにmain_projectとsub_projectの2つのプロジェクトがあるとします。 main_project内のsub_projectという名前のディレクトリにsub_projectをプルしたいのですが、どちらのリポジトリにもsub_projectという名前のディレクトリがなかったと仮定します。
cd main_project
git fetch ../sub_project
git checkout -b sub_project FETCH_HEAD
git filter-branch --Prune-empty --tree-filter '
if [[ ! -e sub_project ]]; then
mkdir -p sub_project
git ls-tree --name-only $GIT_COMMIT | xargs -I files mv files sub_project
fi'
git checkout branch-to-merge-within
git merge sub_project
git branch -d sub_project
このアプローチで問題が見つかった場合は更新します。
Git 2.24.0(2019-11-04にリリース)はgit rebase --rebase-merges --strategy [strategy]
のサポートを追加しました。したがって、現在のブランチにサブツリーマージが含まれているときにgit rebase --rebase-merges --strategy subtree [branch]
を実行すると、正常に機能します。
私のプロジェクトでは、git subtree add
を使用しないことを決定しましたが、代わりにgit replace --edit
を使用してマージコミットの2番目の親を破棄します。私は Gitブックv1の廃止された「サブツリー」チュートリアル も使用しましたが、これは同じことを行いますが面倒です。
使用する必要があります
git rebase --preserve-merges --preserve-committer --onto new_place start end