我有以下提交历史记录:

头部头部~头部~2头部~3

gitcommit--修改当前HEAD提交。但是如何修改HEAD~3?


当前回答

如果您还没有推送提交,则可以使用git reset HEAD^[1,2,3,4…]返回到上一次提交

例如

git commit <file1> -m "Updated files 1 and 2"
git commit <file3> -m "Updated file 3"

抱歉,忘记在第一次提交时添加file2。。。

git reset HEAD^1 // because I only need to go back 1 commit

git add <file2>

这将在第一次提交时添加file2。

其他回答

使用令人惊叹的交互式rebase:

git rebase -i @~9   # Show the last 9 commits in a text editor

找到所需的提交,将pick更改为e(编辑),然后保存并关闭文件。Git将返回到该提交,允许您:

使用gitcommit--修改以进行更改,或使用gitreset@~放弃最后一次提交,但不放弃对文件的更改(即,将您带到编辑文件但尚未提交的位置)。

后者对于执行更复杂的任务(如拆分为多个提交)非常有用。

然后,运行git-rebase--continue,git将在修改后的提交之上回放后续更改。可能会要求您修复一些合并冲突。

注意:@是HEAD的简写,~是指定提交之前的提交。

阅读Git文档中有关重写历史的更多信息。


不要害怕重新启动

ProTip公司™: 不要害怕尝试“危险”的命令来重写历史*-默认情况下,Git不会在90天内删除您的提交;你可以在reflog中找到它们:

$ git reset @~3   # go back 3 commits
$ git reflog
c4f708b HEAD@{0}: reset: moving to @~3
2c52489 HEAD@{1}: commit: more changes
4a5246d HEAD@{2}: commit: make important changes
e8571e4 HEAD@{3}: commit: make some changes
... earlier commits ...
$ git reset 2c52489
... and you're back where you started

*注意一些选项,比如硬和强制,它们可以丢弃数据。*此外,不要重写您正在合作的任何分支的历史。



在许多系统上,git-rebase-i将默认打开Vim。Vim不像大多数现代文本编辑器那样工作,所以看看如何使用Vim重新设置基础。如果您希望使用不同的编辑器,请使用git-config--global-core.editor或您最喜欢的文本编辑器对其进行更改。

如果您还没有推送提交,则可以使用git reset HEAD^[1,2,3,4…]返回到上一次提交

例如

git commit <file1> -m "Updated files 1 and 2"
git commit <file3> -m "Updated file 3"

抱歉,忘记在第一次提交时添加file2。。。

git reset HEAD^1 // because I only need to go back 1 commit

git add <file2>

这将在第一次提交时添加file2。

嗯,这个解决方案可能听起来很愚蠢,但在某些情况下可以拯救你。

我的一个朋友刚刚无意中提交了一些非常大的文件(四个自动生成的文件,每个文件的大小在3GB到5GB之间),然后在此基础上提交了一些额外的代码,然后才意识到git推送不再有效的问题!

这些文件已在.gitignore中列出,但在重命名容器文件夹后,它们被暴露并提交了!现在,在上面还有几次代码提交,但推送一直在运行(试图上传GB的数据!),最终由于Github的文件大小限制而失败。

交互式rebase或任何类似的问题是,他们会处理这些巨大的文件,并且会花费很长时间来做任何事情。然而,在CLI中花了将近一个小时后,我们不确定文件(和增量)是否真的从历史中删除了,或者只是不包括在当前提交中。推也不起作用,我的朋友真的卡住了。

因此,我提出的解决方案是:

将当前git文件夹重命名为~/Project old。再次从github克隆git文件夹(到~/Project)。签出到同一分支。手动将文件从~/Project旧文件夹cp-r到~/Project。确保不需要签入的大量文件被mved并正确包含在.gitignore中。还要确保您没有用旧文件夹覆盖最近克隆的~/Project中的.git文件夹。这就是有问题的历史记录的所在!现在查看更改。它应该是所有最近提交的联合,不包括有问题的文件。最后提交更改,这很好。

这个解决方案最大的问题是,它处理手动复制一些文件,并且它将所有最近的提交合并为一个(显然使用了一个新的提交哈希)

最大的好处是,它在每一步中都非常清晰,它适用于大型文件(以及敏感文件),并且不会在历史上留下任何痕迹!

当我需要更深入地修复历史中以前的提交时,我经常使用交互式rebase和--autosquash。它本质上加快了ZelluX的回答所说明的过程,当您需要编辑多个提交时,它特别方便。

根据文档:

--自动撤销当提交日志消息以“squash…​“(或”修复…​“),并且有一个提交的标题以相同的开头…​, 自动修改rebase-i的todo列表,以便标记为挤压的提交在要修改的提交之后立即出现

假设您的历史记录如下:

$ git log --graph --oneline
* b42d293 Commit3
* e8adec4 Commit2
* faaf19f Commit1

并且您有要修改为Commit2的更改,然后使用

$ git commit -m "fixup! Commit2"

或者,您可以使用commit sha而不是commit消息,因此“fixup!e8adec4”或甚至只是commit消息的前缀。

然后在提交之前启动交互式重新基础

$ git rebase e8adec4^ -i --autosquash

编辑器将打开已正确排序的提交

pick e8adec4 Commit2
fixup 54e1a99 fixup! Commit2
pick b42d293 Commit3

你只需要保存并退出

git存储+再基础自动化

因为当我需要多次修改Gerrit审查的旧提交时,我一直在做:

git-amend-old() (
  # Stash, apply to past commit, and rebase the current branch on to of the result.
  current_branch="$(git rev-parse --abbrev-ref HEAD)"
  apply_to="$1"
  git stash
  git checkout "$apply_to"
  git stash apply
  git add -u
  git commit --amend --no-edit
  new_sha="$(git log --format="%H" -n 1)"
  git checkout "$current_branch"
  git rebase --onto "$new_sha" "$apply_to"
)

GitHub上游。

用法:

修改源文件,如果已在repo中,则无需gitaddgit修改旧$old_sha

我很喜欢这一点,因为它不会挤压其他无关的修复。