我有以下提交历史记录:

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

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


当前回答

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

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

以下是工作流程:

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

其他回答

如果出于某种原因,您不喜欢交互式编辑器,可以使用gitrebase--on。

假设您要修改Commit1。首先,从Commit1之前分支:

git checkout -b amending [commit before Commit1]

第二,用樱桃核抓取Commit1:

git cherry-pick Commit1

现在,修改您的更改,创建Commit1':

git add ...
git commit --amend -m "new message for Commit1"

最后,在隐藏了任何其他更改后,将其余的提交移植到master之上新提交:

git rebase --onto amending Commit1 master

阅读:“将Commit1(不包括在内)和master(包括在内)之间的所有提交重新纳入修改分支”。即Commit2和Commit3,将旧的Commit1完全删除。你可以随便摘,但这样更容易。

记得清理你的树枝!

git branch -d amending

我解决了这个问题,

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

r8gs4r commit 0

2) 我知道我需要合并哪个提交,即提交3。

因此,gitrebase-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(fixup将在没有提交消息的情况下进行合并)或s(带有提交消息的压缩合并可能在运行时发生变化)

然后保存你的树。

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

注意:除非你自己维护,否则这不是更好的方法。如果你的团队规模很大,重写git是不可接受的方法这棵树最终会陷入你们知道其他人不会发生的冲突。如果你愿意为了用更少的提交来保持树的整洁,可以尝试这样做,如果小团队,否则不可取。。。。。

当我需要更深入地修复历史中以前的提交时,我经常使用交互式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

你只需要保存并退出

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