在合并和提交过程中意外删除了子模块文件夹;如何在不还原和完全合并的情况下重新添加它们

Accidentally deleted submodule folders during merge and committed; how to re add them without reverting and completely merging again

我 git 合并了一个包含子模块的分支,但忘记为某些子模块添加文件夹(或者在解决冲突时在合并过程中不小心删除了它们)。

更新子模块现在出错

pathspec '...' did not match any file(s) known to git.

我以为我可以手动为子模块添加空文件夹并进行修改,但这似乎不可能。

有没有办法添加所需的文件夹而不必还原所有内容并再次合并?

这是我所做的 (我使用了 tortoisegit 但这个脚本重现了完全相同的行为;问题不是 tortoisegit 特有的,tortoisegit只是更容易不提交所有更改的文件,即我的子模块目录,这里用 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"

Demo

好的,有了复现器,我就可以复现问题了。最终的合并提交(在 test 目录存储库中的分支 b 上)现在根本没有子模块 test2,因为解析步骤将其删除:

$ git status
On branch b
nothing to commit, working tree clean
$ git submodule status
$ cat .gitmodules
$

所以现在我们想把子模块放回去。我们可以像往常一样使用 git submodule add 来做到这一点:

$ 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.

这是 modern-ish 但不是最新的 Git; Git 的旧版本可能在这里什么都不说,可能会默默地接受尝试,但由于我有这个版本,我现在遵循 git submodule add 打印的建议并使用 --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)

要“更新”合并——而不是进行新的提交,将 添加到 合并——我们现在可以像往常一样使用 git commit --amend。请注意,这实际上只是进行了一次新的提交。 --amend 所做的特殊事情是使新提交的 parentscurrent 提交的父级相同,这我们可以在这里看到 git log --all --decorate --oneline --graph

$ 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

我们现在有一个新的、不同的哈希 ID 用于分支 b 的提示提交(在我的测试中现在 c5650ba,当然每个人都会在这里得到一个不同的哈希 ID)。但是我告诉 Git 到 re-use 来自前一个 branch-tip 提交 (1bc7878) 的提交消息 -C HEAD,没有编辑,所以整个事情没有发生此时人工干预。现在合并提交又有了子模块。

(如果我们将它添加到 git log 命令,我们可以看到旧的合并提交,例如:

$ git log --all --decorate --oneline --graph 1bc7878
*   c5650ba (HEAD -> b) b3
|\  
| | * 1bc7878 b3
| |/| 
|/|/  
| * 3a6d09d (a) a3
| * b11ea5c a2
* | 2d6b761 b2
|/  
* 38babd9 a1

旧的合并提交没有 name。因为我们从来没有把它发送到任何地方——我们没有 运行 git push 也不允许任何人用 git fetch 偷偷进入我们的机器——没有其他人会知道我们成功了。)