git 如何修改指定的提交?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1186535/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 06:47:47  来源:igfitidea点击:

How to modify a specified commit?

gitgit-rewrite-history

提问by Sam Liao

I usually submit a list of commits for review. If I have the following commits:

我通常会提交一份提交清单以供。如果我有以下提交:

  1. HEAD
  2. Commit3
  3. Commit2
  4. Commit1
  1. HEAD
  2. Commit3
  3. Commit2
  4. Commit1

...I know that I can modify head commit with git commit --amend. But how can I modify Commit1, given that it is not the HEADcommit?

...我知道我可以用git commit --amend. 但是Commit1,鉴于它不是HEAD提交,我该如何修改?

回答by ZelluX

You can use git rebase. For example, if you want to modify commit bbc643cd, run

您可以使用git rebase。例如,如果要修改 commit bbc643cd,请运行

$ git rebase --interactive 'bbc643cd^'

Please note the caret ^at the end of the command, because you need actually to rebase back to the commit beforethe one you wish to modify.

请注意^命令末尾的插入符号,因为您实际上需要您希望修改的提交之前重新设定基址。

In the default editor, modify pickto editin the line mentioning 'bbc643cd'.

在默认编辑器中,在提及“bbc643cd”的行中修改pickedit

Save the file and exit: git will interpret and automatically execute the commands in the file. You will find yourself in the previous situation in which you just had created commit bbc643cd.

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

At this point, bbc643cdis your last commit and you can easily amend it: make your changes and then commit them with the command:

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

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

After that, type:

之后,键入:

$ git rebase --continue

to return back to the previous HEAD commit.

返回到之前的 HEAD 提交。

WARNING: Note that this will change the SHA-1 of that commit as well as all children-- in other words, this rewrites the history from that point forward. You can break repos doing thisif you push using the command git push --force

警告:请注意,这将更改该提交以及所有子项的 SHA-1 - 换句话说,这将重写从那时起的历史记录。如果你使用命令推送,你可以打破 repos 这样做git push --force

回答by Zaz

Use the awesome interactive rebase:

使用很棒的交互式变基:

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

Find the commit you want, change pickto e(edit), and save and close the file. Git will rewind to that commit, allowing you to either:

找到您想要的提交,更改picke( edit),然后保存并关闭文件。Git 将回退到该提交,允许您:

  • use git commit --amendto make changes, or
  • use git reset @~to discard the last commit, but not the changes to the files (i.e. take you to the point you were at when you'd edited the files, but hadn't committed yet).
  • 用于git commit --amend进行更改,或
  • 用于git reset @~放弃最后一次提交,但不放弃对文件的更改(即,将您带到编辑文件但尚未提交时的状态)。

The latter is useful for doing more complex stuff like splitting into multiple commits.

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

Then, run git rebase --continue, and Git will replay the subsequent changes on top of your modified commit. You may be asked to fix some merge conflicts.

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

Note: @is shorthand for HEAD, and ~is the commit before the specified commit.

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

Read more about rewriting historyin the Git docs.

在 Git 文档中阅读有关重写历史记录的更多信息。



Don't be afraid to rebase

不要害怕变基

ProTip?: ? Don't be afraid to experiment with "dangerous" commands that rewrite history* — Git doesn't delete your commits for 90 days by default; you can find them in the reflog:

专家提示?: ?不要害怕尝试重写历史记录的“危险”命令* — 默认情况下,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

* Watch out for options like --hardand --forcethough — they can discard data.
* Also, don't rewrite history on any branches you're collaborating on.

*注意像--hard--force虽然这样的选项- 它们可以丢弃数据。
* 另外,不要在您正在协作的任何分支上重写历史记录。





On many systems, git rebase -iwill open up Vim by default. Vim doesn't work like most modern text editors, so take a look at how to rebase using Vim. If you'd rather use a different editor, change it with git config --global core.editor your-favorite-text-editor.

在许多系统上,git rebase -i默认情况下会打开 Vim。Vim 不像大多数现代文本编辑器那样工作,所以看看如何使用 Vim 变基。如果您更愿意使用不同的编辑器,请将其更改为git config --global core.editor your-favorite-text-editor.

回答by thrau

Interactive rebasewith --autosquashis something I frequently use when I need to fixup previous commits deeper in the history. It essentially speeds up the process that ZelluX's answer illustrates, and is especially handy when you have more than one commit you need to edit.

互动变基--autosquash的东西,当我需要更深的修正内容以前犯的历史我经常使用。它从本质上加快了 ZelluX 的答案所说明的过程,并且在您需要编辑多个提交时尤其方便。

From the documentation:

从文档:

--autosquash

When the commit log message begins with "squash! …?" (or "fixup! …?"), and there is a commit whose title begins with the same …?, automatically modify the todo list of rebase -i so that the commit marked for squashing comes right after the commit to be modified

--autosquash

当提交日志消息以“squash! ...?”开头时 (或“修复!...?”),并且有一个提交的标题以相同的...开头

Assume you have a history that looks like this:

假设您的历史记录如下所示:

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

and you have changes that you want to amend to Commit2 then commit your changes using

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

$ git commit -m "fixup! Commit2"

alternatively you can use the commit-sha instead of the commit message, so "fixup! e8adec4or even just a prefix of the commit message.

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

Then initiate an interactive rebase on the commit before

然后在提交之前启动交互式 rebase

$ git rebase e8adec4^ -i --autosquash

your editor will open with the commits already correctly ordered

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

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

all you need to do is save and exit

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

回答by betoharres

Run:

跑:

$ git rebase --interactive commit_hash^

$ git rebase --interactive commit_hash^

each ^indicates how many commits back you want to edit, if it's only one (the commit hash that you specified), then you just add one ^.

每个^表示您要编辑的提交数量,如果只有一个(您指定的提交哈希),那么您只需添加一个^.

Using Vim you change the words pickto rewordfor the commits you want to change, save and quit(:wq). Then git will prompt you with each commit that you marked as reword so you can change the commit message.

使用Vim你换的话pickreword对你要更改的提交,保存并退出(:wq)。然后 git 将提示您标记为 reword 的每个提交,以便您可以更改提交消息。

Each commit message you have to save and quit(:wq) to go to the next commit message

你必须保存每条提交消息并退出(:wq)以转到下一条提交消息

If you want to exit without applying the changes, press :q!

如果您想退出而不应用更改,请按 :q!

EDIT: to navigate in vimyou use jto go up, kto go down, hto go left, and lto go right( all this in NORMALmode, press ESCto go to NORMALmode ). To edit a text, press iso that you enter the INSERTmode, where you insert text. Press ESCto go back to NORMALmode :)

编辑:要导航,vim您可以使用j向上、k向下、h向左和l向右(所有这些都在NORMAL模式中,按ESC进入NORMAL模式)。要编辑文本,请按i以进入INSERT插入文本的模式。按ESC返回NORMAL模式:)

UPDATE: Here's a great link from github listing How to undo (almost) anything with git

更新:这是来自 github 的一个很好的链接,列出了如何使用 git 撤消(几乎)任何事情

回答by FeepingCreature

If for some reason you don't like interactive editors, you can use git rebase --onto.

如果由于某种原因您不喜欢交互式编辑器,您可以使用git rebase --onto.

Say you want to modify Commit1. First, branch from beforeCommit1:

说你要修改Commit1. 首先,从之前分支Commit1

git checkout -b amending [commit before Commit1]

Second, grab Commit1with cherry-pick:

二,抢Commit1cherry-pick

git cherry-pick Commit1

Now, amend your changes, creating Commit1':

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

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

And finally, after having stashed any other changes, transplant the rest of your commits up to masteron top of your new commit:

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

git rebase --onto amending Commit1 master

Read: "rebase, onto the branch amending, all commits between Commit1(non-inclusive) and master(inclusive)". That is, Commit2 and Commit3, cutting the old Commit1 out entirely. You could just cherry-pick them, but this way is easier.

阅读:“重新定位到分支上amendingCommit1(非包含)和master(包含)之间的所有提交”。即 Commit2 和 Commit3,完全删除旧的 Commit1。您可以随意挑选它们,但这种方式更容易。

Remember to clean up your branches!

记得清理你的树枝!

git branch -d amending

回答by justMe

Based on Documentation

基于文档

Amending the message of older or multiple commit messages

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

git rebase -i HEAD~3 

The above displays a list of the last 3 commits on the current branch, change 3 to something else if you want more. The list will look similar to the following:

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

pick e499d89 Delete CNAME
pick 0c39034 Better README
pick f7fde4a Change the commit message but push the same commit.

Replace pickwith rewordbefore each commit message you want to change. Let say you change the second commit in the list, your file will look like the following:

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

pick e499d89 Delete CNAME
reword 0c39034 Better README
pick f7fde4a Change the commit message but push the same commit.

Save and close the commit list file, this will pop up a new editer for you to change your commit message, change the commit message and save.

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

Finaly Force-push the amended commits.

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

git push --force

回答by Dethariel

Completely non-interactive command(1)

完全非交互式命令(1)

I just thought I'd share an alias that I'm using for this. It's based on non-interactiveinteractive rebase. To add it to your git, run this command (explanation given below):

我只是想分享一个我为此使用的别名。它基于非交互式交互式 rebase。要将其添加到您的 git,请运行此命令(下面给出了解释):

git config --global alias.amend-to '!f() { SHA=`git rev-parse ""`; git commit --fixup "$SHA" && GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^"; }; f'

The biggest advantage of this command is the fact that it's no-vim.

这个命令的最大优点是它是no-vim



(1)given that there are no conflicts during rebase, of course

(1)当然,rebase期间没有冲突

Usage

用法

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

The name amend-toseems appropriate IMHO. Compare the flow with --amend:

amend-to恕我直言,这个名字似乎很合适。比较流量--amend

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

Explanation

解释

  • git config --global alias.<NAME> '!<COMMAND>'- creates a global git alias named <NAME>that will execute non-git command <COMMAND>
  • f() { <BODY> }; f- an "anonymous" bash function.
  • SHA=`git rev-parse "$1"`;- converts the argument to git revision, and assigns the result to variable SHA
  • git commit --fixup "$SHA"- fixup-commit for SHA. See git-commitdocs
  • GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^"
    • git rebase --interactive "$SHA^"part has been covered by other answers.
    • --autosquashis what's used in conjunction with git commit --fixup, see git-rebasedocsfor more info
    • GIT_SEQUENCE_EDITOR=trueis what makes the whole thing non-interactive. This hack I learned from this blog post.
  • 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。查看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是什么使整个事情变得非交互式。我从这篇博文中学到的这个技巧。

回答by Tom Hale

Automated interactive rebase edit followed by commit revert ready for a do-over

自动交互式 rebase 编辑,然后提交还原准备好进行重做

I found myself fixing a past commit frequently enough that I wrote a script for it.

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

Here's the workflow:

这是工作流程:

  1. git commit-edit <commit-hash>
    

    This will drop you at the commit you want to edit.

  2. Fix and stage the commit as you wish it had been in the first place.

    (You may want to use git stash saveto keep any files you're not committing)

  3. Redo the commit with --amend, eg:

    git commit --amend
    
  4. Complete the rebase:

    git rebase --continue
    
  1. git commit-edit <commit-hash>
    

    这将使您处于要编辑的提交处。

  2. 修复和暂存提交,如您希望的那样。

    (您可能想用它git stash save来保留您未提交的任何文件)

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

    git commit --amend
    
  4. 完成变基:

    git rebase --continue
    

For the above to work, put the below script into an executable file called git-commit-editsomewhere in your $PATH:

为使上述工作正常运行,请将以下脚本放入名为的可执行文件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

回答by Olga

Came to this approach (and it is probably exactly the same as using interactive rebase) but for me it's kind of straightforward.

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

Note: I present this approach for the sake of illustration of what you can do rather than an everyday alternative. Since it has many steps (and possibly some caveats.)

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

Say you want to change commit 0and you are currently on feature-branch

假设您要更改提交0并且您当前处于feature-branch

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

Checkout to this commit and create a quick-branch. You can also clone your feature branch as a recovery point (before starting).

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

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

You will now have something like this:

您现在将拥有如下内容:

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

Stage changes, stash everything else.

阶段变化,隐藏其他一切。

git add ./example.txt
git stash

Commit changes and checkout back to feature-branch

提交更改并结帐返回 feature-branch

git commit --amend
git checkout feature-branch

You will now have something like this:

您现在将拥有如下内容:

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

Rebase feature-branchonto quick-branch(resolve any conflicts along the way). Apply stash and remove quick-branch.

变基feature-branchquick-branch(解决沿途的任何冲突)。应用 stash 并删除quick-branch.

git rebase quick-branch
git stash pop
git branch -D quick-branch

And you end up with:

你最终得到:

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

Git will not duplicate (although I can't really say to what extent) the 0 commit when rebasing.

Git 不会在变基时复制(虽然我不能真正说出到什么程度) 0 提交。

Note: all commit hashes are changed starting from the commit we originally intended to change.

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

回答by Pelle Nilsson

To get a non-interactive command, put a script with this content in your PATH:

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

#!/bin/sh
#
# git-fixup
# Use staged changes to modify a specified commit
set -e
cmt=$(git rev-parse )
git commit --fixup="$cmt"
GIT_EDITOR=true git rebase -i --autosquash "$cmt~1"

Use it by staging your changes (with git add) and then run git fixup <commit-to-modify>. Of course, it will still be interactive if you get conflicts.

通过暂存更改(使用git add)来使用它,然后运行git fixup <commit-to-modify>. 当然,如果您遇到冲突,它仍然是交互式的。