web-dev-qa-db-ja.com

親プロジェクトからGitのサブモジュールにパッチを適用することは可能ですか?

プロジェクトmainにサブモジュールfooが含まれています。この特定のプロジェクトでは、この特定のプロジェクトfooにのみ適用されるmainに小さな変更を加えたいと思います。

main/
  + .git
  + main.c
  + lib/
  |   + bar.c
  + foo/           # My `foo` submodule
      + .git
      + config.h   # The file I want to patch from `main`
      + ...

一般的な解決策は、サブモジュールに移動し、Applied patch for mainという新しいブランチでmain-projectをコミットして、それをプッシュすることです。残念ながら、私はfooにのみ重要なmainに変更を加えているため、これは非常に悪いアプローチです。また、fooを最新バージョンに更新する場合、fooの履歴に多くのノイズを導入するパッチも選択する必要があります。

別の解決策は、ビルドの直前にmainに適用される実際のpatchファイルをfooに置くことです。残念ながら、これによりサブモジュールの内容が変更されるため、fooでコミットされていない変更が行われるため、これも適切な解決策ではありません。

理想的な解決策は、Gitを使用してパッチを追跡することですが、トップレベルで(たとえば、mainではなくfooで直接)です。理論的には、サブモジュールの場所を指すblobをGit treeに追加することが可能です。

blob   <sha> main.c
tree   <sha> lib/
commit <sha> foo
blob   <sha> foo/config.h

このアイデアにより、fooに属するパッチされたファイルconfig.hmainで追跡されます。

どうしてそうすることができるのでしょうか?

17
nowox

私はまだ2番目のオプション(メインに実際のパッチファイルを持っています)を使用しますが、ビルドプロセスを次のように調整します。

  • サブモジュールにconfig.hのコピーを作成します
  • パッチを適用する
  • 建てる
  • config.hを元のコンテンツに復元します。

このようにして、サブモジュールのステータスを変更しません。

OPはコメントを追加します。

しかし、ソリューションがIDEで機能していない場合、Intellisenseは混乱します–

True:そのため、チェックアウト時にパッチを自動的に適用し、チェック時に smudge/clean content filter driver を介して削除します。
そのようにすると、パッチはすべてのセッション中はそのまま残りますが、どのgit status/diff/checkinでも消えます。

ただし、これは理想的ではなく、これを処理するネイティブGitの方法はないようです。

8
VonC

ベンダー履歴にプロジェクト固有のパッチを保持する最も簡単な方法は、ベンダーリポジトリを複製し、変更をプロジェクトブランチとして保持し、その複製を.gitmodulesアップストリームとしてアドバタイズすることです。

これにより、ベンダーの上流プロジェクトへの変更が正常に機能します。git clone --recurse-submodules yourprojectは正常に機能します。サブモジュールの変更を上流のプロジェクトのサブモジュールにプッシュバックできます(サブモジュールリポジトリのOriginリモート)。 、すべてが機能します。

唯一の追加項目は、プロジェクトのサブモジュールのバージョンを最新のベンダーコードに更新することです。誰かが(さらに上流の)ベンダーリポジトリからフェッチしてマージする必要があります。

...しかし、それもまったく普通のことです。ベンダーのリポジトリからフェッチしてマージする方法は、それを実行することです。 git remote add vendor u://r/l; git fetch vendor; git merge vendor/master。あるいは、リベースをマージしたい場合は、それを行います。それが完了したら、結果をサブモジュールのOrigin、プロジェクトのバージョンにすべて通常どおりプッシュします。

6
jthill

理想的な解決策は、Gitを使用してパッチを追跡することですが、トップレベルで(たとえば、fooではなくメインで直接)です。理論的には、サブモジュールの場所を指すBitをGitツリーに追加することが可能です。

これが実行可能であっても、個人的には非常に複雑です。これがネイティブに実装され、理解と管理を容易にするユーザー向けのコマンドが含まれていない限り、私はそれを避けます。

代替1:パッチ

受け入れられた答え以外に、gitサブモジュールでパッチを実行するよりエレガントで効率的な方法はありますか?

サブモジュールをいじるのをまったく避けたい場合は、ワークツリーをコピーして別の場所にチェックアウトし、ビルド中にそれだけを使用することをお勧めします。そうすれば、サブモジュールは常に「クリーン」(そしておそらくmainの観点からは「不変」)であり、ビルドディレクトリでそれについて心配するだけで済みます。

簡略化されたビルドプロセスの例:

_cd main
mkdir -p build
cp -R foo/ build/
cp myconfig.patch build/
cd build
patch <myconfig.patch
make
_

これはfooのみをビルドし、mainのビルドプロセスは、_build/_の代わりに_foo/_を指す必要がある以外に変更する必要がないことに注意してください。

foo自体を変更するつもりがない場合、「そのまま」に保つ場合は、それをベアリポジトリに変換し、cpの代わりに_GIT_WORK_TREE="$PWD/build" git checkout HEAD_を使用して、ビルド時にのみチェックアウトされます。これは、元のソース(_$source_配列vs _$srcdir_)の変更を避けるために makepkg(8) が(少なくとも私のAURの経験で)行う方法と似ています。また、ビルド自体からのソース検索を分割します(prepare() vs build())。 PKGBUILD(5) および パッケージの作成 も参照してください。あなたの場合、開発とIDEも関係しているので、元のファイルとビルドファイルの両方を一度に検査したい場合は、よりトリッキーになるかもしれません。

長所

  • ソースはビルドファイルから分離されています
  • mainfooには影響しません
  • Gitに依存せず、ビルドの自動化の問題にすぎない
  • パッチファイルのみが必要

短所

  • パッチファイルを最新の状態に保つ必要がある(変更のリベースとの比較)
  • ビルドプロセスを変更する必要がある

パッチが小さいか、mainに固有のパッチである場合は、これを使用します。

PS:必要に応じて、サブモジュールを使用する代わりに、さらに一歩進んで、ビルドプロセスでfooのバージョンを直接追跡することができます。 :

fooを1つ上のディレクトリに移動してから、ビルドプロセスで:

_cd build
GIT_DIR='../../foo/.git' git checkout "$myrev"
patch <myconfig.patch
make
_

代替2:別のブランチ

また、fooを最新バージョンに更新する場合、fooの履歴に多くのノイズを導入するパッチもチェリーピックする必要があります。

あなたは本当にそれをチェリーピックする必要はありません、あなたは代わりにあなたのブランチの変更を維持することができ、そしてmergemaster毎回少しの間。

個人的には、同期を保つことによって引き起こされるノイズ(つまり、マージと競合)よりもはるかに重要な変更でない限り、これを回避します。特に競合が含まれている場合、無関係な/偶発的な変更を検出するのが難しいため、マージコミットは非常に不透明であることがわかりました。

リベースmasterへのコミットもオプションです。

長所

  • 個別のリポジトリは不要
  • ワークツリーを同じ場所に保持します(IDEをいじる必要はありません)。

短所

  • fooのリポジトリを無関係なコミットで汚染する(マージ時)
  • fooのリポジトリを無関係なコミットオブジェクトで汚染する(リベース時)
  • _config.h_への変更の進化の暗い歴史(リベース時)

代替3:ソフトフォーク

また、fooを最新バージョンに更新する場合、fooの履歴に多くのノイズを導入するパッチもチェリーピックする必要があります。

残念ながら、これは非常に悪いアプローチです。私がメインにのみ重要なfooに変更を加えているためです

foomainに合わせて変更したいが、fooアップストリームを台無しにしたくない場合は、fooのソフトフォークを作成してみませんか? _foo-fork_の履歴にあまり関心がない場合は、_main-project_ブランチで変更をコミットし、リベースを通じてfoomasterとの同期を維持することができます。

フォークを作成する:

_cd foo
git remote add foo-fork 'https://foo-fork.com'
git branch main-project master
git Push -u foo-fork main-project
_

同期を保つ:

_git checkout main-project
git pull --rebase foo/master
# (resolve the conflicts, if any)
git Push foo-fork
_

長所

  • アップストリームとの同期が簡単(例:_pull --rebase_を使用)
  • ワークツリーを同じ場所に保持します(IDEをいじる必要はありません)。

短所

  • _config.h_への変更の進化の不明瞭な履歴(リベースのため)

リベースの代わりにパッチを使用する追加の利点は、パッチの履歴を保持できることです。しかし、本当にシンプルな同期を維持したい場合は、これが方法だと思います。

代替案4:ハードフォーク

fooの変更が多すぎる/頻繁すぎる、および/またはパッチを適用しすぎる必要がある場合は、代わりにフルフォークを作成して変更をチェリーピックすることをお勧めします。

4
kelvin

サブモジュールではなくgitサブツリーを使用する方が理にかなっているかもしれません。サブツリーは、他のプロジェクトを現在のリポジトリ内の個別のリポジトリとして管理するのではなく、ローカルリポジトリ内の場所にコピーします。

メインマスターブランチの一部としてローカルで変更を適用し、定期的に更新して、上流からの変更をマージできます。

2
rjmunro