web-dev-qa-db-ja.com

git-subtree add後にリベースする方法は?

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コミットが押しつぶされただけです。次のマージコミットだけがそれについて知っていますが、リベースはそれをここでは考慮しません。

回避策はありますか(サブツリーのコミットをリベースしないことを除いて)?

59
Epeli

これは解決策ではありませんが、私が使用する現在の回避策は...

最初の例を使用します。

*   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
9
ericpeters

これは古い質問ですが、リポジトリに同じ問題があっただけで、ついにすべてのサブツリーのメタデータを(できれば)保持する完全なソリューションが見つかりました。

次のコミットツリーがあるとします。

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

次に、featuremasterにリベースします。方法は次のとおりです。

1. 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に必要な外部リポジトリに関するすべての情報が失われました。それを復元するには:

2. 不足している親リンクを移植片として追加 し、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の履歴が書き換えられるため、このブランチで他の誰かがあなたと協力している場合は、公開しないように注意してください。ただし、最初にリベースを作成したかったので、おそらくそれに気づいているでしょう:-)

7
Skiminok

これは単純な場合に機能します。

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...'のようになります-これが必要なメッセージです。

4
Kevin Cooper

どうやらこれは予期された動作です(「予期された動作」の誤った定義については)。参照: http://git.661346.n2.nabble.com/subtree-merges-lose-prefix-after-rebase-td7332850 .html

これは誰にとっても大きな助けになるわけではありません。私もこれの回避策を見つけたいです。

3
Nick Hutchinson

私は同様の問題を抱えていました:サブツリーの追加を行った後にリベースしたかったのですが、--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

このアプローチで問題が見つかった場合は更新します。

2

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の廃止された「サブツリー」チュートリアル も使用しましたが、これは同じことを行いますが面倒です。

0
nyanpasu64

使用する必要があります

git rebase --preserve-merges --preserve-committer --onto new_place start end
0
Adam Dymitruk