首页 文章

如何修改指定的提交?

提问于
浏览
1791

我通常会提交一份提交列表以供审核 . 如果我有以下提交:

  • HEAD

  • Commit3

  • Commit2

  • Commit1

...我知道我可以使用 git commit --amend 修改头部提交 . 但是我怎么能修改 Commit1 ,因为它不是 HEAD 提交?

13 回答

  • 333

    跑:

    $ git rebase --interactive commit_hash^

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

    使用Vim,您可以为要更改,保存和退出的提交更改 pickreword:wq ) . 然后git将提示您标记为reword的每个提交,以便您可以更改提交消息 .

    每个提交消息都必须保存并退出( :wq )才能转到下一个提交消息

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

    EDIT :在 vim 中导航你使用 j 上升, k 下移, h 向左移动, l 向右移动(所有这些都在 NORMAL 模式下,按 ESC 进入 NORMAL 模式) . 要编辑文本,请按 i 以便进入 INSERT 模式,您可以在其中插入文本 . 按 ESC 返回 NORMAL 模式:)

    UPDATE :这是github列出的一个很棒的链接How to undo (almost) anything with git

  • 33

    要获取非交互式命令,请在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> . 当然,如果你遇到冲突,它仍然是互动的 .

  • 5

    对我来说,它是从一个回购中删除一些凭据 . 在试图改变时,我尝试了变调并遇到了大量看似无关的冲突 - 继续 . 不要试图自己动手,在Mac上使用名为BFG(brew install bfg)的工具 .

  • 7

    例如,如果要修改回提交 bbc643cd ,则可以使用git rebase运行

    $ git rebase --interactive 'bbc643cd^'
    

    在默认编辑器中,将 pick 修改为要修改其提交的行中的 edit . 进行更改,然后使用之前的相同消息提交它们:

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

    修改提交,然后修改

    $ git rebase --continue
    

    返回上一个头部提交 .

    WARNING :请注意,这将更改该提交的SHA-1 as well as all children - 换句话说,这将从该点向前重写历史记录 . You can break repos doing this如果使用命令 git push --force 推送

  • 3

    git stash + rebase automation

    因为当我需要为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 因为它没有压制其他无关的修正 .

  • 2

    使用令人敬畏的交互式rebase:

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

    找到所需的提交,将 pick 更改为 eedit ),然后保存并关闭该文件 . Git将回退到该提交,允许您:

    • 使用 git commit --amend 进行更改,或

    • 使用 git reset @~ 丢弃最后一次提交,但不丢弃对文件的更改(即将您带到'd edited the files, but hadn't提交时所处的位置) .

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

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

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

    在Git文档中阅读有关rewriting history的更多信息 .

    不要害怕变革

    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
    

    *注意像 --hard--force 这样的选项 - 他们可以丢弃数据 .
    *另外,请't rewrite history on any branches you'重新合作 .


    在许多系统上, git rebase -i 默认会打开Vim . Vim不像大多数现代文本编辑器那样工作,所以看一下how to rebase using Vim . 如果您更愿意使用其他编辑器,请使用 git config --global core.editor your-favorite-text-editor 进行更改 .

  • 6

    采用这种方法(它可能与使用交互式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)
    

    Rebase feature-branchquick-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 .

    注意:所有提交哈希值都是从我们最初要更改的提交开始更改的 .

  • 2350

    我解决了这个,

    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 替换为 ffixup 将合并而不提交消息)或 ssquash 合并提交消息可以在运行时更改)

    然后保存您的树 .

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

    注意:除非你自己维护,否则它不是更好的方法 . 如果你的团队规模很大,那么重写git树的方法就不会是你所知道的其他方法 . 如果你想保持你的树清洁与较少的提交可以尝试这个,如果它的小团队,否则它不是更好.....

  • 6

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

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

    git checkout -b amending [commit before Commit1]
    

    其次,用 cherry-pick grab Commit1

    git cherry-pick Commit1
    

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

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

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

    git rebase --onto amending Commit1 master
    

    阅读:“rebase,到分支 amendingCommit1 (非包含)和 master (包括)之间的所有提交” . 也就是说,Commit2和Commit3完全削减了旧的Commit1 . 你可以挑选它们,但这种方式更容易 .

    记得清理你的树枝!

    git branch -d amending
    
  • 13

    交互式rebase--autosquash 是我经常使用的东西,当我需要修复历史上更深层次的提交时 . 它实质上加快了ZelluX的答案所说明的过程,并且当您需要编辑多个提交时,它尤其方便 .

    从文档:

    --autosquash当提交日志消息以“squash!...”(或“fixup!...”)开头,并且有一个提交 Headers 以相同的...开头时,自动修改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
    

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

  • 63

    自动交互式rebase编辑,然后提交恢复准备好进行重建

    我发现自己经常修复过去的提交,因此我为它编写了一个脚本 .

    这是工作流程:

    git commit-edit <commit-hash>
    

    这将使您想要编辑的提交 .

    • 根据您的意愿修复并暂存提交 .

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

    • 使用 --amend 重做提交,例如:
    git commit --amend
    
    • 完成rebase:
    git rebase --continue
    

    要使上述工作正常,请将以下脚本放入 $PATH 中某处名为 git-commit-edit 的可执行文件中:

    #!/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
    
  • 6

    基于Documentation

    Amending the message of older or multiple commit messages

    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 替换为 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
    
  • 0

    完全非交互式命令(1)

    我只是觉得我'd share an alias that I'米用于此 . 它基于非交互式交互式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)当然,在变形期间没有冲突

    用法

    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 - 一个"anonymous" 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^" 部分已被其他答案所涵盖 .

    • --autosquashgit commit --fixup 一起使用,有关详细信息,请参阅git-rebase docs

    • GIT_SEQUENCE_EDITOR=true 是整个事物非交互的原因 . 这个黑客我学会了from this blog post .

相关问题