プロジェクト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.h
はmain
で追跡されます。
どうしてそうすることができるのでしょうか?
私はまだ2番目のオプション(メインに実際のパッチファイルを持っています)を使用しますが、ビルドプロセスを次のように調整します。
config.h
のコピーを作成しますconfig.h
を元のコンテンツに復元します。このようにして、サブモジュールのステータスを変更しません。
OPはコメントを追加します。
しかし、ソリューションがIDEで機能していない場合、Intellisenseは混乱します–
True:そのため、チェックアウト時にパッチを自動的に適用し、チェック時に smudge/clean content filter driver を介して削除します。
そのようにすると、パッチはすべてのセッション中はそのまま残りますが、どのgit status/diff/checkinでも消えます。
ただし、これは理想的ではなく、これを処理するネイティブGitの方法はないようです。
ベンダー履歴にプロジェクト固有のパッチを保持する最も簡単な方法は、ベンダーリポジトリを複製し、変更をプロジェクトブランチとして保持し、その複製を.gitmodules
アップストリームとしてアドバタイズすることです。
これにより、ベンダーの上流プロジェクトへの変更が正常に機能します。git clone --recurse-submodules yourproject
は正常に機能します。サブモジュールの変更を上流のプロジェクトのサブモジュールにプッシュバックできます(サブモジュールリポジトリのOrigin
リモート)。 、すべてが機能します。
唯一の追加項目は、プロジェクトのサブモジュールのバージョンを最新のベンダーコードに更新することです。誰かが(さらに上流の)ベンダーリポジトリからフェッチしてマージする必要があります。
...しかし、それもまったく普通のことです。ベンダーのリポジトリからフェッチしてマージする方法は、それを実行することです。 git remote add vendor u://r/l; git fetch vendor; git merge vendor/master
。あるいは、リベースをマージしたい場合は、それを行います。それが完了したら、結果をサブモジュールのOrigin
、プロジェクトのバージョンにすべて通常どおりプッシュします。
理想的な解決策は、Gitを使用してパッチを追跡することですが、トップレベルで(たとえば、fooではなくメインで直接)です。理論的には、サブモジュールの場所を指すBitをGitツリーに追加することが可能です。
これが実行可能であっても、個人的には非常に複雑です。これがネイティブに実装され、理解と管理を容易にするユーザー向けのコマンドが含まれていない限り、私はそれを避けます。
受け入れられた答え以外に、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も関係しているので、元のファイルとビルドファイルの両方を一度に検査したい場合は、よりトリッキーになるかもしれません。
長所:
main
はfoo
には影響しません短所:
パッチが小さいか、main
に固有のパッチである場合は、これを使用します。
PS:必要に応じて、サブモジュールを使用する代わりに、さらに一歩進んで、ビルドプロセスでfoo
のバージョンを直接追跡することができます。 :
foo
を1つ上のディレクトリに移動してから、ビルドプロセスで:
_cd build
GIT_DIR='../../foo/.git' git checkout "$myrev"
patch <myconfig.patch
make
_
また、fooを最新バージョンに更新する場合、fooの履歴に多くのノイズを導入するパッチもチェリーピックする必要があります。
あなたは本当にそれをチェリーピックする必要はありません、あなたは代わりにあなたのブランチの変更を維持することができ、そしてmergemaster
毎回少しの間。
個人的には、同期を保つことによって引き起こされるノイズ(つまり、マージと競合)よりもはるかに重要な変更でない限り、これを回避します。特に競合が含まれている場合、無関係な/偶発的な変更を検出するのが難しいため、マージコミットは非常に不透明であることがわかりました。
リベースmaster
へのコミットもオプションです。
長所:
短所:
foo
のリポジトリを無関係なコミットで汚染する(マージ時)foo
のリポジトリを無関係なコミットオブジェクトで汚染する(リベース時)config.h
_への変更の進化の暗い歴史(リベース時)また、fooを最新バージョンに更新する場合、fooの履歴に多くのノイズを導入するパッチもチェリーピックする必要があります。
残念ながら、これは非常に悪いアプローチです。私がメインにのみ重要なfooに変更を加えているためです
foo
をmain
に合わせて変更したいが、foo
アップストリームを台無しにしたくない場合は、foo
のソフトフォークを作成してみませんか? _foo-fork
_の履歴にあまり関心がない場合は、_main-project
_ブランチで変更をコミットし、リベースを通じてfoo
のmaster
との同期を維持することができます。
フォークを作成する:
_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
_を使用)短所:
config.h
_への変更の進化の不明瞭な履歴(リベースのため)リベースの代わりにパッチを使用する追加の利点は、パッチの履歴を保持できることです。しかし、本当にシンプルな同期を維持したい場合は、これが方法だと思います。
foo
の変更が多すぎる/頻繁すぎる、および/またはパッチを適用しすぎる必要がある場合は、代わりにフルフォークを作成して変更をチェリーピックすることをお勧めします。
サブモジュールではなくgitサブツリーを使用する方が理にかなっているかもしれません。サブツリーは、他のプロジェクトを現在のリポジトリ内の個別のリポジトリとして管理するのではなく、ローカルリポジトリ内の場所にコピーします。
メインマスターブランチの一部としてローカルで変更を適用し、定期的に更新して、上流からの変更をマージできます。