我有以下提交历史:
HEAD
HEAD~
HEAD~2
HEAD~3
git commit --amend
修改了当前的 HEAD
提交记录。但是如何修改 HEAD~3
呢?
我有以下提交历史:
HEAD
HEAD~
HEAD~2
HEAD~3
git commit --amend
修改了当前的 HEAD
提交记录。但是如何修改 HEAD~3
呢?
使用git rebase
。例如,要修改提交bbc643cd
,运行:
git rebase --interactive bbc643cd~
~
,因为您需要在bbc643cd
之前的提交上重新应用提交(即bbc643cd~
)。bbc643cd
的行中的pick
为edit
。bbc643cd
的先前状态。bbc643cd
是您的最后一次提交,您可以轻松地修改它。进行更改,然后使用以下命令提交它们:git commit --all --amend --no-edit
然后,使用以下命令返回到先前的 HEAD 提交:
git rebase --continue
警告:请注意这将更改该提交的SHA-1以及所有子提交--换句话说,这将重写从该点之后的历史记录。如果您使用命令git push --force
进行推送,可能会破坏repos。
git rebase -i
中使用reword
操作代替edit
(它会自动打开编辑器并继续执行其余的rebase步骤;这样就不需要使用git commit --amend
和git rebase --continue
,当您只需要更改提交消息而不是内容时)。 - Chris Johnsengit rebase
之前可能需要运行git stash
命令,并在之后运行git stash pop
来还原这些更改。 - user123444555621git rebase -i # Show your commits in a text editor
pick
改为 e
(edit
),然后保存并关闭文件。Git 将会回退到该提交,让你可以选择:git commit --amend
进行修改,或者
- 使用 git reset @~
放弃最后一次提交,但保留对文件的修改(即回到你编辑文件但尚未提交时的状态)。git rebase --continue
,Git 将在你修改的提交之上重播后续的更改。可能需要解决一些合并冲突。@
是 HEAD
的简写,~
表示指定提交的前一个提交。专业提示™:不要害怕尝试使用“危险”的命令来重写历史* — 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
--hard
和--force
,它们可能会丢弃数据。git rebase -i
会打开Vim。Vim的工作方式与大多数现代文本编辑器不同,所以请参考如何使用Vim进行rebase。如果你更喜欢使用其他编辑器,请使用git config --global core.editor your-favorite-text-editor
进行更改。git rebase -i --root
. - basickarlgit rebase -i
而不是git rebase -i @~9
来查看您分支的所有提交。如果您使用git rebase -i @~9
并且您的分支中的提交较少,那么rebase将使最后的9个提交成为您分支的一部分。 - undefinedrebase
不会影响我不想修改的旧提交记录;然而,在测试过程中,我发现只有在先前的提交记录被修改过后,pick
才会对提交记录进行操作。 - undefined当我需要修复历史中更深的以前提交时,我经常使用带有--autosquash
的交互式rebase。它本质上加速了ZelluX答案所示的过程,并且在您需要编辑多个提交时特别方便。
来自文档:
--autosquash
当提交日志消息以"squash!..."(或"fixup!...")开头,并且有一个标题以相同...开头的提交时,自动修改rebase -i的待办事项列表,使标记为squashing的提交紧随要修改的提交之后
假设您的历史记录如下所示:
$ git log --graph --oneline
* b42d293 Commit3
* e8adec4 Commit2
* faaf19f Commit1
如果您有更改需要修订到Commit2,则可以使用以下命令提交您的更改:
$ git commit -m "fixup! Commit2"
您可以选择使用提交 SHA 而不是提交消息,例如 "fixup! e8adec4"
或者仅使用提交消息的前缀。
然后在之前的提交上启动交互式变基。
$ git rebase e8adec4^ -i --autosquash
pick e8adec4 Commit2
fixup 54e1a99 fixup! Commit2
pick b42d293 Commit3
你需要做的就是保存并退出
git commit --fixup=@~
替代 git commit -m "fixup! Commit2"
。当你的提交信息很长,在键入整个信息时会很麻烦时,这一点尤为有用。 - Zazfixup = "!fn() { git commit --fixup ${1} && GIT_EDITOR=true git rebase --autosquash -i ${1}^; }; fn
-> git fixup <commitId>
将所有暂存的更改添加到给定的提交中。 - thrau"
。 - Roaldfixup =“!fn(){git commit -m \”fixup!$ {1} \“&& GIT_EDITOR = true git rebase --autosquash -i $ {1} ^;}; fn”
。 - Jens RolandHEAD
、HEAD^
、HEAD~7
等:fixup = "!fn() { _FIXUP_COMMIT=\
git rev-parse ${1}` && git commit -m "fixup! ${_FIXUP_COMMIT}" && GIT_EDITOR=true git rebase --autosquash -i ${_FIXUP_COMMIT}^; }; fn"` - Dorigit rebase -i HEAD~3
以上显示了当前分支上最近的3个提交记录,如果您想获取更多,请将3更改为其他数字。列表将类似于以下内容:
pick e499d89 Delete CNAME
pick 0c39034 Better README
pick f7fde4a Change the commit message but push the same commit.
在您想要更改的每个提交消息之前,将pick替换为reword。假设您要更改列表中的第二个提交,则文件将如下所示:
pick e499d89 Delete CNAME
reword 0c39034 Better README
pick f7fde4a Change the commit message but push the same commit.
保存并关闭提交列表文件,这将弹出一个新的编辑器供您更改提交消息,更改提交消息并保存。
最后,强制推送修改后的提交。
git push --force
运行:
$ git rebase --interactive commit_hash^
每个^
表示您要编辑多少个提交,如果只有一个(您指定的提交哈希值),则只需添加一个^
。
使用Vim,将单词pick
更改为reword
以更改您想要更改的提交,保存并退出(:wq
)。 然后Git会提示您每个标记为重新编辑的提交,以便您可以更改提交消息。
每个提交消息都必须保存并退出(:wq
)以进入下一个提交消息
如果您想退出而不应用更改,请按:q!
编辑:要在vim
中导航,您可以使用j
向上,k
向下,h
向左和l
向右(所有这些都在NORMAL
模式下,在NORMAL
模式下按ESC
键)。
要编辑文本,请按i
,以便您进入INSERT
模式,在该模式下插入文本。 按ESC
返回NORMAL
模式 :)
更新:这是来自Github的一个很棒的链接,列出了使用Git撤消(几乎)所有操作
git push --force
吗? - u01jmg3我想分享一个我正在使用的别名。它基于非交互式交互式变基。将其添加到您的git中,运行以下命令(下面给出解释):
git config --global alias.amend-to '!f() { SHA=`git rev-parse "$1"`; git commit --fixup "$SHA" && GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^"; }; f'
或者,还有一种可以处理未暂存文件(通过储藏和取消储藏)的版本:
git config --global alias.amend-to '!f() { SHA=`git rev-parse "$1"`; git stash -k && git commit --fixup "$SHA" && GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^" && git stash pop; }; f'
这个命令最大的优点就是它不需要使用vim。
(1)当然,前提是在rebase过程中没有冲突。
git amend-to <REV> # e.g.
git amend-to HEAD~1
git amend-to aaaa1111
在我看来,amend-to
这个名称似乎很合适。将其与 --amend
进行比较:
git add . && git commit --amend --no-edit
# vs
git add . && git amend-to <REV>
git config --global alias.<NAME> '!<COMMAND>'
- 创建名为<NAME>
的全局Git别名,它将执行非Git命令<COMMAND>
f() { <BODY> }; f
- 一个“匿名”的Bash函数。SHA=`git rev-parse "$1"`;
- 将参数转换为Git修订,并将结果分配给变量SHA
git commit --fixup "$SHA"
- 用于SHA
的修复提交。更多信息请参见git-commit
文档GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^"
git rebase --interactive "$SHA^"
部分已经在其他答案中涵盖。--autosquash
是与git commit --fixup
一起使用的选项,请参阅git-rebase
文档以了解更多信息GIT_SEQUENCE_EDITOR=true
使整个过程非交互式。我从这篇博客文章中学到了这个技巧。amend-to
处理未暂存的文件:git config --global alias.amend-to '!f() { SHA=
git rev-parse "$1"; git stash -k && git commit --fixup "$SHA" && GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^" && git stash pop; }; f'
- Detharielgit rebase --onto
。假设您想修改Commit1
。首先,请从Commit1
之前的分支中创建一个新分支:git checkout -b amending [commit before Commit1]
第二步,使用 cherry-pick
命令获取 Commit1
:
git cherry-pick Commit1
现在,请修改您的更改,创建Commit1'
:
git add ...
git commit --amend -m "new message for Commit1"
最后,将所有其他更改都存储后,将其余的提交移植到您新提交的 master
顶部:
git rebase --onto amending Commit1 master
阅读:"将位于amending
分支和master
分支之间(不含Commit1)的所有提交,rebase到当前分支上。也就是说,剪切掉旧的Commit1,只保留Commit2和Commit3。你也可以使用cherry-pick方式,但这种方式更简单。
记得清理你的分支!
git branch -d amending
git checkout -b amending Commit1~1
命令获取之前的提交记录。 - Arin Taylorgit checkout -b amending Commit1
? - Haoshugit stash
+ rebase
自动化
当我需要多次修改旧提交以进行 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"
)
用法:
git add
git-amend-old $old_sha
我喜欢这个方法胜过 --autosquash
,因为它不会压缩其他无关的修补。
git amend
的默认选项,使用当前存储来应用更改到特定提交,非常聪明! - caiohamamura最佳选择是使用"交互式变基命令"。
git rebase
命令非常强大,它允许您编辑提交消息、合并提交、重新排序等。每次重新设置提交时,无论内容是否更改,每个提交都将创建一个新的SHA!您应该谨慎使用此命令,因为它可能会对与其他开发人员协作的工作产生重大影响。他们可能会在您重新设置某些提交时开始使用您的提交。在您强制推送提交之后,他们将不同步,您可能会发现处于混乱状态。所以要小心!
建议在重新设置之前创建一个
备份
分支,这样当您发现事情失控时,可以返回到以前的状态。
git rebase -i <base>
-i
代表 "交互式"。请注意,您可以在非交互模式下执行变基。例如:
#interactivly rebase the n commits from the current position, n is a given number(2,3 ...etc)
git rebase -i HEAD~n
HEAD
表示您当前的位置(可以是分支名称或提交 SHA)。~n
表示“往前 n”,因此 HEAD~n
将是您当前所在的提交之前的“n”个提交的列表。
git rebase
有不同的命令,例如:
p
或 pick
:保持提交信息不变。r
或 reword
:保留提交的内容但更改提交消息。s
或 squash
:将此提交的更改合并到上一个提交中(列表中的前一个提交)。... 等等。
注意:最好让 Git 与您的代码编辑器配合使用,以使事情变得更简单。例如,如果您使用 Visual Code,您可以像这样添加:git config --global core.editor "code --wait"
。或者您可以搜索谷歌以了解如何将您喜欢的代码编辑器与 GIT 关联起来。
git rebase
的示例我想要更改我最近的两个提交,所以我按照以下步骤进行:
#This to show all the commits on one line
$git log --oneline
4f3d0c8 (HEAD -> documentation) docs: Add project description and included files"
4d95e08 docs: Add created date and project title"
eaf7978 (origin/master , origin/HEAD, master) Inital commit
46a5819 Create README.md
现在我使用git rebase
来修改最近两个提交的信息:
$ git rebase -i HEAD~2
它会打开代码编辑器并显示如下内容:
pick 4d95e08 docs: Add created date and project title
pick 4f3d0c8 docs: Add project description and included files
# Rebase eaf7978..4f3d0c8 onto eaf7978 (2 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
...
由于我想要修改这2次提交的提交信息。因此,我会在pick
的位置上输入r
或reword
。然后保存文件并关闭标签页。
请注意,rebase
是一个多步骤的过程,下一步是更新消息。还要注意,提交按照时间倒序显示,因此最后一个提交在最上面一行,第一个提交在最下面一行等等。
更新消息: 更新第一个消息:
docs: Add created date and project title to the documentation "README.md"
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
...
保存并关闭 编辑第二个消息
docs: Add project description and included files to the documentation "README.md"
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
...
保存并关闭。
你将在rebase结束时收到这样的消息:Successfully rebased and updated refs/heads/documentation
,这意味着你成功了。你可以显示更改:
5dff827 (HEAD -> documentation) docs: Add project description and included files to the documentation "README.md"
4585c68 docs: Add created date and project title to the documentation "README.md"
eaf7978 (origin/master, origin/HEAD, master) Inital commit
46a5819 Create README.md
我希望这能帮助新用户 :).
我发现自己经常需要修复过去的提交,因此我写了一个脚本来处理它。
以下是工作流程:
git commit-edit <commit-hash>
这将使您进入要编辑的提交。
按您原本希望的方式修复和暂存提交。
(您可能需要使用git stash save
命令来保存未提交的任何文件)
使用--amend
重新提交,例如:
git commit --amend
完成变基:
git rebase --continue
为了使上述内容生效,请将以下脚本放入名为 git-commit-edit
的可执行文件中,放在您的 $PATH
中的某个位置:
#!/bin/bash
set -euo pipefail
script_name=${0##*/}
warn () { printf '%s: %s\n' "$script_name" "$*" >&2; }
die () { warn "$@"; exit 1; }
[[ $# -ge 2 ]] && die "Expected single commit to edit. Defaults to HEAD~"
# Default to editing the parent of the most recent commit
# The most recent commit can be edited with `git commit --amend`
commit=$(git rev-parse --short "${1:-HEAD~}")
message=$(git log -1 --format='%h %s' "$commit")
if [[ $OSTYPE =~ ^darwin ]]; then
sed_inplace=(sed -Ei "")
else
sed_inplace=(sed -Ei)
fi
export GIT_SEQUENCE_EDITOR="${sed_inplace[*]} "' "s/^pick ('"$commit"' .*)/edit \\1/"'
git rebase --quiet --interactive --autostash --autosquash "$commit"~
git reset --quiet @~ "$(git rev-parse --show-toplevel)" # Reset the cache of the toplevel directory to the previous commit
git commit --quiet --amend --no-edit --allow-empty # Commit an empty commit so that that cache diffs are un-reversed
echo
echo "Editing commit: $message" >&2
echo