我通常会提交一份提交列表以供审核.如果我有以下提交:
HEAD
Commit3
Commit2
Commit1
...我知道我可以修改头部提交git commit --amend
.但是我怎么能修改Commit1
,因为它不是HEAD
提交?
例如,如果要修改回提交bbc643cd
,运行,可以使用git rebase
$ git rebase --interactive 'bbc643cd^'
在默认的编辑,修改^
,以pick
在您要修改其犯行.进行更改,然后使用之前的相同消息提交它们:
$ git commit --all --amend --no-edit
修改提交,然后修改
$ git rebase --continue
返回上一个头部提交.
警告:请注意,这将更改该提交的SHA-1 以及所有子项 - 换句话说,这将从此时开始重写历史记录.如果使用命令推送,可以中断repos执行此操作edit
git rebase -i @~9 # Show the last 9 commits in a text editor
找到所需的提交,更改pick
为e
(edit
),然后保存并关闭文件.Git将回退到该提交,允许您:
使用git commit --amend
进行更改,或
用于git reset @~
丢弃最后一次提交,但不丢弃对文件的更改(即将您带到编辑文件时所处的位置,但尚未提交).
后者对于执行更复杂的操作(如拆分为多个提交)非常有用.
然后,运行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
*注意喜欢--hard
和选择的选项--force
- 他们可以丢弃数据.
* 此外,不要在您正在协作的任何分支上重写历史记录.
在许多系统上,git rebase -i
默认情况下会打开Vim.Vim不像大多数现代文本编辑器那样工作,所以看看如何使用Vim进行rebase.如果您更愿意使用其他编辑器,请更改它git config --global core.editor your-favorite-text-editor
.
互动变基带--autosquash
的东西,当我需要修正内容以前犯历史更深我经常使用.它实质上加快了ZelluX的答案所说明的过程,并且当您需要编辑多个提交时,它尤其方便.
从文档:
--autosquash
当提交日志消息以"squash!..."(或"fixup!...")开头,并且有一个提交标题以相同的...开头时,自动修改rebase -i的待办事项列表以便提交标记为压缩是在提交修改后立即进行的
假设您的历史记录如下所示:
$ git log --graph --oneline * b42d293 Commit3 * e8adec4 Commit2 * faaf19f Commit1
并且您有要修改为Commit2的更改,然后使用提交更改
$ git commit -m "fixup! Commit2"
或者你可以使用commit-sha而不是commit消息,所以"fixup! e8adec4
甚至只是提交消息的前缀.
然后在提交之前启动交互式rebase
$ git rebase e8adec4^ -i --autosquash
您的编辑器将打开已经正确订购的提交
pick e8adec4 Commit2 fixup 54e1a99 fixup! Commit2 pick b42d293 Commit3
你需要做的就是保存并退出
跑:
$ git rebase --interactive commit_hash^
each ^
表示要编辑的提交数量,如果只有一个(您指定的提交哈希),则只需添加一个^
.
使用Vim,您pick
可以reword
为要更改的提交更改单词,保存并退出(:wq
).然后git将提示您标记为reword的每个提交,以便您可以更改提交消息.
您必须保存每个提交消息并退出(:wq
)以转到下一个提交消息
如果要退出而不应用更改,请按 :q!
编辑:导航vim
你使用j
上升,k
下降,h
左转,然后l
向右(所有这些在NORMAL
模式下,按下ESC
进入NORMAL
模式).要编辑文本,请按i
以便进入INSERT
插入文本的模式.按ESC
返回NORMAL
模式:)
更新:这是github列出的一个很棒的链接如何用git撤消(几乎)任何东西
如果由于某种原因你不喜欢交互式编辑器,你可以使用git rebase --onto
.
假设您要修改Commit1
.首先,从以前 分支Commit1
:
git checkout -b amending [commit before Commit1]
二,抢Commit1
用cherry-pick
:
git cherry-pick Commit1
现在,修改您的更改,创建Commit1'
:
git add ... git commit --amend -m "new message for Commit1"
最后,在隐藏任何其他更改之后,将其余提交移植到master
新提交之上:
git rebase --onto amending Commit1 master
阅读:"rebase,进入分支amending
,所有提交Commit1
(非包含)和master
(包括)".也就是说,Commit2和Commit3完全削减了旧的Commit1.你可以只挑选它们,但这种方式更容易.
记得清理你的树枝!
git branch -d amending
基于文档
修改旧的或多个提交消息的消息
git 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.
在要更改的每个提交邮件之前,使用reword替换pick.假设您更改了列表中的第二个提交,您的文件将如下所示:
pick e499d89 Delete CNAME reword 0c39034 Better README pick f7fde4a Change the commit message but push the same commit.
保存并关闭提交列表文件,这将弹出一个新的编辑器,您可以更改提交消息,更改提交消息并保存.
Finaly Force推动修改后的提交.
git push --force
我以为我会分享一个我正在使用的别名.它基于非交互式交互式rebase.要将它添加到您的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'
这个命令的最大优点是它不是vim的事实.
(1)当然,在rebase期间没有冲突
git amend-to# 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
git config --global alias.
- 创建一个名为的
执行非git命令的全局git别名
f() { }; f
- "匿名"bash功能.
SHA=`git rev-parse "$1"`;
- 将参数转换为git revision,并将结果赋给变量 SHA
git commit --fixup "$SHA"
- fixup-commit for 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
是什么使整个事物非互动.我从这篇博文中了解到了这个黑客攻击.
采用这种方法(它可能与使用交互式rebase完全相同),但对我而言,它是直截了当的.
注意:我提出这种方法是为了说明你可以做什么,而不是日常的替代方案.因为它有很多步骤(可能还有一些警告.)
假设您要更改提交,0
并且您当前正在使用feature-branch
some-commit---0---1---2---(feature-branch)HEAD
签出此提交并创建一个quick-branch
.您还可以将功能分支克隆为恢复点(在启动之前).
?(git checkout -b feature-branch-backup) git checkout 0 git checkout -b quick-branch
你现在会有这样的事情:
0(quick-branch)HEAD---1---2---(feature-branch)
阶段变化,隐藏其他一切.
git add ./example.txt git stash
提交更改并结帐回复 feature-branch
git commit --amend git checkout feature-branch
你现在会有这样的事情:
some-commit---0---1---2---(feature-branch)HEAD \ ---0'(quick-branch)
重订feature-branch
到quick-branch
(解决沿途的任何冲突).申请藏匿并删除quick-branch
.
git rebase quick-branch git stash pop git branch -D quick-branch
你最终得到:
some-commit---0'---1'---2'---HEAD(feature-branch)
在重新定位时,Git不会复制(虽然我不能真正说明在多大程度上)0提交.
注意:所有提交哈希值都是从我们最初要更改的提交开始更改的.
我发现自己经常修复过去的提交,因此我为它编写了一个脚本.
这是工作流程:
git commit-edit
这将使您想要编辑的提交.
根据您的意愿修复并暂存提交.
(你可能想用来git stash save
保存你没有提交的任何文件)
重做提交--amend
,例如:
git commit --amend
完成rebase:
git rebase --continue
要使上述工作正常,请将以下脚本放入一个名为git-commit-edit
somewhere 的可执行文件中$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
要获取非交互式命令,请将包含此内容的脚本放在PATH中:
#!/bin/sh # # git-fixup # Use staged changes to modify a specified commit set -e cmt=$(git rev-parse $1) git commit --fixup="$cmt" GIT_EDITOR=true git rebase -i --autosquash "$cmt~1"
通过暂存更改(使用git add
)然后运行来使用它git fixup
.当然,如果你遇到冲突,它仍然是互动的.
git 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" )
GitHub上游。
用法:
修改源文件,git add
如果已经在仓库中则不需要
git-amend-old $old_sha
我喜欢这个,--autosquash
因为它不会压缩其他无关的修复程序。