I did git merge with a branch which contains submodules and forgot to add the folders for some submodules (or deleted them accidentally during the merge while resolving conflicts).
Update submodules now errors with
pathspec '...' did not match any file(s) known to git.
I thought i could manually add empty folders for the submodules and amend but that doesn't seem possible.
Is there a way to add the needed folders without having to revert everything and merge again?
Here's what i did (i used tortoisegit but this script reproduces the exact same behavior; the problem is not tortoisegit specific, tortoisegit just makes it easier to not commit all changed files, i.e. the submodule directory in my case, which here is mimicked with git rm -f test2
):
rm -rf test2
mkdir test2
cd test2
git init .
echo 2 > test2.cpp
git add .
git commit -a -m 1
git branch -m main
cd ..
rm -rf test
mkdir test
cd test
git init .
echo 1 > test.cpp
git add .
git commit -a -m a1
git branch -m a
git branch b
git submodule add -- ../test2 test2
git commit -m a2
git checkout b
rm -r test2
echo 12 > test.cpp
git commit -a -m b2
git checkout a
echo 13 > test.cpp
git commit -a -m a3
git checkout b
git merge a
echo 123 > test.cpp
git add test.cpp
git rm -f test2
git commit -m b3
git submodule update --init --recursive -- "test2"
CodePudding user response:
OK, with the reproducer, I can reproduce the problem. The final merge commit (on branch b
in the test
directory repository) now has no submodule test2
at all, because the resolving step removed it:
$ git status
On branch b
nothing to commit, working tree clean
$ git submodule status
$ cat .gitmodules
$
So now we'd like to put the submodule back. We can do that with git submodule add
as usual:
$ git submodule add -- ../test2 test2
A git directory for 'test2' is found locally with remote(s):
origin [...]/test2
If you want to reuse this local git directory instead of cloning again from
[...]/test2
use the '--force' option. If the local git directory is not the correct repo
or you are unsure what this means choose another name with the '--name' option.
This is with a modern-ish but not up to date Git; older versions of Git may not say anything at all here, and might silently accept the attempt, but since I have this version, I now follow the advice that git submodule add
printed and use --force
:
$ git submodule add --force -- ../test2 test2
Reactivating local git directory for submodule 'test2'.
$ git status
On branch b
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: .gitmodules
new file: test2
$ git submodule status
b603f2eb4c83459de3d0796a89f1ab8bc19e1449 test2 (heads/main)
To "update" the merge—rather than making a new commit that adds on to the merge—we can now use git commit --amend
, as usual. Note that this really does just make a new commit. The special thing that --amend
does is to make the parents of the new commit the same as the parents of the current commit, which we can see with git log --all --decorate --oneline --graph
here:
$ git log --all --decorate --oneline --graph
* 1bc7878 (HEAD -> b) b3
|\
| * 3a6d09d (a) a3
| * b11ea5c a2
* | 2d6b761 b2
|/
* 38babd9 a1
$ git commit --amend -C HEAD
[b c5650ba] b3
Date: Mon May 9 03:11:28 2022 -0700
$ git log --all --decorate --oneline --graph
* c5650ba (HEAD -> b) b3
|\
| * 3a6d09d (a) a3
| * b11ea5c a2
* | 2d6b761 b2
|/
* 38babd9 a1
We now have a new, different hash ID for the tip commit of branch b
(now c5650ba
in my test, but of course everyone will get a different hash ID here). But I told Git to re-use the commit message from the previous branch-tip commit (1bc7878
) with -C HEAD
, without editing, so the entire thing happened with no manual intervention at this point. And now the merge commit has the submodule again.
(We can see the old merge commit if we add it to the git log
command, e.g.:
$ git log --all --decorate --oneline --graph 1bc7878
* c5650ba (HEAD -> b) b3
|\
| | * 1bc7878 b3
| |/|
|/|/
| * 3a6d09d (a) a3
| * b11ea5c a2
* | 2d6b761 b2
|/
* 38babd9 a1
There is just no name for the old merge commit. Since we never sent it anywhere—we did not run git push
nor allow anyone to sneak into our machine with git fetch
—nobody else will know we ever made it.)