我有以下提交历史记录:

头部头部~头部~2头部~3

gitcommit--修改当前HEAD提交。但是如何修改HEAD~3?


当前回答

使用git-rebase。例如,要修改提交bbc643cd,请运行:

$ git rebase --interactive 'bbc643cd^'

请注意命令末尾的插入符号^,因为您实际上需要在要修改的提交之前重新返回提交。

在默认编辑器中,在提到bbc643cd的行中修改pick以进行编辑。

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

此时,bbc643cd是您最后一次提交,您可以很容易地修改它。进行更改,然后使用以下命令进行提交:

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

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

$ git rebase --continue

警告:请注意,这将更改该提交的SHA-1以及所有子项——换句话说,这将重写从此点开始的历史。如果使用命令gitpush-force进行推送,则可以断开repos。

其他回答

git存储+再基础自动化

因为当我需要多次修改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上游。

用法:

修改源文件,如果已在repo中,则无需gitaddgit修改旧$old_sha

我很喜欢这一点,因为它不会挤压其他无关的修复。

自动交互式重新基础编辑,然后提交恢复,以备重做

我发现自己经常修复一个过去的错误,为此我写了一个脚本。

以下是工作流程:

git提交编辑<提交哈希>这将在您要编辑的提交时将您丢弃。修复并按您希望的方式进行提交。(您可能希望使用git存储保存来保存未提交的文件)重新提交--修改,如:修改最后一次提交完成重新基准:git rebase—继续

为了实现上述功能,请将以下脚本放入$PATH中名为gitcommitedit的可执行文件中:

#!/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:

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

找到所需的提交,将pick更改为e(编辑),然后保存并关闭文件。Git将返回到该提交,允许您:

使用gitcommit--修改以进行更改,或使用gitreset@~放弃最后一次提交,但不放弃对文件的更改(即,将您带到编辑文件但尚未提交的位置)。

后者对于执行更复杂的任务(如拆分为多个提交)非常有用。

然后,运行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

*注意一些选项,比如硬和强制,它们可以丢弃数据。*此外,不要重写您正在合作的任何分支的历史。



在许多系统上,git-rebase-i将默认打开Vim。Vim不像大多数现代文本编辑器那样工作,所以看看如何使用Vim重新设置基础。如果您希望使用不同的编辑器,请使用git-config--global-core.editor或您最喜欢的文本编辑器对其进行更改。

当我需要更深入地修复历史中以前的提交时,我经常使用交互式rebase和--autosquash。它本质上加快了ZelluX的回答所说明的过程,当您需要编辑多个提交时,它特别方便。

根据文档:

--自动撤销当提交日志消息以“squash…​“(或”修复…​“),并且有一个提交的标题以相同的开头…​, 自动修改rebase-i的todo列表,以便标记为挤压的提交在要修改的提交之后立即出现

假设您的历史记录如下:

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

并且您有要修改为Commit2的更改,然后使用

$ git commit -m "fixup! Commit2"

或者,您可以使用commit sha而不是commit消息,因此“fixup!e8adec4”或甚至只是commit消息的前缀。

然后在提交之前启动交互式重新基础

$ git rebase e8adec4^ -i --autosquash

编辑器将打开已正确排序的提交

pick e8adec4 Commit2
fixup 54e1a99 fixup! Commit2
pick b42d293 Commit3

你只需要保存并退出

完全非交互式命令(1)

我只是想分享一个我正在使用的别名。它基于非交互式交互式数据库。要将其添加到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) 当然,考虑到重新启动期间没有冲突

用法

git amend-to <REV> # e.g.
git amend-to HEAD~1
git amend-to aaaa1111

将名称修改为似乎合适IMHO。将流程与--修订:

git add . && git commit --amend --no-edit
# vs
git add . && git amend-to <REV>

解释

git-config—全局别名<名称>'<COMMAND>'-创建名为<NAME>的全局git别名,该别名将执行非git命令<COMMAND>f(){<BODY>};f-一个“匿名”bash函数。SHA=`git rev parse“$1”`;-将参数转换为gitrevision,并将结果赋给变量SHAgitcommit--fixup“$SHA”--fixup提交SHA。参见git提交文档GIT_SEQUENCE_EDITOR=true GIT rebase--交互式--autosquash“$SHA^”gitrebase——交互式“$SHA^”部分已被其他答案覆盖。--autosquash与gitcommit一起使用--fixup,有关更多信息,请参阅gitrebase文档GIT_SEQUENCE_EDITOR=true是使整个事情非交互式的原因。这是我从这篇博客文章中学到的。