在合并和提交过程中意外删除了子模块文件夹;如何在不还原和完全合并的情况下重新添加它们
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"
好的,有了复现器,我就可以复现问题了。最终的合并提交(在 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
所做的特殊事情是使新提交的 parents 与 current 提交的父级相同,这我们可以在这里看到 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
偷偷进入我们的机器——没有其他人会知道我们成功了。)
我 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"
好的,有了复现器,我就可以复现问题了。最终的合并提交(在 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
所做的特殊事情是使新提交的 parents 与 current 提交的父级相同,这我们可以在这里看到 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
偷偷进入我们的机器——没有其他人会知道我们成功了。)