我有以下提交历史记录:

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

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


当前回答

使用令人惊叹的交互式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或您最喜欢的文本编辑器对其进行更改。

其他回答

当我需要更深入地修复历史中以前的提交时,我经常使用交互式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-rebase。例如,要修改提交bbc643cd,请运行:

$ git rebase --interactive 'bbc643cd^'

请注意命令末尾的插入符号^,因为您实际上需要在要修改的提交之前重新返回提交。

在默认编辑器中,在提到bbc643cd的行中修改pick以进行编辑。

保存文件并退出。git将解释并自动执行文件中的命令。您将发现自己处于之前创建提交bbc643cd的情况。

此时,bbc643cd是您最后一次提交,您可以很容易地修改它。进行更改,然后使用以下命令进行提交:

$ git commit --all --amend --no-edit

之后,使用以下命令返回到上一个HEAD提交:

$ git rebase --continue

警告:请注意,这将更改该提交的SHA-1以及所有子项——换句话说,这将重写从此点开始的历史。如果使用命令gitpush-force进行推送,则可以断开repos。

我解决了这个问题,

1) 通过使用我想要的更改创建新的提交。。

r8gs4r commit 0

2) 我知道我需要合并哪个提交,即提交3。

因此,gitrebase-i HEAD~4#4表示最近的4次提交(此处提交3位于第4位)

3) 在交互式rebase中,最近的提交将位于底部。它看起来很像,

pick q6ade6 commit 3
pick vr43de commit 2
pick ac123d commit 1
pick r8gs4r commit 0

4) 如果您想与特定的提交合并,我们需要重新排列提交。应该是这样的,

parent
|_child

pick q6ade6 commit 3
f r8gs4r commit 0
pick vr43de commit 2
pick ac123d commit 1

重新排列后,需要将p pick替换为f(fixup将在没有提交消息的情况下进行合并)或s(带有提交消息的压缩合并可能在运行时发生变化)

然后保存你的树。

现在与现有提交合并完成。

注意:除非你自己维护,否则这不是更好的方法。如果你的团队规模很大,重写git是不可接受的方法这棵树最终会陷入你们知道其他人不会发生的冲突。如果你愿意为了用更少的提交来保持树的整洁,可以尝试这样做,如果小团队,否则不可取。。。。。

如果出于某种原因,您不喜欢交互式编辑器,可以使用gitrebase--on。

假设您要修改Commit1。首先,从Commit1之前分支:

git checkout -b amending [commit before Commit1]

第二,用樱桃核抓取Commit1:

git cherry-pick Commit1

现在,修改您的更改,创建Commit1':

git add ...
git commit --amend -m "new message for Commit1"

最后,在隐藏了任何其他更改后,将其余的提交移植到master之上新提交:

git rebase --onto amending Commit1 master

阅读:“将Commit1(不包括在内)和master(包括在内)之间的所有提交重新纳入修改分支”。即Commit2和Commit3,将旧的Commit1完全删除。你可以随便摘,但这样更容易。

记得清理你的树枝!

git branch -d amending

对我来说,这是为了从回购中删除一些凭证。我试着换基,在换基的过程中遇到了一大堆看似无关的冲突——继续。不要费心尝试重新设置自己的基础,在mac上使用名为BFG(brew-install-BFG)的工具。