我已提交更改并忘记将文件添加到更改集中。在其他提交之后,我意识到该文件现在从HEAD^4
提交中丢失了。
如何重写以前的提交以包含丢失的文件?
我意识到人们可以用谷歌搜索并来这里找到一个更简单的答案:如果它只是最后一次提交怎么办? (OP的问题是修复历史上的第四次提交)
如果您提交并意识到您忘记立即添加一些文件,只需执行以下操作:
# edited file-that-i-remember.txt
git add file-that-i-remember.txt
git commit
# realize you forgot a file
git add file-that-i-forgot.txt
git commit --amend --no-edit
哪里--no-edit
会保留相同的提交消息。
十分简单!
使用git rebase --interactive HEAD~4
并设置edit
您要修改的提交的选项。
请记住,您不应以这种方式修改推送到远程存储库的提交。在这种情况下,最好添加一个缺少文件的新提交。
为了更清楚地说明这一点,首先将所有当前更改存储为git stash
. 那么,git rebase --interactive HEAD~4
。您在文本编辑器中获得以下内容(请注意,您将获得 5 次提交,按降序排列):
pick 123e123 fifth last commit message
pick 321e122 fourth last commit message
pick 1d23e3f third last commit message
pick 987a987 second last commit message
pick 8a8a8a8 last commit message
将更改条目的前缀从 修改pick
为edit
。那是edit 321e122 ...
给OP的。
git rebase
按顺序浏览条目。由于我们只更改了一项,因此您只有一项需要更改。现在,添加您的文件git add
,并git commit --amend
使用这些添加的文件修改当前提交。
最后,git rebase --continue
移动到下一个文件。因为只有一个,所以变基已经完成
如果你还没有推送这 4 个提交,你可以这样做:
为所有这些提交创建补丁文件:
git format-patch -4
回退 4 次提交:
git reset --hard HEAD~4
添加丢失的文件:
git add missing-file
提交--amend
:
git commit --amend
应用所有保存的补丁:
git am *.patch
如果您已推送,则不应使用此方法。相反,只需承认您的错误并在 HEAD 之上再创建一个提交即可解决此问题。
尽管接受的答案是正确的,但它缺乏关于如何在变基过程中执行编辑提交的详细说明。
首先,启动一个 rebase 进程:
git rebase --interactive HEAD~4
将显示提交列表,通过更改单词pick
来选择要编辑的提交edit
并保存文件。
git add
在您的代码中进行必要的修改(记得为新文件调用)
完成所有修改后,发出git commit --amend
- 这将修改标记为的提交edit
调用git rebase --continue
将完成该过程(如果有更多标记为的提交edit
,则需要重复上述步骤)
重要笔记:
不要删除标记为pick
您不想编辑的行 - 保持原样。删除这些行将导致删除相关的提交
stash
如果您的工作目录不干净,GIT 会强制您在变基之前进行;但是,您可以git stash pop / git stash apply
在变基期间将这些更改(即在启动变基过程之前隐藏的更改)修改为标记为的提交edit
如果出现问题并且您想在变基过程完成之前恢复在变基过程中所做的更改(即您想在开始变基之前恢复到该点),请使用git rebase --abort
- 另请阅读:如果 --abort 没有,如何中止交互式变基不行吗?
正如接受的答案中所说:
请记住,您不应以这种方式修改推送到远程存储库的提交。在这种情况下,最好添加一个缺少文件的新提交。
答案在Git Book 中(标题为“ The Perils of Rebase ”的段落):
不要 rebase 存在于存储库之外的提交。
如果您遵循该准则,您会没事的。如果你不这样做,人们会恨你,你会被朋友和家人鄙视。
当你 rebase 东西时,你放弃了现有的提交并创建了相似但不同的新提交。如果你在某处推送提交,而其他人将它们拉下来并基于它们进行工作,然后你用 git rebase 重写这些提交并再次推送它们,你的协作者将不得不重新合并他们的工作,当你尝试时事情会变得一团糟把他们的工作拉回你的工作。
[...]
这是一种非交互式rebase
方法。
这需要额外的提交(因此不会按照 OP 的要求“重写以前的提交”),但我发现它更容易记住。
初始状态:
* e834111 (HEAD -> master) do something that depends on file x
* 6dde62a do stuff, forget to add file x
...
第 1 步 - 在提交时签出一个应该包含文件 ( git checkout -b temp 6dde
) 的新临时分支:
* e834111 (master) do something that depends on file x
* 6dde62a (HEAD -> temp) do stuff, forget to add file x
...
第 2 步 -add
丢失的文件和commit
:
* 50d1412 (HEAD -> temp) add file x
| * e834111 (master) do something that depends on file x
|/
* 6dde62a do stuff, forget to add file x
...
第 3 步 - 签出原始分支并rebase
进入temp
:
* dd6f2dd (HEAD -> master) do something that depends on file x
* 50d1412 (temp) add file x
* 6dde62a do stuff, forget to add file x
...
第 4 步 - 删除临时分支 ( git branch -d temp
)
注意:这也可以在不创建临时分支的情况下完成,例如在分离的头上使用git checkout 6dde
acommit
和。rebase