使用Git将我的最后一次X提交压缩在一起

Squash my last X commits together using Git

如何使用git将最后的x提交压缩为一个提交?


如果没有git rebasegit merge --squash,您可以很容易地做到这一点。在这个例子中,我们将压扁最后3次提交。

如果您想从头开始编写新的提交消息,这就足够了:

1
2
git reset --soft HEAD~3 &&
git commit

如果要开始编辑新的提交消息,并将现有的提交消息串联起来(即类似于pick/squash/squash/…/squash git rebase -i指令列表将开始使用的操作),则需要提取这些消息并将它们传递给git commit

1
2
git reset --soft HEAD~3 &&
git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"

这两种方法都以相同的方式将最后三个提交压缩为一个新提交。软重置只是重新指向您不想挤压的最后一次提交。无论是索引还是工作树都不会被软重置所触动,从而使索引处于新提交所需的状态(即,它已经拥有了您将要"丢弃"的提交的所有更改)。


如手册所述,使用git rebase -i 并在第二次和后续提交时用"挤压"或"修复"替换"拾取"。

在这个例子中,要么是sha1散列,要么是当前分支头部的相对位置,从中分析REBASE命令的提交。例如,如果用户希望在过去查看来自当前头部的5个提交,则命令是git rebase -i HEAD~5


你可以用git merge --squash来做这个,它比git rebase -i稍微优雅一些。假设您在master上,希望将最后12个提交压缩为一个。

警告:首先,确保您的工作确认git status是干净的(因为git reset --hard将丢弃阶段性和非阶段性的更改)

然后:

1
2
3
4
5
6
7
8
9
10
11
# Reset the current branch to the commit just before the last 12:
git reset --hard HEAD~12

# HEAD@{1} is where the branch was just before the previous command.
# This command sets the state of the index to be as it would just
# after a merge from that commit:
git merge --squash HEAD@{1}

# Commit those squashed changes.  The commit message will be helpfully
# prepopulated with the commit messages of all the squashed commits:
git commit

git merge的文档更详细地描述了--squash选项。

更新:与Chris Johnsen在其答案中建议的更简单的git reset --soft HEAD~12 && git commit相比,此方法的唯一真正优势是,您得到的提交消息预先填充了您要压缩的每个提交消息。


我建议尽可能避免使用git reset,尤其是对于git新手。除非您真的需要基于许多提交实现流程的自动化,否则还有一种不那么奇特的方法…

  • 将要挤压的提交放到一个工作的分支上(如果它们还没有),为此使用gitk
  • 查看目标分支(例如"master")。
  • 埃多克斯1〔19〕
  • 埃多克斯1〔14〕
  • 将根据挤压预填充提交消息。


    根据克里斯·约翰森的回答,

    从bash(或Windows上的Git bash)添加全局"squash"别名

    1
    git config --global alias.squash '!f(){ git reset --soft HEAD~${1} && git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"; };f'

    …或使用Windows的命令提示:

    1
    git config --global alias.squash"!f(){ git reset --soft HEAD~${1} && git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"; };f"

    您的~/.gitconfig现在应该包含以下别名:

    1
    2
    [alias]
        squash ="!f(){ git reset --soft HEAD~${1} && git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"; };f"

    用途:

    1
    git squash N

    …它会自动压缩最后一个N承诺,包括。

    注意:结果提交消息按顺序是所有压缩提交的组合。如果您对此不满意,您可以随时使用cx1(17)手动修改它。(或者,编辑别名以符合您的口味。)


    多亏了这篇便利的博客文章,我发现您可以使用此命令压缩最后3次提交:

    1
    git rebase -i HEAD~3

    这很方便,因为即使您在没有跟踪信息/远程回购的本地分支上,它也能工作。

    该命令将打开交互式钢筋网编辑器,然后允许您根据正常情况重新排序、挤压、返工等。

    使用交互式钢筋网编辑器:

    交互式钢筋网编辑器显示最后三次提交。这个约束是由HEAD~3在运行命令git rebase -i HEAD~3时确定的。

    最新的提交,HEAD首先显示在第1行。以#开头的行是注释/文档。

    显示的文档非常清楚。在任何给定的行上,您都可以将命令从pick更改为您选择的命令。

    我更喜欢使用命令fixup,因为这样会"压扁"上面一行中commit对commit的更改,并丢弃commit的消息。

    因为第1行的commit是HEAD,所以在大多数情况下,您会将其保留为pick。您不能使用squashfixup,因为没有其他承诺可以将该承诺压扁。

    氧化镁


    如果使用TortoisGit,则可以使用函数Combine to one commit

  • 打开TortoisGit上下文菜单
  • 选择Show Log
  • 在日志视图中标记相关提交
  • 从上下文菜单中选择Combine to one commit
  • Combine commits

    此函数自动执行所有必需的单个git步骤。不幸的是,只能用于Windows。


    基于本文,我发现这个方法对于我的用例来说更容易。

    我的"dev"分支在96次提交之前领先于"origin/dev"(因此这些提交尚未推送到远程)。

    我想在推动变革之前把这些承诺压缩成一个。我宁愿将分支重置为"origin/dev"状态(这将使96提交中的所有更改保持未过时),然后立即提交更改:

    1
    2
    3
    git reset origin/dev
    git add --all
    git commit -m 'my commit message'


    为此,可以使用以下git命令。

    1
     git rebase -i HEAD~n

    n(=4这里)是最后一次提交的次数。然后你有以下选择,

    1
    2
    3
    4
    pick 01d1124 Message....
    pick 6340aaa Message....
    pick ebfd367 Message....
    pick 30e0ccb Message....

    更新如下:

    1
    2
    3
    4
    p 01d1124 Message....
    s 6340aaa Message....
    s ebfd367 Message....
    s 30e0ccb Message....

    有关详细信息,请单击链接

    祝你好运!!


    1)标识提交短哈希

    1
    2
    3
    4
    5
    # git log --pretty=oneline --abbrev-commit
    abcd1234 Update to Fix for issue B
    cdababcd Fix issue B
    deab3412 Fix issue A
    ....

    这里甚至可以使用git log --oneline来获取短散列值。

    2)如果要挤压(合并)最后两个提交

    1
    # git rebase -i deab3412

    3)这将打开一个用于合并的nano编辑器。它看起来像下面

    1
    2
    3
    4
    ....
    pick cdababcd Fix issue B
    pick abcd1234 Update to Fix for issue B
    ....

    4)将pick改为squash,该词出现在abcd1234之前。重命名后应该如下所示。

    1
    2
    3
    4
    ....
    pick cdababcd Fix issue B
    squash abcd1234 Update to Fix for issue B
    ....

    5)现在保存并关闭nano编辑器。按ctrl + o,按Enter保存。然后按ctrl + x退出编辑器。

    6)然后,nano编辑器再次打开以更新注释,如有必要,进行更新。

    7)现在它被成功压缩,您可以通过检查日志来验证它。

    1
    2
    3
    4
    # git log --pretty=oneline --abbrev-commit
    1122abcd Fix issue B
    deab3412 Fix issue A
    ....

    8)现在按回购。注意在分行名称前加上+号。这意味着用力推。

    1
    # git push origin +master

    注:这是基于在ubuntushell上使用git。如果您使用的是不同的操作系统(WindowsMac),那么上面的命令除了编辑器外是相同的。你可能会得到不同的编辑。


    失范的答案是好的,但我对此感到不安全,所以我决定添加一些截图。

    步骤0:git日志

    看看你和git log在哪里。最重要的是,找到不想挤压的第一个提交的提交哈希。所以只有:

    enter image description here

    第一步:Git Rebase

    执行git rebase -i [your hash],在我的情况下:

    1
    $ git rebase -i 2d23ea524936e612fae1ac63c95b705db44d937d

    第二步:挑选/挤压你想要的

    在我的例子中,我想把第一次提交的所有内容都压缩掉。顺序从头到尾,与git log中的顺序完全相反。就我而言,我想:

    氧化镁

    步骤3:调整消息

    如果只选择了一个提交并压扁了其余的提交,则可以调整一个提交消息:

    氧化镁

    就这样。一旦你保存了这个(:wq),你就完成了。用git log看一下。


    如果您在一个从黄金存储库(golden_repo_name克隆而来的远程分支(称为feature-branch上),那么下面是将您的承诺压缩为一种技术:

  • 检查黄金回购

    1
    git checkout golden_repo_name

  • 从中创建一个新分支(黄金回购),如下所示

    1
    git checkout -b dev-branch
  • 与本地分支合并

    1
    git merge --squash feature-branch

  • 提交您的更改(这将是dev分支中唯一进行的提交)

    1
    git commit -m"My feature complete"
  • 将分支推送到本地存储库

    1
    git push origin dev-branch


  • 在要合并提交的分支中,运行:

    1
    git rebase -i HEAD~(n number of commits back to review)

    例子:

    1
    git rebase -i HEAD~1

    这将打开文本编辑器,如果希望将这些提交合并在一起,则必须在每次提交之前使用"挤压"切换"pick"。来自文档:

    p,pick=使用提交s,squash=使用commit,但合并到以前的commit中

    例如,如果要将所有提交合并为一个提交,则"pick"是您所做的第一个提交,并且所有将来的提交(放在第一个提交下面)都应设置为"squash"。如果使用VIM,请在插入模式下使用:x保存并退出编辑器。

    然后继续重新设置钢筋:

    1
    git rebase --continue

    有关重写提交历史记录的更多信息,请参阅这篇有用的文章。


    真正方便的是:找到你想要压扁的提交哈希表,比如d43e15

    现在使用

    1
    2
    git reset d43e15
    git commit -am 'new commit name'


    这是超级duper Kludgy,但以一种很酷的方式,所以我会把它扔到戒指里:

    1
    GIT_EDITOR='f() { if ["$(basename $1)" ="git-rebase-todo" ]; then sed -i"2,\$s/pick/squash/" $1; else vim $1; fi }; f' git rebase -i foo~5 foo

    翻译:为git提供一个新的"编辑器",如果要编辑的文件名是git-rebase-todo(交互式的rebase提示),除了第一个"pick"更改为"squash",否则会生成vim,这样当提示您编辑压缩的提交消息时,您会得到vim。(显然,我在压扁分支foo的最后五个承诺,但您可以根据自己的喜好更改。)

    不过,我可能会照马克·朗盖尔的建议去做。


    如果您希望将每个提交挤到一个提交中(例如,第一次公开发布项目时),请尝试:

    1
    2
    git checkout --orphan <new-branch>
    git commit

    我认为最简单的方法是从master创建一个新的分支并进行合并——挤压特性分支。

    1
    2
    3
    git checkout master
    git checkout -b feature_branch_squashed
    git merge --squash feature_branch

    然后您就可以提交所有的更改了。


    要将最后10次提交压缩为1次提交:

    1
    git reset --soft HEAD~10 && git commit -m"squashed commit"

    如果还希望使用压缩的提交更新远程分支:

    1
    git push -f


    简单的一个始终有效的行程序,假设您当前在要挤压的分支上,master是它源于的分支,最新的commit包含要使用的commit消息和作者:

    1
    git reset --soft $(git merge-base HEAD master) && git commit --reuse-message=HEAD@{1}


    ??警告:"我的最后X次提交"可能不明确。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
      (MASTER)  
    Fleetwood Mac            Fritz
          ║                    ║
      Add Danny  Lindsey     Stevie      
        Kirwan  Buckingham    Nicks                                              
          ║         ╚═══╦══════╝    
    Add Christine       ║          
       Perfect      Buckingham
          ║           Nicks            
        LA1974══════════╝                                    
          ║                  
          ║                  
        Bill <══════ YOU ARE EDITING HERE
      Clinton        (CHECKED OUT, CURRENT WORKING DIRECTORY)

    在https://github.com/fleetwood-mac/band-history存储库的这段非常简短的历史中,您打开了一个请求,将bill clinton commit合并到原始(MASTERfleetwood mac commit中。

    您打开了一个拉请求,在Github上看到:

    四项承诺:

    • 添加丹尼·基尔万
    • 添加Christine Perfect
    • LA1974
    • 比尔·克林顿

    认为没有人愿意阅读完整的存储库历史。(实际上有一个存储库,请单击上面的链接!)你决定取消这些承诺。所以你去运行git reset --soft HEAD~4 && git commit。然后你把它放到Github上清理你的公关。

    会发生什么?你刚刚做了一个从弗里茨到比尔克林顿的承诺。因为你忘了昨天你在为这个项目的白金汉版工作。而且git log与你在Github上看到的不匹配。

    ??故事的寓意

  • 找到你想要的确切文件,和git checkout它们
  • 找到你想要保留在历史中的确切的先前承诺,以及git reset --soft
  • 制作一个直接从"从"到"到"扭曲的git commit

  • 对于与这样的工作流相关的问题的答案如何?

  • 许多本地提交,混合了来自master的多个合并,
  • 最后推到遥控器,
  • 由审阅者pr并合并到master。(是的,开发人员在公关之后更容易使用merge --squash,但团队认为这会减慢流程。)
  • 我在这个页面上没有看到这样的工作流。(可能是我的眼睛)如果我正确理解rebase,多个合并将需要多个冲突解决方案。我甚至不想去想那个!

    所以,这似乎对我们有用。

  • 埃多克斯1〔2〕
  • 埃多克斯1〔3〕
  • 江户十一〔四〕号
  • 在本地编辑和提交大量内容,定期合并主控形状
  • 埃多克斯1〔5〕
  • git merge --squash new-branch-temp//把所有的变化都放在阶段
  • 江户十一〔七〕号
  • 埃多克斯1〔8〕
  • 审阅者进行pr并合并到master。

  • 我发现一个更通用的解决方案不是指定"n"提交,而是您想要压扁的分支/提交ID。这比将提交计数到一个特定的提交更不容易出错,只需直接指定标记,或者如果确实想计数,可以指定head~n。

    在我的工作流程中,我启动了一个分支,我对该分支的第一次提交总结了目标(即,这通常是我将作为特性的"最终"消息推送到公共存储库的内容)。因此,完成后,我所要做的就是返回到第一条消息,然后准备推送。

    我使用别名:

    1
    squash = !EDITOR=""_() { sed -n 's/^pick //p' "\\$1"; sed -i .tmp '2,\\$s/^pick/f/' "\\$1"; }; _"" git rebase -i

    这将在销毁历史记录之前将其转储,因此,如果您希望恢复,可以通过从控制台获取旧的提交ID来恢复。(Solaris用户注意到它使用了GNU SED -i选项,Mac和Linux用户应该可以接受。)


    有问题的是,"last"的意思可能不明确。

    例如,git log --graph输出以下内容(简化):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    * commit H0
    |
    * merge
    |\
    | * commit B0
    | |
    | * commit B1
    | |
    * | commit H1
    | |
    * | commit H2
    |/
    |

    最后一次按时间提交是h0,merge,b0。要压扁它们,您必须在提交h1时重新设置合并的分支。

    问题是h0包含h1和h2(通常在合并之前和分支之后提交更多),而b0不包含,所以您必须至少管理h0、merge、h1、h2、b0的更改。

    可以使用REBASE,但使用方式不同,然后在其他人提到的答案中:

    江户十一〔一〕号

    这将向您显示选项(如其他答案中所述):

    1
    2
    3
    pick B1
    pick B0
    pick H0

    将挤压而不是拾取放置到h0:

    1
    2
    3
    pick B1
    pick B0
    s H0

    保存并退出后,rebase将在h1之后依次应用提交。这意味着它将要求您再次解决冲突(头将首先是h1,然后在应用时累积提交)。

    完成重新平衡后,您可以为挤压的h0和b0选择消息:

    1
    2
    3
    4
    5
    6
    7
    8
    * commit squashed H0 and B0
    |
    * commit B1
    |
    * commit H1
    |
    * commit H2
    |

    P.S.如果您只需重置为BO:(例如,使用reset --mixed,这里更详细地解释了https://stackoverflow.com/a/18690845/2405850):

    1
    2
    3
    git reset --mixed hash_of_commit_B0
    git add .
    git commit -m 'some commit message'

    然后压缩到h0、h1、h2的b0更改(分支后和合并前完全丢失对更改的提交)。


    1
    git rebase -i HEAD^^

    其中^的数目是x

    (在这种情况下,压扁最后两次提交)


    除了其他很好的答案外,我想补充一点,git rebase -i总是把我与承诺订单混淆,从旧订单到新订单,或者相反?这是我的工作流程:

  • git rebase -i HEAD~[N],其中n是我要加入的承诺数,从最近的一个开始。所以,git rebase -i HEAD~5的意思是"把最后5个承诺压成一个新的承诺";
  • 弹出编辑器,显示我要合并的提交列表。现在它们以相反的顺序显示:旧的提交位于顶部。将其中的所有提交标记为"squash"或"s",但第一个/更旧的提交除外:它将用作起点。保存并关闭编辑器;
  • 编辑器再次弹出一条新提交的默认消息:根据需要更改它,保存并关闭。挤压完成!
  • 来源和附加阅读:1,2。


    例如,如果您希望压缩分支(远程存储库)中最后3个提交到单个提交,例如:https://bitback.org

    我做的是

  • Git Reset--软头~3&;&;
  • Git提交
  • git push origin(branch_name)--力

  • 首先,我通过以下方式了解我的功能分支和当前主分支之间的提交数:

    1
    2
    git checkout master
    git rev-list master.. --count

    然后,我根据我的特性分支创建另一个分支,保持my-feature分支不变。

    最后,我跑步

    1
    2
    3
    4
    5
    6
    7
    8
    9
    git checkout my-feature
    git checkout -b my-rebased-feature
    git checkout master
    git checkout my-rebased-feature
    git rebase master
    git rebase head^x -i
    // fixup/pick/rewrite
    git push origin my-rebased-feature -f // force, if my-rebased-feature was ever pushed, otherwise no need for -f flag
    // make a PR with clean history, delete both my-feature and my-rebased-feature after merge

    希望有帮助,谢谢。


    如果您使用的是Gitup,请选择要与其父级合并的提交,然后按S键。每次提交都必须执行一次,但这比使用正确的命令行咒语要简单得多。尤其是如果你只是偶尔做一次。


    如果您使用的是Gitlab,您只需单击合并请求中的squash选项,如下所示。提交消息将是合并请求的标题。

    enter image description here


    只需将这个bash函数添加到.zshrc文件的bash中。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # Squash last X commits with a Commit message.
    # Usage: squash X 'COMMIT_MSG'
    # where X= Number of last commits.
    # where COMMIT_MSG= New commit msg.
    function squash() {
        if [ -z"${1}" -o -z"${2}" ]; then
            echo"Usage: \`squash X COMMIT_MSG\`"
            echo"X= Number of last commits."
            echo"COMMIT_MSG= New commit msg."
            return 1
        fi

        git reset --soft HEAD~"$1"
        git add . && git ci -m"$2" # With 100 emoji
        git push --force
    }

    那就跑吧

    1
    squash X 'New Commit Message'

    你完成了。