如何修改特定的提交?

我有以下提交历史:

  1. HEAD
  2. HEAD~
  3. HEAD~2
  4. HEAD~3

git commit --amend修改当前的HEAD提交。但是我如何修改HEAD~3

1234688 次浏览

使用#0。例如,要修改提交bbc643cd,请运行:

$ git rebase --interactive 'bbc643cd^'

请注意命令末尾的插入符号^,因为您实际上需要重新回到前面的就是你想修改的那个

在默认编辑器中,在提到bbc643cd的行中将pick修改为edit

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

此时,bbc643cd是你的最后一次提交,你可以轻松修改它。进行更改,然后使用命令提交它们:

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

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

$ git rebase --continue

警告:请注意,这将更改该提交的SHA-1以及所有儿童-换句话说,这将从该点向前重写历史。你可以这么做打破记录如果您使用命令git push --force推送。

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

git rebase -i @~9   # Show the last 9 commits in a text editor

找到您想要的提交,将pick更改为eedit),然后保存并关闭文件。Git将倒带到该提交,允许您:

  • 使用git commit --amend进行更改,或
  • 使用git reset @~丢弃最后一次提交,但不丢弃对文件的更改(即,将您带到编辑文件但尚未提交的位置)。

后者对于做更复杂的事情很有用,比如拆分成多个提交。

然后,运行git rebase --continue,Git将在修改后的提交之上重播后续更改。你可能会被要求修复一些合并冲突。

注意:@HEAD的简写,~是指定提交之前的提交。

阅读更多关于重写历史记录在Git文档中。


不要害怕重新开始

ProTip™:不要害怕尝试重写历史的“危险”命令*-默认情况下,Git不会在90天内删除您的提交;您可以在重新发布中找到它们:

$ git reset @~3   # go back 3 commits$ git reflogc4f708b HEAD@{0}: reset: moving to @~32c52489 HEAD@{1}: commit: more changes4a5246d HEAD@{2}: commit: make important changese8571e4 HEAD@{3}: commit: make some changes... earlier commits ...$ git reset 2c52489... and you're back where you started

*注意#0和#1等选项-它们可以丢弃数据。
另外,不要在你正在合作的任何分支上重写历史。



在许多系统上,git rebase -i将默认打开Vim。Vim不像大多数现代文本编辑器那样工作,所以看看如何使用Vim。如果你想使用不同的编辑器,请用git config --global core.editor your-favorite-text-editor更改它。

运行:

$ 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模式:)

更新:这是来自github列表如何用git撤消(几乎)任何东西的一个很棒的链接

当我需要更深入地修复以前的提交时,交互式rebase--autosquash是我经常使用的东西。它本质上加快了ZelluX的答案所示的过程,当你需要编辑多个提交时尤其方便。

从留档:

--autosquash

当提交日志消息以“squash! … " (或”修复! … "), 开始并且有一个提交的标题以相同的…开头时,自动修改rebase-i的待办事项列表,以便标记为压扁的提交就在要修改的提交之后

假设你有这样的历史:

$ git log --graph --oneline* b42d293 Commit3* e8adec4 Commit2* faaf19f Commit1

并且您有更改要修改到Comm 2,然后使用

$ git commit -m "fixup! Commit2"

或者,您可以使用提交-sha而不是提交消息,因此"fixup! e8adec4甚至只是提交消息的前缀。

然后在之前的提交上发起交互式rebase

$ git rebase e8adec4^ -i --autosquash

您的编辑器将打开已正确订购的提交

pick e8adec4 Commit2fixup 54e1a99 fixup! Commit2pick b42d293 Commit3

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

采用这种方法(它可能与使用交互式rebase完全相同),但对我来说,它有点简单。

注意:我介绍这种方法是为了说明您可以做什么,而不是日常的替代方法。因为它有很多步骤(可能还有一些警告)。

假设您想更改提交0,而您当前位于feature-branch

some-commit---0---1---2---(feature-branch)HEAD

签出此提交并创建quick-branch。您还可以克隆您的功能分支作为恢复点(在开始之前)。

?(git checkout -b feature-branch-backup)git checkout 0git checkout -b quick-branch

你现在会有这样的东西:

0(quick-branch)HEAD---1---2---(feature-branch)

舞台变化,隐藏其他一切。

git add ./example.txtgit stash

提交更改并签出回feature-branch

git commit --amendgit checkout feature-branch

你现在会有这样的东西:

some-commit---0---1---2---(feature-branch)HEAD\---0'(quick-branch)

feature-branch重新定位到quick-branch(解决沿途的任何冲突)。应用stash并删除quick-branch

git rebase quick-branchgit stash popgit branch -D quick-branch

最后你会得到:

some-commit---0'---1'---2'---HEAD(feature-branch)

Git在重写时不会重复(虽然我不能说到什么程度)0提交。

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

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

阅读:“rebase,到分支amending上,Commit1(非包容性)和master(包容性)之间的所有提交”。也就是说,Comm 2和Comm 3,完全切断旧的Comm 1。你可以只挑选它们,但这种方式更容易。

记得清理你的树枝!

git branch -d amending

对我来说,这是为了从存储库中删除一些凭据。我试图重新建立基础,并在试图重新建立基础的过程中遇到了大量看似无关的冲突-继续。不要费心尝试重新定位自己,在mac上使用名为BFG(brew install bfg)的工具。

我解决了这个问题,

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

r8gs4r commit 0

2)我知道我需要合并哪个提交。这是提交3。

所以,git rebase -i HEAD~4#4代表最近的4次提交(这里提交3在第4位)

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

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

4)这里我们需要重新安排提交,如果你想与特定的一个合并。

parent|_child
pick q6ade6 commit 3f r8gs4r commit 0pick vr43de commit 2pick ac123d commit 1

重新排列后,您需要将ppick替换为f修复将在没有提交消息的情况下合并)或s南瓜合并提交消息可以在运行时更改)

拯救你的树。

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

注意:这不是最好的方法,除非你自己维护。如果你的团队规模很大,这不是重写git的可接受方法树最终会陷入冲突,你知道其他人不会。如果你愿意为了保持你的树清洁与较少的提交可以尝试这个,如果它的#36825;小团队,不太好……

要获取非交互式命令,请在PATH中放置包含此内容的脚本:

#!/bin/sh## git-fixup# Use staged changes to modify a specified commitset -ecmt=$(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)

我只是想分享一个我为此使用的别名。它基于非交互式交互式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'

或者,也可以处理未暂存文件的版本(通过存储然后取消存储它们):

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~1git amend-to aaaa1111

amend-to这个名字似乎很合适,IMHO。将流程与--amend进行比较:

git add . && git commit --amend --no-edit# vsgit 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的修复提交。见#2文档
  • GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^"
    • git rebase --interactive "$SHA^"部分已被其他答案覆盖。
    • --autosquash是与git commit --fixup结合使用的,有关更多信息,请参阅#2文档
    • GIT_SEQUENCE_EDITOR=true是使整个事情非交互式的原因。我学到的这个黑客从这篇博客文章

基于需求文档

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

git rebase -i HEAD~3

上面显示了当前分支上最后3次提交的列表,如果需要更多,请将3更改为其他内容。列表将类似于以下内容:

pick e499d89 Delete CNAMEpick 0c39034 Better READMEpick f7fde4a Change the commit message but push the same commit.

在您要更改的每个提交消息之前将挑选替换为改写。假设您更改列表中的第二次提交,您的文件将如下所示:

pick e499d89 Delete CNAMEreword 0c39034 Better READMEpick f7fde4a Change the commit message but push the same commit.

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

最后,强制推送修改后的提交。

git push --force

自动交互式rebase编辑,然后提交恢复准备重做

我发现自己经常修复过去的提交,以至于我为它写了一个脚本。

下面是workflow:

  1. git提交-编辑<提交-哈希>

    这将把你放在你想要编辑的提交处。

  2. 按照您希望的方式修复和执行提交。

    (您可能希望使用git stash save来保留您没有提交的任何文件)

  3. 使用--amend重做提交,例如:

    git commit --amend
  4. Complete the rebase:

    git rebase --continue

For the above to work, put the below script into an executable file called git-commit-edit somewhere in your $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 ]]; thensed_inplace=(sed -Ei "")elsesed_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 commitgit commit --quiet --amend --no-edit --allow-empty  #  Commit an empty commit so that that cache diffs are un-reversed
echoecho "Editing commit: $message" >&2echo

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 stashgit checkout "$apply_to"git stash applygit add -ugit commit --amend --no-editnew_sha="$(git log --format="%H" -n 1)"git checkout "$current_branch"git rebase --onto "$new_sha" "$apply_to")

github上游

用法:

  • 修改源文件,如果已经在repo中,则无需git add
  • git-amend-old $old_sha

我喜欢这个超过--autosquash,因为它不会压扁其他不相关的修复程序。

如果您还没有推送提交,那么您可以使用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的文件大小限制而失败。

交互式rebase或任何类似的方法的问题在于,他们会处理这些巨大的文件,并且需要很长时间才能完成任何事情。然而,在CLI中花费了近一个小时后,我们不确定这些文件(和增量)是否真的从历史记录中删除了,或者根本不包含在当前提交中。推送也不起作用,我的朋友真的卡住了。

所以,我想出的解决方案是:

  1. 将当前git文件夹重命名为~/Project-old
  2. 再次从github克隆git文件夹(到~/Project)。
  3. 结帐到同一个分支。
  4. 手动cp -r文件夹从~/Project-old~/Project
  5. 确保不需要签入的大量文件是mved,并正确包含在.gitignore中。
  6. 还要确保不要用旧文件覆盖最近克隆的~/Project中的.git文件夹。这是有问题历史记录的日志所在的位置!
  7. 现在查看更改。它应该是所有最近提交的合并,不包括有问题的文件。
  8. 最后提交更改,最好是push'ed。

这个解决方案的最大问题是,它处理手动复制一些文件,并且它将所有最近的提交合并为一个(显然使用新的提交哈希)。

最大的好处是,它在每一步都非常清楚,它适用于大型文件(以及敏感文件),并且不会在历史上留下任何痕迹!

最好的选择是使用"交互式rebase命令"

git rebase命令非常强大。它允许您编辑提交消息,合并提交,重新排序……等等。

每次你重写一个提交时,都会为每个提交创建一个新的SHA无论内容是否会更改,都要提交!你应该使用此命令时要小心,因为它可能会有激烈的影响,特别是如果你与其他开发人员。他们可能会开始使用您的提交,而您重设一些。在你强制推送提交后,他们将被淘汰同步,你可能会发现稍后在一个混乱的情况。所以要小心!

建议在重置之前创建一个backup分支,以便当你发现事情失控时,你可以回到上一篇:

现在如何使用这个命令?

git rebase -i <base>

-i代表“互动”。请注意,您可以在非交互模式下执行rebase。例如:

#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 beveré”,因此HEAD~n将是您当前所在的“n”提交列表之前的“n”提交列表。

git rebase有不同的命令,如:

  • ppick保持提交原样。
  • rreword:保留提交的内容,但更改提交消息。
  • ssquash:将此提交的更改合并到前一个提交中(列表中位于其上的提交)。
  • 等等。

    注意:最好让Git与您的代码编辑器一起工作,以使事情更简单。例如,如果您使用可视化代码,您可以像这样添加git config --global core.editor "code --wait"。或者您可以在Google中搜索如何将您喜欢的代码编辑器与GIT相关联。

示例git rebase

我想更改我所做的最后2次提交,所以我这样处理:

  1. 显示当前提交:
    #This to show all the commits on one line$git log --oneline4f3d0c8 (HEAD -> documentation) docs: Add project description and included files"4d95e08 docs: Add created date and project title"eaf7978 (origin/master , origin/HEAD, master) Inital commit46a5819 Create README.md
  2. Now I use git rebase to change the 2 last commits messages:$git rebase -i HEAD~2It opens the code editor and show this:

    pick 4d95e08 docs: Add created date and project titlepick 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次提交的提交消息。所以我将键入rreword代替pick。然后保存文件并关闭选项卡。请注意,rebase是在多步骤过程中执行的,因此下一步是更新消息。另请注意,提交以相反的时间顺序显示,因此最后一次提交显示在该提交中,第一次提交显示在第一行中,依此类推。

  3. 更新消息:更新第一条消息:

    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....

    保存并关闭。

  4. 在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 commit46a5819 Create README.md

    希望对新用户有所帮助:)。

更改最后一次提交:

git commit --amend// orgit commit --amend -m "an updated commit message"

不要修改公共提交修改后的提交实际上是全新的提交,之前的提交将不再位于当前分支上。

例如,如果您想更改最后三个提交消息,或该组中的任何提交消息,您可以向git rebase-i提供要编辑的最后一个提交的父级作为参数,即HEAD~2^或HEAD~3。记住~3可能更容易,因为您正在尝试编辑最后三个提交,但请记住,您实际上是在您要编辑的最后一个提交的父级之前指定四个提交:

$ git rebase -i HEAD~3

了解更多

我执行以下操作,包括更改本地提交的日期和时间:

git rebase -i HEAD~6

~6是要显示的提交历史记录量。

  • 将要编辑的提交从pick更改为edit
  • 然后我保存并退出(在ubuntu中:Ctrl+O保存,Ctrl+X退出)
    1. 然后我跑:git commit --amend --date="2022-09-02T19:10:04" -m "NEW_MSG"
    1. 如果编辑打开,只需保存并退出。
    1. 然后确认并转到下一个提交或完成,如果它是最后一个,我执行:git rebase --continue

如果有更多的提交要编辑,则从第1点开始重复

最后我验证更改,如果一切正常,我做push

我遇到了同样的问题,这个:

  1. 首先将分支复制为x
  2. 然后努力休息到你想回去的地方
  3. 然后修改新的变化
  4. 在樱桃采摘之后,原始分支的所有其他变化
  5. 签出原来的分支
  6. 在修改后的回归之前重置原始分支
  7. rebase到x分支

我的骑手git日志:

git checkout -b x 77164a510f1c17ed650b87c2ebf0f7762ac6b2a2 --git reset --hard 0d038b5e3e3e2adef4bd6aab7653f922c3fdc63fgit add --ignore-errors -A -f -- src/Mores.Warehouse.Core.Api.ClientSdk/Mores.Warehouse.Core.Api.ClientSdk.csprojgit commit -F C:\Users\Hassan\AppData\Local\Temp\git-commit-msg-.txt --amend --git cherry-pick 77164a510f1c17ed650b87c2ebf0f7762ac6b2a2git checkout feature/MOR-2947 --git reset --hard 0d038b5e3e3e2adef4bd6aab7653f922c3fdc63fgit rebase x