Is it possible to patch a submodule in Git from the parent project? Is it possible to patch a submodule in Git from the parent project? git git

Is it possible to patch a submodule in Git from the parent project?


I would still go with the second option (have a real patch file on main), but adapt my build process to:

  • make a copy of the config.h in the submodule
  • apply the patch
  • build
  • restore config.h to its original content.

That way, I keep the submodule status unchanged.

The OP adds in the comments:

But your solution is not working in a IDE, Intellisense will be confused –

True: for that, I would apply automatically the patch on checkout, and remove it on checking, through a smudge/clean content filter driver.
That way, the patch remains in place during the all session, but would disappear on any git status/diff/checkin.

This is not ideal though, and there does not seem to be a native Git way to handle this.


The ideal solution would be to track my patch using Git, but at the top-level (e.g. directly on main, not on foo). Theoretically, it would be possible to add a blob on the Git tree that points into the submodule location:

Even if this is doable, personally I find it very convoluted. Unless this wasimplemented natively and included user-facing commands that make it easy tounderstand and manage, I'd stay away from it.

Alternative 1: Patch

Is there are more elegant/streamlined way of doing a patch in a git submodule, other than the accepted answer?

If you would like to avoid messing with the submodule at all, I would suggestcopying over/checking out the worktree somewhere else and using only thatduring the build. That way, the submodule is always "clean" (and perhaps"immutable", from main's point of view) and you only have to worry about itin the build directory.

Simplified build process example:

cd mainmkdir -p buildcp -R foo/ build/cp myconfig.patch build/cd buildpatch <myconfig.patchmake

Note that this builds only foo, and that main's build process does not needto be altered besides having to point to build/ instead of foo/.

If you do not intend on modifying foo itself/would rather keep it "pristine",you could also turn it into a bare repository and useGIT_WORK_TREE="$PWD/build" git checkout HEAD instead of cp, so that it isonly checked out during the build. This is similar to howmakepkg(8) does it (at least in my experience with the AUR) in orderto avoid modifying the original sources ($source array vs $srcdir). Italso splits source retrieval from the build itself (prepare() vs build()).See also PKGBUILD(5) and Creating packages.In your case, development and an IDE are also involved, so it might be trickierif you want to inspect both the original and the build files at once.

Pros:

  • Sources are separated from the build files
  • main does not affect foo
  • Does not depend on git/makes it merely a build automation issue
  • Only requires a patch file

Cons:

  • Need to keep the patch file updated (vs rebasing changes)
  • Need to change the build process

I'd go with this one if your patches are small and/or very specific to main.

P.S.: It is possible to go one step further and track foo's versiondirectly in the build process instead of using submodules if you wanted to:

Move foo one directory up, then in the build process:

cd buildGIT_DIR='../../foo/.git' git checkout "$myrev"patch <myconfig.patchmake

Alternative 2: Separate Branch

Also, when I update foo to the latest version, I will have to cherry-pick the patch too which introduces a lot of noise in foo's history.

You don't really have to cherry-pick it, you could just keep the changes inyour branch instead and merge master every once in a while.

Personally, I would avoid this unless your changes are much more significantthan the noise caused by keeping it in sync (i.e.: the merges and conflicts).I find merge commits to be very opaque, especially when conflicts are involved,as unrelated/accidental changes are harder to detect.

Rebasing your commits onto master is also an option.

Pros:

  • No need for a separate repository
  • Keeps the worktree in the same place (no need to mess with your IDE)

Cons:

  • Pollutes foo's repository with unrelated commits (when merging)
  • Pollutes foo's repository with unrelated commit objects (when rebasing)
  • Murky history of the evolution of your changes to config.h (when rebasing)

Alternative 3: Soft Fork

Also, when I update foo to the latest version, I will have to cherry-pick the patch too which introduces a lot of noise in foo's history.

Unfortunately, this is a very bad approach because I am making changes to foo that only matters to main

If you want to change foo to suit main, but not mess with foo upstream,why not create a soft-fork of foo? If you do not care too much aboutfoo-fork's history, you could just commit your changes on the main-projectbranch and keep it in sync with foo's master through rebase:

Creating the fork:

cd foogit remote add foo-fork 'https://foo-fork.com'git branch main-project mastergit push -u foo-fork main-project

Keeping it in sync:

git checkout main-projectgit pull --rebase foo/master# (resolve the conflicts, if any)git push foo-fork

Pros:

  • Easy to sync with upstream (e.g.: with pull --rebase)
  • Keeps the worktree in the same place (no need to mess with your IDE)

Cons:

  • Murky history of the evolution of your changes to config.h (because ofrebasing)

The added benefit of using a patch instead of rebasing is that you keep thehistory of the patch. But if you want to keep things really simple sync-wise,I suppose that this is the way.

Alternative 4: Hard Fork

If you find that foo changes too much/too often and/or you need to patch toomany things, your best bet is probably creating a full fork and cherry-pickingtheir changes instead.


The simplest way to carry project-specific patches on a vendor history is to clone the vendor repo, carry the changes as a project branch and advertise that clone as the .gitmodules upstream.

This makes work on your changes to the vendor upstream project perfectly ordinary, git clone --recurse-submodules yourproject works fine, your-submodule changes can be pushed back to the your-project-submodule upstream (the submodule repo's origin remote), everything works.

The only additional fillip is, to update your project's version of the submodule to the latest vendor code somebody has to fetch and merge from the (further-upstream) vendor repo

... but that's also perfectly ordinary: the way to fetch and merge from the vendor repo is, do it. git remote add vendor u://r/l; git fetch vendor; git merge vendor/master. Or if you prefer rebase to merge, do that. Once you've done that, push the results to your submodule's origin, your project's version, all as usual.