Reputation: 1590
I have a git repo foo
that has several submodules in their own folders bar
and baz
(the actual repo is eclipse.platform.releng.aggregator). I need to generate patches for changes to those repos.
I can generate a patch with git --no-pager diff > file.patch
which is fine for changes to foo
but doesn't report changes in the bar
or baz
, the submodules.
Using git submodule --quiet foreach --recursive git --no-pager diff > file
(cribbed from this answer) nearly works but the diffs don't include the names of the folders the subrepos live in.
Presently I work round this by hacking the paths into the patches by hand but that only works for simple patches and the scripting solutions I can think of (e.g. getting the subrepo names from git, changing to the folders, generating the patches in folders) mean I can't use a simple git apply
to reapply the patches later.
Is there a way to get git to diff the subrepos directly rather than through foreach so the generated patches can be simply applied?
Upvotes: 0
Views: 1696
Reputation: 191
In source repository run
(git --no-pager diff --src-prefix= --dst-prefix=;\
git submodule foreach --quiet 'git --no-pager diff --src-prefix=$sm_path/ --dst-prefix=$sm_path/' \
) > uncommited.patch
(git --no-pager diff --src-prefix= --dst-prefix=;\
GIT_PROJECT_PATH=$PWD git submodule foreach --quiet --recursive 'git --no-pager diff --src-prefix=$(realpath --relative-to=$GIT_PROJECT_PATH $PWD)/ --dst-prefix=$(realpath --relative-to=$GIT_PROJECT_PATH $PWD)/' \
) > uncommited.patch
In destination repository run to apply patch:
git apply -p 0 uncommited.patch
--no-pager
prevents diff
interactive mode (notice it is git
parameter, not diff
).--src-prefix=
and --dst-prefix=
(empty value; ;
is command separator, not value) remove default path prefix, so they contain only relative values.--quiet
prevents lines starting with Entering for each folder to appear in patch file.--recursive
dives into nested submodules.realpath
generates relative path to source root.$sm_path
is environment variable provided by git submodule foreach
containing relative module folder path.$PWD
(when used in foreach
) is environment variable with current module folder absolute path.GIT_PROJECT_PATH
is arbitrary named environment variable with source root absolute path (exists only during command execution). When modules are not nested, foreach
provides $toplevel
variable with same value.uncommited.patch
is file with diff for source root and all submodules with paths relative to source root.-p 0
treats paths as relative to source root when applying (disables leading slashes stripping in paths).Upvotes: 4
Reputation: 1590
With the clue offered by @larsks I managed to find the following one liner which does what I need:
git submodule --quiet foreach --recursive 'export NAME="${PWD##*/}"; git --no-pager diff --src-prefix="a/${NAME}/" --dst-prefix="b/${NAME}/"'
This adds the folder name of the subfolders into the patch generated with git diff
so I can just run git apply
in the parent folder even if the patch spans more than one of the submodules. Note however that it only works because my submodules are all one folder below the main repo folder. That could be overcome if needed by changing the code used to set NAME
to something a little more clever.
Upvotes: 3
Reputation: 28981
With submodules the idea is that each repo is independent, it may be supported by different maintainers and have different process of approvals. Moreover, the repos could be checked out separately and even be part of completely different repo as submodule.
So, the approach here is to create patch-sets independently for each repo and pass them through independently. It makes more sense if you create patches after commits with git format-patch
.
Upvotes: 0