我通常会提交一份提交清单供审核。如果我有以下提交:
HEAD
Commit3
Commit2
Commit1
...我知道我可以使用来修改head commit git commit --amend
。但是Commit1
鉴于它不是HEAD
提交,我该如何修改?
我通常会提交一份提交清单供审核。如果我有以下提交:
HEAD
Commit3
Commit2
Commit1
...我知道我可以使用来修改head commit git commit --amend
。但是Commit1
鉴于它不是HEAD
提交,我该如何修改?
Answers:
您可以使用git rebase。例如,如果要修改commit bbc643cd
,请运行
$ git rebase --interactive 'bbc643cd^'
请注意^
命令末尾的插入符号,因为实际上您需要在要修改的提交之前将基准变回到提交。
在默认编辑器中,将其修改pick
为edit
提及“ bbc643cd”的行。
保存文件并退出:git将解释并自动执行文件中的命令。您将发现自己处于刚刚创建commit的先前情况bbc643cd
。
在此刻, bbc643cd
是您的最后一次提交,您可以轻松地对其进行修改:进行更改,然后使用以下命令进行提交:
$ git commit --all --amend --no-edit
之后,输入:
$ git rebase --continue
返回上一个HEAD提交。
警告:请注意,这将更改该提交以及所有子项的SHA-1- 换句话说,这将从该点开始重写历史记录。如果使用命令推送,则可以中断存储库git push --force
reword
操作git rebase -i
代替edit
(它会自动打开编辑器并继续其余的变基步骤;这避免了使用,git commit --ammend
并且git rebase --continue
仅当您需要更改提交消息而不是内容时, )。
git stash
之前git rebase
和git stash pop
之后运行。
git commit --all --amend --no-edit
此处更明智。之后git rebase -i ...
,我所要做的就是恢复git commit --amend
正常git rebase --continue
。
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天;您可以在参考日志中找到它们:
$ 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
。
@
用作的简写HEAD
。感谢您发布此信息。
git reset @~
选择提交后正是我想做的git 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
甚至可以使用commit消息的前缀。
然后在提交之前启动交互式基础
$ git rebase e8adec4^ -i --autosquash
您的编辑器将打开,并已正确订购提交
pick e8adec4 Commit2
fixup 54e1a99 fixup! Commit2
pick b42d293 Commit3
您需要做的就是保存并退出
git commit --fixup=@~
代替git commit -m "fixup! Commit2"
。当您的提交消息较长时,这非常有用,并且要输入整个内容会很麻烦。
跑:
$ 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撤销(几乎)任何操作
git push --force
吗?
git push --force
是用您的本地提交覆盖远程提交。并非如此:)
如果由于某种原因您不喜欢交互式编辑器,则可以使用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
读:“将amending
所有提交Commit1
(到此,包括(不包括在内)和master
(包括)之间)重新建立到分支上”。也就是说,Commit2和Commit3,完全淘汰了旧的Commit1。您可以挑选它们,但是这种方式更容易。
记住要清理您的分支!
git branch -d amending
git checkout -b amending Commit1~1
用来获取先前的提交
git checkout -b amending Commit1
?
根据文档
修改旧的或多个提交消息的消息
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
我只是以为我会共享一个我为此使用的别名。它基于非交互式交互式资源库。要将其添加到您的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)鉴于在重新定基期间没有冲突,当然
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版本,并将结果分配给变量 SHA
git commit --fixup "$SHA"
-fixup-commit 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
是什么使整个事情不互动。我从这篇博客文章中学到了这种技巧。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'
我发现自己经常修正过去的提交,以至于为此写了一个脚本。
这是工作流程:
git commit-edit <commit-hash>
这将使您进入要编辑的提交。
首先修复并分阶段提交。
(您可能想git stash save
保留所有未提交的文件)
重做提交--amend
,例如:
git commit --amend
完成变基:
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
来到了这种方法(它可能与使用交互式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提交(尽管我真的不能说到什么程度)。
注意:从我们最初打算更改的提交开始,将更改所有提交哈希。
要获取非交互式命令,请将具有以下内容的脚本放入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>
。当然,如果发生冲突,它将仍然是交互式的。
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 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 add
如果已经在仓库中则不需要git-amend-old $old_sha
我喜欢这个,--autosquash
因为它不会压缩其他无关的修复程序。
git amend
可以使用当前存储将更改应用于特定提交,非常聪明!
我解决了
1)通过创建我想要的更改的新提交。
r8gs4r commit 0
2)我知道我需要与之合并的提交。这是提交3。
因此,git rebase -i HEAD~4
#4代表最近的4次提交(此处,提交3位于第四位)
3)在交互式变基中,最近的提交将位于底部。看起来会一样,
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与commit消息合并可以在运行时更改)
然后保存您的树。
现在合并完成与现有提交。
注意:除非您自己维护,否则它不是可取的方法。如果您的团队规模很大,那么它是不可接受的重写git tree的方法,将最终导致您知道其他情况不会发生的冲突。如果您想用更少的提交来保持树的干净,则可以尝试此操作,如果它的团队很小,则不推荐.....
最好的选择是使用“交互式rebase命令”。
该
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.
...
保存并关闭。
在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 commit
46a5819 Create README.md
希望对新用户有所帮助:)。
好吧,这种解决方案听起来可能很愚蠢,但是可以在某些情况下为您省钱。
我的一个朋友偶然遇到了非常大的文件提交问题(四个自动生成的文件,每个文件的大小在3GB到5GB之间),然后在此基础上又进行了一些其他代码提交,然后才意识到git push
不再起作用的问题!
文件已在其中列出,.gitignore
但是在重命名容器文件夹后,它们被公开并提交!现在,在此之上还提交了一些代码,但是该代码push
一直运行(试图上传GB的数据!),最后由于Github的文件大小限制而失败。
交互式变基或类似方法的问题在于,它们会处理这些巨大的文件,并且永远需要做任何事情。但是,在CLI中花费了将近一个小时的时间之后,我们不确定文件(和增量)是否实际上已从历史记录中删除或仅不包含在当前提交中。推送也不起作用,我的朋友真的被卡住了。
因此,我想到的解决方案是:
~/Project-old
。~/Project
)。 cp -r
将文件从~/Project-old
文件夹移到~/Project
。 mv
编辑,并.gitignore
正确地包含其中。 .git
最近克隆的文件夹~/Project
旧的。那就是有问题的历史记录的住所!push
编辑。此解决方案的最大问题是,它处理手动复制一些文件的问题,并且还将所有最近的提交合并为一个(显然带有新的提交哈希)。
最大的好处是,每一步都非常明显,它适用于大型文件(以及敏感文件),并且不会留下任何历史痕迹!