题 如何在git中修改指定的提交?


我通常会提交一份提交列表以供审核。如果我有:

  • HEAD 
  • Commit3 
  • Commit2 
  • Commit1

我知道我可以修改头部提交 git commit --amend,但我该如何修改 Commit1鉴于它不是 HEAD 承诺?


1704
2017-07-27 05:19


起源


请在此处查看替代答案: stackoverflow.com/a/18150592/520567 您接受的答案确实是您问题的准确答案,但如果您在决定使用编辑之前准备好新的提交,那么这个答案会更直接。它也适用于您想要与旧版本合并/压缩的多个提交。 - akostadinov
你也可以看到 拆分提交 在 Git工具 - 重写历史 了解更多信息。 - hakre
可能重复 如何修改现有的,未删除的提交? - tkruse


答案:


例如,如果要修改回提交,可以使用git rebase bbc643cd, 跑

$ git rebase --interactive 'bbc643cd^'

在默认编辑器中,修改 pick 至 edit 在您要修改其提交的行中。进行更改,然后使用之前的相同消息提交它们:

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

修改提交,然后修改

$ git rebase --continue

返回上一个头部提交。

警告:请注意,这将更改该提交的SHA-1 以及所有的孩子  - 换句话说,这会重写从那时起的历史。 你可以打破回购这样做 如果你推动使用命令 git push --force


2240
2017-07-27 05:28



此流程中另一个有趣的选项是,一旦您转移到要修改的提交,而不是修改文件并通过顶部的提交(您正在编辑的那个),您可能希望将该提交拆分为两个不同的提交(甚至更多)。在这种情况下,返回提交进行编辑,然后运行“git reset HEAD ^”。这会将该提交的修改后的文件放入舞台。现在根据需要选择并提交任何文件。这个流程在“git-rebase”手册页中得到了很好的解释。请参见“拆分提交”部分。 bit.ly/d50w1M - Diego Pino
在Git 1.6.6和更新版本中你可以使用 reword 行动 git rebase -i 代替 edit (它会自动打开编辑器并继续执行其余的rebase步骤;这样可以避免使用 git commit --ammend 和 git rebase --continue 当你只需要更改提交消息而不是内容时)。 - Chris Johnsen
在运行'git rebase hash ^ --interactive'之后,然后在提交上标记编辑,'git commit --amend'只显示提交消息 - 而不是实际代码。如何更改已提交的代码?谢谢! - mikemaccana
值得注意的是,您可能需要运行 git stash 之前 git rebase 和 git stash pop 之后,如果您有待更改。 - user123444555621
请注意,对于较新的git,遵循提示指示而不是盲目使用会更明智 git commit --all --amend --no-edit 这里。以后我只能做 git rebase -i ... 是 git commit --amend 通常那么 git rebase --continue。 - Eric Chen


使用令人敬畏的交互式rebase:

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


304
2018-04-29 17:50



你的答案中间是一个奇怪的地方,我只能描述为VIM的miniture广告。这与问题无关,只是让你的答案变得杂乱无章。 - Intentss
@Intentss:啊,我明白为什么这看起来很奇怪。其背后的原因是Vim是许多系统上的默认文本编辑器,因此许多人第一次使用交互式变基是一种屏幕,其中键入使光标在整个地方飞来飞去。然后,他们将编辑器切换到其他东西,他们的第二次交互式变基体验是相当正常的,但让他们想知道为什么它使用文本文件而不是GUI。要通过变基来实现流程,您需要像Vim或Emacs的rebase-mode这样的东西。 - Zaz
如果我不得不使用像Gedit或nano这样的东西来进行交互式反射,那么我会减少很多。也许这不会是一件坏事,因为我有点像一个反叛者。 - Zaz
好的。看到这么多人发现那部分无关紧要,我把它简化为3行,并解释了如果需要如何更改编辑器。 - Zaz
真棒!我不知道你可以用 @ 作为简写 HEAD。感谢您发布此内容。 - James Ko


互动 变基 同 --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

你需要做的就是保存并退出


57
2017-09-29 17:59



你也可以使用 git commit --fixup=@~ 代替 git commit -m "fixup! Commit2"。当您的提交消息更长时,这尤其有用,输入整个内容会很麻烦。 - Zaz


跑:

$ git rebase --interactive commit_hash^

^ 表示要编辑的提交数量,如果它只是一个(您指定的提交哈希),那么您只需添加一个 ^

使用Vim你可以改变单词 pick 至 reword 对于要更改,保存和退出的提交(:wq)。然后git将提示您标记为reword的每个提交,以便您可以更改提交消息。

您必须保存并退出的每个提交消息(:wq)转到下一个提交消息

如果要退出而不应用更改,请按 :q!

编辑:导航 vim 你用 j 上去, k 往下走, h 离开,和 l 向右走(这一切都在 NORMAL 模式,按 ESC 要去 NORMAL 模式)。 要编辑文本,请按 i 这样你就进入了 INSERT 模式,您插入文本的位置。 按 ESC 回去 NORMAL 模式:)

UPDATE:这是github上市的一个很棒的链接 如何用git撤消(几乎)任何东西 


30
2017-07-02 19:11



为我工作完美。值得一提 git push --force? - u01jmg3
什么 git push --force 确实用你的本地提交覆盖了远程提交。这不是本主题的情况:) - betoharres
@BetuUuUu当然如果你的提交被推送到远程并且你在本地修改了提交消息,你会想强制推送到远程,不是吗? - Sudip Bhandari
@SudipBhandari这就是我的感受。我没有强迫,现在我有了一个额外的分支,将所有提交镜像回到我改变的消息,这是非常丑陋的。 - ruffin
互动式底板在开始时可能看起来很棘手。我写了一篇文章(附图片),详细介绍了它,一步一步: blog.tratif.com/2018/04/19/the-power-of-git-interactive-rebase - Tomasz Kaczmarzyk


如果由于某种原因你不喜欢交互式编辑器,你可以使用 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

12
2017-10-22 12:19



您可以使用 git checkout -b amending Commit1~1 获得先前的提交 - Arin Taylor


采用这种方法(它可能与使用交互式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提交。

注意:所有提交哈希值都是从我们最初要更改的提交开始更改的。


6
2018-06-01 11:57





要获取非交互式命令,请在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>。当然,如果你遇到冲突,它仍然是互动的。


4
2018-01-16 15:27



这很好用。我添加了一些额外的功能来完成一个脏树的零碎修正,以完善提交集。 `dirtydiff = $(git diff); if [“$ {dirtydiff}”!=“”];然后回声“Stashing dirty tree”>&2; git stash;网络连接; - Simon Feltman


完全非交互式命令(1)

我以为我会分享一个我正在使用的别名。它基于 非交互式 交互式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 <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>'  - 创建一个名为的全局git别名 <NAME> 这将执行非git命令 <COMMAND>
  • f() { <BODY> }; 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 是什么使整个事情非互动。我学到了这个黑客 来自这篇博文

4
2018-02-27 01:47



人们也可以 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' - Dethariel
我用它 --autostash rebase命令上的标志。 - idanp


对我来说,它是从一个回购中删除一些凭据。 在试图改变时,我尝试了变调并遇到了大量看似无关的冲突 - 继续。 不要试图自己动手,在mac上使用名为BFG(brew install bfg)的工具。


1
2017-11-07 07:24





我解决了这个,

1)通过创建我想要的更改的新提交..

r8gs4r commit 0

2)我知道我需要与它合并的提交。这是提交3。

所以, git rebase -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 (修理 将合并而不提交消息)或 s (壁球 合并提交消息可以在运行时更改)

然后保存您的树。

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

注意:除非你自己维护,否则它不是更好的方法。如果   你有很大的团队规模,这是不可接受的重写git的方法   树将最终陷入你不会知道的冲突中。如果你想   用较少的提交来保持你的树清洁可以尝试这个,如果它   小团队,否则不可取.....


1
2018-01-05 18:35



如果您不想在交互式rebase期间进行实时修改,这是一个很好的解决方案。 - Dunatotatos


基于 文档

修改旧的或多个提交消息的消息

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 e499d89 Delete CNAME
reword 0c39034 Better README
pick f7fde4a Change the commit message but push the same commit.

保存并关闭提交列表文件,这将弹出一个新的编辑器,您可以更改提交消息,更改提交消息并保存。

Finaly Force推动修改后的提交。

git push --force

0
2018-05-17 08:38