我通常会提交一份提交列表以供审核。如果我有以下提交:
HEAD
Commit3
Commit2
Commit1
...我知道我可以使用git commit --amend
修改头部提交。但是,我如何修改Commit1
,因为它不是HEAD
提交?
答案 0 :(得分:2524)
您可以使用git rebase,例如,如果要修改回提交bbc643cd
,请运行
$ git rebase --interactive 'bbc643cd^'
在默认编辑器中,将pick
修改为要修改其提交的行中的edit
。进行更改,然后使用之前的相同消息提交它们:
$ git commit --all --amend --no-edit
修改提交,然后修改
$ git rebase --continue
返回上一个头部提交。
警告:请注意,这会更改该提交的SHA-1以及所有子项 - 换句话说,这将从此时开始重写历史记录。如果您使用命令git push --force
答案 1 :(得分:67)
交互式rebase与--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
您需要做的就是保存并退出
答案 2 :(得分:35)
执行命令
$ 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列表How to undo (almost) anything with git
的精彩链接答案 3 :(得分:17)
如果由于某种原因您不喜欢交互式编辑器,则可以使用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
答案 4 :(得分:10)
我只是想与我分享一个别名,我正在使用它。它基于非交互式交互式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'
这个命令的最大优点是它的 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
- 一个&#34;匿名&#34; bash功能。SHA=`git rev-parse "$1"`;
- 将参数转换为git revision,并将结果分配给变量SHA
git commit --fixup "$SHA"
- SHA
的fixup-commit。请参阅git-commit
docs GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^"
git rebase --interactive "$SHA^"
部分已被其他答案所涵盖。--autosquash
与git commit --fixup
一起使用,有关详细信息,请参阅git-rebase
docs GIT_SEQUENCE_EDITOR=true
是整个事物非互动的原因。这个黑客我学会了from this blog post。答案 5 :(得分:7)
采用这种方法(它可能与使用交互式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 :(得分:7)
我发现自己经常修改过去的提交,以至于为此编写了一个脚本。
这是工作流程:
git commit-edit <commit-hash>
这将使您进入要编辑的提交。
按您希望的那样修复并暂存提交。
(您可能希望使用git stash save
来保存所有未提交的文件)
使用--amend
重做提交,例如:
git commit --amend
完成rebase:
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
答案 7 :(得分:6)
要获取非交互式命令,请将包含此内容的脚本放入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>
。当然,如果你遇到冲突,它仍然是互动的。
答案 8 :(得分:6)
修改旧邮件或多个提交邮件的消息
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.
在您要更改的每个提交邮件之前,将选择替换为 reword 。假设您更改了列表中的第二个提交,您的文件将如下所示:
pick e499d89 Delete CNAME
reword 0c39034 Better README
pick f7fde4a Change the commit message but push the same commit.
保存并关闭提交列表文件,这将弹出一个新的编辑器,供您更改提交消息,更改提交消息并保存。
Finaly Force-推动修改后的提交。
git push --force
答案 9 :(得分:4)
我解决了这个问题,
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
( fixup 将合并而不提交消息)或s
( squash 与提交消息合并可以在运行时更改)
然后保存您的树。
现在与现有提交合并完成。
注意:除非您自己维护,否则它不是更好的方法。如果 你有很大的团队规模,这不是一个可以接受的方法来重写git 树将最终陷入你不会知道的冲突中。如果你想 保持你的树清洁与较少的提交可以尝试这个,如果它 小团队,否则不可取.....
答案 10 :(得分:3)
git stash
+ rebase
自动化
当我需要多次修改旧提交以进行Gerrit评论时,我一直在做:
git-amend-to() (
# 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-amend-to $old_sha
我喜欢--autosquash
,因为它不会挤压其他无关的修正。
答案 11 :(得分:2)
对我而言,它是从回购中删除一些凭据。 在试图改变时,我尝试了变调并遇到了大量看似无关的冲突 - 继续。 不要试图自己动手,在Mac上使用名为BFG(brew install bfg)的工具。
答案 12 :(得分:1)
最好的选择是使用“交互式变基命令” 。
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
的意思是“ nbeforeé”,因此HEAD~n
将是您当前正在提交的“ n”个提交的列表。
git rebase
具有不同的命令,例如:
p
或pick
保持提交不变。r
或reword
:保留提交内容,但更改提交消息。 s
或squash
:将此次提交的更改合并到先前的提交(列表中位于其上方的提交)。...等等
注意:最好让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
或reword
代替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.
...
保存并关闭。
在变基结束之前,您将收到以下消息: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
希望对新用户有所帮助:)。
答案 13 :(得分:0)
如果您尚未推送提交,则可以使用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添加到第一次提交中。
答案 14 :(得分:0)
好吧,这个解决方案听起来可能很愚蠢,但是可以在某些情况下为您省钱。
我的一个朋友遇到了意外提交了很多大文件(四个自动生成的文件,每个文件的大小从3GB到5GB),然后在实现之前又做了一些其他代码提交git push
不再工作的问题!
文件已在.gitignore
中列出,但是在重命名容器文件夹后,它们被公开并提交!现在,除此之外,还提交了一些代码,但是push
一直在运行(试图上传GB的数据!),最后由于Github's file size limits而失败。
交互式变基或类似方法的问题在于,它们将处理这些巨大的文件,并且永远需要做任何事情。但是,在CLI中花费了将近一个小时的时间之后,我们不确定文件(和增量)是否实际上已从历史记录中删除或仅不包含在当前提交中。推送也没有用,我的朋友真的被卡住了。
所以,我想出的解决方案是:
~/Project-old
。~/Project
)。 cp -r
文件夹中的文件手动~/Project-old
到~/Project
。 mv
不需要检入的大量文件已正确地包含在.gitignore
中。 .git
中的~/Project
文件夹。那就是有问题的历史记录的住所!push
编辑。此解决方案的最大问题是,它处理手动复制一些文件的问题,并且还将所有最近的提交合并到一个文件中(显然带有新的提交哈希)。B
最大的好处是,它在每个步骤中都非常明显,对大型文件(以及敏感文件)都非常有用,并且它不会留下任何历史痕迹!