我通常会提交一份提交列表以供审核。如果我有以下提交:
HEAD
Commit3
Commit2
Commit1
...我知道我可以使用git commit --amend
. Commit1
但是,鉴于它不是提交,我该如何修改HEAD
?
我通常会提交一份提交列表以供审核。如果我有以下提交:
HEAD
Commit3
Commit2
Commit1
...我知道我可以使用git commit --amend
. Commit1
但是,鉴于它不是提交,我该如何修改HEAD
?
您可以使用git rebase。例如,如果要修改 commit bbc643cd
,请运行
$ git rebase --interactive 'bbc643cd^'
请注意^
命令末尾的插入符号,因为您实际上需要重新定位到要修改的提交之前的提交。
在默认编辑器中,在提及“bbc643cd”的行中修改pick
为edit
。
保存文件并退出:git 将解释并自动执行文件中的命令。您会发现自己处于刚刚创建 commit 的先前情况bbc643cd
。
此时,bbc643cd
是您的最后一次提交,您可以轻松修改它:进行更改,然后使用以下命令提交:
$ git commit --all --amend --no-edit
之后,键入:
$ git rebase --continue
返回到之前的 HEAD 提交。
警告:请注意,这将更改该提交以及所有子项的 SHA-1 - 换句话说,这将重写从那时起的历史记录。如果你使用命令推送,你可以打破 reposgit push --force
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 变基。如果您想使用其他编辑器,请使用git config --global core.editor your-favorite-text-editor
.
当我需要更深入地修复以前的提交时,我经常使用交互式rebase 。--autosquash
它从本质上加快了 ZelluX 的回答所说明的过程,并且当您需要编辑多个提交时特别方便。
从文档中:
--autosquash
当提交日志消息以“squash!...”(或“fixup!...”)开头,并且有一个标题以相同的...开头的提交时,自动修改 rebase -i 的 todo 列表,以便提交在要修改的提交之后立即标记为挤压
假设您的历史记录如下所示:
$ git log --graph --oneline
* b42d293 Commit3
* e8adec4 Commit2
* faaf19f Commit1
并且您有要修改到 Commit2 的更改,然后使用
$ git commit -m "fixup! Commit2"
或者,您可以使用 commit-sha 代替提交消息,"fixup! e8adec4
甚至只是提交消息的前缀。
然后在之前的提交上启动交互式变基
$ 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 -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,请运行以下命令(解释如下):
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'
该命令的最大优点是它是no-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
是什么使整个事情非交互。我从这篇博文中学到了这个技巧。如果由于某种原因您不喜欢交互式编辑器,您可以使用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
之间的所有提交”。即 Commit2 和 Commit3,将旧的 Commit1 完全剔除。你可以挑选它们,但这种方式更容易。Commit1
master
记得清理你的树枝!
git branch -d amending
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"
)
用法:
git add
修改源文件,如果已经在 repo中则不需要git-amend-old $old_sha
我喜欢这个,--autosquash
因为它不会压制其他不相关的修复。
最好的选择是使用"Interactive rebase command"。
该
git rebase
命令非常强大。它允许您编辑 提交消息、合并提交、重新排序......等等。每次您重新提交提交时,都会为每个提交创建一个新的 SHA,无论内容是否会更改!使用此命令时应小心,因为它可能会产生重大影响,尤其是在您与其他开发人员合作时。他们可能会在您重新设置一些基础时开始处理您的提交。在您强制推送提交后,它们将不同步,您稍后可能会在混乱的情况下发现。所以要小心!
建议在变基之前创建一个
backup
分支,这样每当您发现事情失控时,您都可以返回到以前的状态。
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 beforeé”,因此HEAD~n
将是“n”提交列表之前您当前所在的提交。
git rebase
有不同的命令,如:
p
或pick
保持原样。r
或reword
:保留提交的内容但更改提交消息。s
或squash
:将此提交的更改合并到上一个提交中(列表中它上面的提交)。... ETC。
注意:最好让 Git 与您的代码编辑器一起工作,以使事情变得更简单。例如,如果您使用可视代码,您可以像这样添加git config --global core.editor "code --wait"
。或者您可以在 Google 中搜索如何将您喜欢的代码编辑器与 GIT 相关联。
git rebase
我想更改我所做的最后 2 次提交,所以我处理如下:
#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
用来更改最后的 2 个提交消息:
$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 个提交的提交消息。所以我会输入r
orreword
代替pick
. 然后保存文件并关闭选项卡。请注意,这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
采用了这种方法(它可能与使用交互式 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
解决沿途的任何冲突)。应用 stash 并删除quick-branch
.
git rebase quick-branch
git stash pop
git branch -D quick-branch
你最终得到:
some-commit---0'---1'---2'---HEAD(feature-branch)
Git 不会在变基时复制 0 提交(尽管我不能真正说出在多大程度上)。
注意:所有提交哈希都从我们最初打算更改的提交开始更改。
要获取非交互式命令,请将包含此内容的脚本放在 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 <commit-to-modify>
. 当然,如果发生冲突,它仍然是交互式的。
我解决了这个,
1)通过创建带有我想要的更改的新提交..
r8gs4r commit 0
2)我知道我需要与哪个提交合并。这是提交3。
所以,git rebase -i HEAD~4
#4 代表最近的 4 次提交(这里的第 3 次提交是第 4 位)
3) 在交互式变基中,最近的提交将位于底部。它看起来很像,
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
(修复将在没有提交消息的情况下合并)或s
(与提交消息合并的壁球可以在运行时更改)
然后保存你的树。
现在与现有提交合并完成。
注意:除非您自己维护,否则它不是可取的方法。如果您的团队规模很大,那么重写 git 树的方法不是可接受的方法,最终会出现您知道其他人不会发生的冲突。如果你想用更少的提交来保持你的树干净,可以试试这个,如果它的小团队,否则它不是可取的.....
对我来说,这是为了从回购中删除一些凭据。我尝试变基并在尝试变基时遇到了大量看似无关的冲突--继续。不要费心尝试重新设置自己的基础,在 mac 上使用名为 BFG (brew install bfg) 的工具。
如果您还没有推送提交,那么您可以使用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 push
不再有效!
这些文件已被列出,.gitignore
但在重命名容器文件夹后,它们被暴露并提交了!现在在此之上还有一些代码的提交,但push
一直在运行(试图上传 GB 的数据!),最后由于Github 的文件大小限制而失败。
交互式变基或任何类似的问题是他们会处理这些巨大的文件,并且需要永远做任何事情。然而,在 CLI 中花费了近一个小时后,我们不确定文件(和增量)是否实际上已从历史记录中删除,或者根本不包含在当前提交中。推动也不起作用,我的朋友真的被卡住了。
所以,我想出的解决方案是:
~/Project-old
.~/Project
)。 cp -r
将文件~/Project-old
夹中的文件添加到~/Project
. mv
确保编辑并正确包含不需要签入的大量文件.gitignore
。 .git
文件夹中最近克隆的文件夹。~/Project
那就是有问题的历史记录所在的地方!push
编辑。该解决方案的最大问题是,它处理手动复制一些文件,并且还将所有最近的提交合并为一个(显然使用新的提交哈希。) B
最大的好处是,每一步都非常清晰,它适用于大文件(以及敏感文件),并且不会留下任何历史痕迹!
更改最后一次提交:
git commit --amend
// or
git commit --amend -m "an updated commit message"
不要修改公共提交 修改后的提交实际上是全新的提交,之前的提交将不再在您当前的分支上。
例如,如果您想更改最后三个提交消息,或该组中的任何提交消息,您将作为参数提供给 git rebase -i 您要编辑的最后一个提交的父级,即 HEAD~2 ^ 或 HEAD~3。记住 ~3 可能更容易,因为您正在尝试编辑最后三个提交,但请记住,您实际上是在指定四个提交之前,即您要编辑的最后一个提交的父级:
$ git rebase -i HEAD~3