如何将我的最后N次提交压缩在一起?

如何将我的最后N个提交压缩到一个提交中?

3010573 次浏览

使用git rebase -i <after-this-commit>并将第二次和后续提交的“选择”替换为“壁球”或“修复”,如该手册所述。

在此示例中,<after-this-commit>是SHA1哈希或来自当前分支的HEAD的相对位置,用于分析rebase命令的提交。例如,如果用户希望查看过去来自当前HEAD的5次提交,则命令为git rebase -i HEAD~5

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

警告:首先确保你提交了你的工作-检查git status是否干净(因为git reset --hard将丢弃分阶段和非分阶段的更改)

然后:

# 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

留档#0更详细地描述了--squash选项。


更新时间:与Chris Johnsen在他的回答中建议的更简单的git reset --soft HEAD~12 && git commit相比,此方法的唯一真正优势在于,您可以将要压缩的每个提交消息预先填充到提交消息中。

这是超级笨拙的,但以一种很酷的方式,所以我就把它扔进戒指里:

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提示符),则将除第一个“选择”之外的所有内容更改为“壁球”,否则会生成vim-因此,当提示您编辑压扁的提交消息时,您会得到vim。(显然,我正在压缩foo分支上的最后五个提交,但您可以随意更改。)

我可能会做什么Mark Longair建议,虽然。

您可以在没有git rebasegit merge --squash的情况下轻松完成此操作。在本例中,我们将压扁最后3次提交。

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

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

如果您想开始使用现有提交消息的串联编辑新的提交消息(即类似于选择/squash/squash/…/squashgit rebase -i指令列表将开始您的内容),那么您需要提取这些消息并将它们传递给git commit

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

这两种方法都以相同的方式将最后三次提交压缩为一个新提交。软重置只是将HEAD重新指向你不想压缩的最后一次提交。软重置不会触及索引和工作树,从而使索引处于新提交所需的状态(即它已经拥有你即将“丢弃”的提交的所有更改)。

基于克里斯·约翰森的回答

从bash:(或Windows上的Git Bash)添加全局“squash”别名

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的命令提示符:

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现在应该包含此别名:

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


用法:

git squash N

…它会自动将最后的N提交压缩在一起,包括在内。

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

我建议尽可能避免git reset——尤其是对于Git新手。除非您真的需要基于数量的提交来自动化流程,否则有一种不那么奇特的方法…

  1. 将要压扁的提交放在一个工作分支上(如果它们还没有)-使用gitk
  2. 检查目标分支(例如'master')
  3. git merge --squash (working branch name)
  4. git commit

提交消息将根据壁球进行预填充。

基于这篇文章,我发现这种方法对我的用户来说更容易。

我的“dev”分支在“原始/dev”之前有96次提交(所以这些提交还没有被推送到远程)。

我想在推送更改之前将这些提交压缩成一个。我更喜欢将分支重置为“原始/开发”状态(这将使96次提交的所有更改未分阶段),然后立即提交更改:

git reset origin/devgit add --allgit commit -m 'my commit message'

我发现一个更通用的解决方案是不指定“N”提交,而是指定你想要压扁的分支/提交ID。这比将提交数计算到特定提交不太容易出错——直接指定标签,或者如果你真的想计数,你可以指定HEAD~N。

在我的工作流程中,我启动了一个分支,我在该分支上的第一次提交总结了目标(即,它通常是我将作为该功能的“最终”消息推送到公共存储库的内容。

我使用别名:

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

这将在被压缩之前转储历史记录-如果您想恢复,您可以通过从控制台上抓取旧的提交ID来恢复。(Solaris用户注意到它使用GNU ses-i选项,Mac和Linux用户应该对此没问题。)

如果你是在一个从Golden存储库(golden_repo_name)克隆的远程分支(称为feature-branch)上,那么这里有一种技术可以将你的提交压缩成一个:

  1. 查看黄金仓库

    git checkout golden_repo_name
  2. Create a new branch from it(golden repo) as follows

    git checkout -b dev-branch
  3. Squash merge with your local branch that you have already

    git merge --squash feature-branch
  4. Commit your changes (this will be the only commit that goes in dev-branch)

    git commit -m "My feature complete"
  5. Push the branch to your local repository

    git push origin dev-branch

如果您使用TortoiseGit,您可以使用函数Combine to one commit

  1. 打开TortoiseGit上下文菜单
  2. 选择Show Log
  3. 在日志视图中标记相关提交
  4. 从上下文菜单中选择Combine to one commit

合并提交

此函数自动执行所有必要的单个git步骤。仅适用于Windows

如果您使用的是GitUp,请选择要与其父级合并的提交并按S。您必须为每个提交执行一次,但这比提出正确的命令行咒语要简单得多。特别是如果它是您偶尔只做一次的事情。

感谢这个方便的博客文章,我发现您可以使用此命令来压缩最后3次提交:

git rebase -i HEAD~3

这很方便,因为即使您在没有跟踪信息/远程存储库的本地分支上,它也可以工作。

该命令将打开交互式rebase编辑器,然后允许您按照正常方式重新排序、壁球、重新编辑等。


使用交互式rebase编辑器:

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

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

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

我更喜欢使用命令fixup,因为它将提交的更改“压扁”到上面一行的提交中,并丢弃提交的消息。

由于第1行的提交是HEAD,在大多数情况下,您会将其保留为pick。您不能使用squashfixup,因为没有其他提交来压缩提交。

您还可以更改提交的顺序。这允许您压扁或修复按时间顺序不相邻的提交。

交互式rebase编辑器


一个实际的日常例子

我最近提交了一个新功能。从那时起,我提交了两个bug修复。但是现在我在提交的新功能中发现了一个bug(或者可能只是拼写错误)。多么烦人!我不想新的提交污染我的提交历史!

我做的第一件事是修复错误并使用注释squash this into my new feature!进行新的提交。

然后我运行git loggitk并获取新功能的提交SHA(在本例中为1ff9460)。

接下来,我使用git rebase -i 1ff9460~打开交互式rebase编辑器。提交SHA后的~告诉编辑器在编辑器中包含该提交。

接下来,我将包含修复(fe7f1e0)的提交移动到功能提交的下方,并将pick更改为fixup

关闭编辑器时,修复将被压扁到功能提交中,我的提交历史将看起来漂亮干净!

当所有提交都在本地时,这工作得很好,但是如果您尝试更改已推送到远程的任何提交,您可能会给已签出同一分支的其他开发人员带来问题!

在此处输入图片描述

如果您想将提交压缩为单个提交(例如,第一次公开发布项目时),请尝试:

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

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

 git rebase -i HEAD~n

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

pick 01d1124 Message....pick 6340aaa Message....pick ebfd367 Message....pick 30e0ccb Message....

像下面这样更新pick一个提交和squash其他到最近的,

p 01d1124 Message....s 6340aaa Message....s ebfd367 Message....s 30e0ccb Message....

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

只需将此bash函数添加到您的bash of. z的文件中。

# 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}" ]; thenecho "Usage: \`squash X COMMIT_MSG\`"echo "X= Number of last commits."echo "COMMIT_MSG= New commit msg."return 1fi
git reset --soft HEAD~"$1"git add . && git ci -m "$2" # With 100 emojigit push --force}

那就跑

squash X 'New Commit Message'

你完蛋了。

"最后"的含义可能含糊不清。

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

* commit H0|* merge|\| * commit B0| || * commit B1| |* | commit H1| |* | commit H2|/|

然后最后一次提交是H0、合并、B0。要压扁它们,您必须将合并的分支重新建立在提交H1上。

问题是H0包含H1和H2(通常在合并前和分支后提交更多),而B0没有。所以你必须至少管理来自H0、合并、H1、H2、B0的更改。

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

rebase -i HEAD~2

这将显示您的选择选项(如其他答案中提到的):

pick B1pick B0pick H0

把壁球而不是选择到H0:

pick B1pick B0s H0

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

rebase完成后,您可以选择压扁H0和B0的消息:

* commit squashed H0 and B0|* commit B1|* commit H1|* commit H2|

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

git reset --mixed hash_of_commit_B0git add .git commit -m 'some commit message'

然后你压缩到H0、H1、H2的B0更改(在分支之后和合并之前完全丢失更改的提交。

什么可以真正方便:
找到你想要压扁的提交哈希,比如d43e15

现在使用

git reset d43e15git commit -am 'new commit name'

程序1

1)识别提交短哈希

# git log --pretty=oneline --abbrev-commitabcd1234 Update to Fix for issue Bcdababcd Fix issue Bdeab3412 Fix issue A....

这里甚至git log --oneline也可以用来获取短哈希。

2)如果你想压扁(合并)最后两个提交

# git rebase -i deab3412

3)这打开了一个用于合并的nano编辑器。如下所示

....pick cdababcd Fix issue Bpick abcd1234 Update to Fix for issue B....

4)将abcd1234之前的单词pick重命名为squash。重命名后应该如下所示。

....pick cdababcd Fix issue Bsquash abcd1234 Update to Fix for issue B....

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

6)然后nano编辑器再次打开以更新注释,如果需要更新它。

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

# git log --pretty=oneline --abbrev-commit1122abcd Fix issue Bdeab3412 Fix issue A....

8)现在推送到repo。注意在分支名称之前添加+符号。这意味着强制推送。

# git push origin +master

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

程序2

  1. 首先添加提交所需的文件
git add <files>
  1. 然后使用--fixup选项提交,OLDCOMMIT应该是我们需要合并(壁球)这个提交的地方。
git commit --fixup=OLDCOMMIT

现在,这将在HEAD之上创建一个新的提交fixup1 <OLDCOMMIT_MSG>

  1. 然后执行下面的命令将新提交合并(Squash)到OLDCOMMIT
git rebase --interactive --autosquash OLDCOMMIT^

这里^表示上一次提交OLDCOMMIT。这个rebase命令在编辑器(vim或nano)上打开交互式窗口我们不需要做任何事情,只需保存和退出就足够了。因为传递给this的选项会自动移动最新的提交到旧提交旁边并将操作更改为fixup(相当于壁球)。然后rebase继续并完成。

程序3

  1. 如果需要对最后一次提交添加新更改,意味着--amend可以与git-commit一起使用。
    # git log --pretty=oneline --abbrev-commitcdababcd Fix issue Bdeab3412 Fix issue A....# git add <files> # New changes# git commit --amend# git log --pretty=oneline --abbrev-commit1d4ab2e1 Fix issue Bdeab3412 Fix issue A....

这里--amend合并了对上次提交cdababcd的新更改并生成新的提交ID1d4ab2e1

结论

  • 第一个过程的优点是压缩多个提交并重新排序。但是如果我们需要将修复合并到非常旧的提交,这个过程会很困难。
  • 因此,第二个过程有助于将提交合并为非常旧的提交。
  • 第三个过程在将新更改压扁到最后提交的情况下很有用。
git rebase -i HEAD^^

其中^的个数是X

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

除了其他优秀的答案,我想补充一下git rebase -i总是让我对提交顺序感到困惑-从旧到新,反之亦然?所以这是我的工作流程:

  1. git rebase -i HEAD~[N],其中N是我想要加入的提交数,从最近的一个开始。所以git rebase -i HEAD~5意味着“将最后5个提交压缩为一个新提交”;
  2. 编辑器弹出,显示我要合并的提交列表。现在它们显示在顺序相反中:旧的提交在顶部。将除了第一个/旧的中的所有提交标记为“壁球”或“s”:它将用作起点。保存并关闭编辑器;
  3. 编辑器再次弹出,并显示新提交的默认消息:将其更改为您的需要,保存并关闭。壁球完成!

来源和附加阅读:#2#2

我认为最简单的方法是在master的基础上创建一个新的分支,并执行一个merge --squash的功能分支。

git checkout mastergit checkout -b feature_branch_squashedgit merge --squash feature_branch

然后,您已准备好提交所有更改。

如何回答与这样的工作流相关的问题?

  1. 许多本地提交,与多个合并从主混合
  2. 最后推到远程,
  3. PR并合并TO master由审稿人。(是的,在PR之后,开发人员更容易merge --squash,但团队认为这会减慢过程。)

我还没有在这个页面上看到这样的工作流程。(那可能是我的眼睛。)如果我正确理解rebase,多次合并需要多种冲突解决。我甚至不想考虑这个!

这似乎对我们有用。

  1. git pull master
  2. git checkout -b new-branch
  3. git checkout -b new-branch-temp
  4. 本地大量编辑提交,定期合并master
  5. git checkout new-branch
  6. git merge --squash new-branch-temp//将所有更改放入阶段
  7. git commit 'one message to rule them all'
  8. git push
  9. 审阅者做PR并合并到master。

异常答案很好,但我对此感到不安全,所以我决定添加几个截图。

步骤0:git日志

看看你对git log的看法。最重要的是,找到你不要想要压扁的第一次提交的提交哈希。所以只有:

在此处输入图片描述

步骤1:git rebase

执行#0,在我的情况下:

$ git rebase -i 2d23ea524936e612fae1ac63c95b705db44d937d

第2步:选择/压扁你想要的东西

在我的情况下,我想压缩第一次提交的所有内容。顺序是从头到尾,所以与git log完全相反。在我的情况下,我想要:

在此处输入图片描述

第3步:调整消息

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

在此处输入图片描述

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

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

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

例子:

git rebase -i HEAD~2

这将打开文本编辑器,如果您希望将这些提交合并在一起,您必须将每个提交前面的“选择”切换为“壁球”。从留档:

p,选择=使用提交

s, squash=使用提交,但融合到之前的提交中

例如,如果您希望将所有提交合并为一个,“选择”是您所做的第一次提交,所有未来的提交(位于第一次提交下方)都应设置为“壁球”。如果使用vim,请在插入模式下使用: x保存并退出编辑器。

然后继续rebase:

git add .
git rebase --continue

有关此以及其他重写提交历史记录的方法的更多信息,请参阅这个有用的帖子

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

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

如果您还想使用压缩提交更新远程分支:

git push -f

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

git checkout mastergit rev-list master.. --count

然后,我创建了另一个分支基于我的功能分支,保持my-feature分支不变。

最后,我跑

git checkout my-featuregit checkout -b my-rebased-featuregit checkout mastergit checkout my-rebased-featuregit rebase mastergit rebase head^x -i// fixup/pick/rewritegit 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

希望有帮助,谢谢。

⚠️警告:“我的最后一个X提交”可能是模棱两可的。

  (MASTER)Fleetwood Mac            Fritz║                    ║Add Danny  Lindsey     StevieKirwan  Buckingham    Nicks║         ╚═══╦══════╝Add Christine       ║Perfect      Buckingham║           NicksLA1974══════════╝║║Bill <══════ YOU ARE EDITING HEREClinton        (CHECKED OUT, CURRENT WORKING DIRECTORY)

https://github.com/fleetwood-mac/band-history存储库的这个非常简短的历史记录中,您打开了一个拉取请求,以将比尔克林顿提交合并到原始(MASTER)Fleetwood Mac提交中。

你打开了一个拉取请求,在GitHub上你会看到:

四个提交:

  • 加上丹尼·柯万
  • 添加克里斯汀完美
  • LA1974
  • 比尔克林顿

认为没有人会愿意阅读完整的存储库历史记录。(实际上有一个存储库,点击上面的链接!)你决定挤压这些提交。所以你去运行git reset --soft HEAD~4 && git commit。然后你git push --force到GitHub上清理你的PR。

然后发生了什么?你刚刚从弗里茨到比尔克林顿进行了一次提交。因为你忘记了昨天你正在处理这个项目的白金汉尼克版本。git log与你在GitHub上看到的不匹配。

🐻故事的寓意

  1. 找到您想要获取的确切文件,并git checkout它们
  2. 找到你想在历史中保留的确切的先前提交,并git reset --soft
  3. 制作一个git commit,直接从弯曲到

简单的一行代码始终有效,假设您当前位于要压扁的分支上,master是它起源的分支,并且最新的提交包含您希望使用的提交消息和作者:

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

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

在此处输入图片描述

例如,如果您想将最后3次提交压缩到分支(远程存储库)中的单个提交,例如:https://bitbucket.org

我所做的是

  1. git reset --soft HEAD~3
  2. git commit
  3. git push origin <branch_name> --force

如果您不关心中间提交的提交消息,您可以使用

git reset --mixed <commit-hash-into-which-you-want-to-squash>git commit -a --amend

为了避免在重新基于同一分支中的提交时必须解析任何合并冲突,您可以使用以下命令

git rebase -i <last commit id before your changes start> -s recursive -X ours

要将所有提交压缩为一个,当系统提示您编辑要合并的提交(-i标志)时,请按照其他答案中的建议,将除第一个操作之外的所有操作从pick更新为squash

在这里,我们使用合并策略(-s标志)recursive和策略选项(-X)ours来确保历史中稍后的提交赢得任何合并冲突。

:不要将此与git rebase -s ours混淆,后者做其他事情。

参考:git rebase递归合并策略

2020简单没有rebase的解决方案:

git reset --soft HEAD~2git commit -m "new commit message"git push -f

2表示最后两个提交将被压缩。您可以用任何数字替换它

如何使用Git将我的最后一个X提交压缩到一个提交中?

git rebase -i HEAD~X

将显示以下内容:

pick 1bffc15c My earlier commitpick 474bf0c2 My recent commit
# ...

对于要压缩的提交,将挑选替换为修复,因此它变成:

pick 1bffc15c My earlier commitfixup 474bf0c2 My recent commit
# ...

如果它在vim中打开(终端内的默认界面),然后按键盘上的Esc,键入:wqEnter保存文件。

验证:检查git log

尝试了这里提到的所有方法。但最终我的问题通过以下链接解决了。https://gist.github.com/longtimeago/f7055aa4c3bba8a62197

$ git fetch upstream$ git checkout omgpull$ git rebase -i upstream/master
< choose squash for all of your commits, except the first one >< Edit the commit message to make sense, and describe all your changes >
$ git push origin omgpull -f

许多答案都基于git rebase命令,但根据我的经验,对于git初学者来说,它有点复杂和高级。

假设你想压缩最后3次提交。然后是以下步骤:

  • 记下当前提交id:使用git log -1 --oneline并记下当前状态的提交ID(以防你在git重置中做错了什么)
  • 返回3次提交:使用git reset --soft HEAD~3,您将返回3次提交(并且有点忘记您之前已经进行了这三次提交)
  • 做一个新的提交:现在只需执行git commit -m <NEW_SINGLE_MESSAGE>,它将自动合并消息下的三个提交

如果git重置出现问题,您可以通过git reset --soft <ORIGINAL_COMMIT>再次返回原始状态

这是执行后会发生什么的另一个可视化示例:git rebase -i HEAD~3

这里有一个可视化的例子,说明在执行最后三次提交的git rebase后会发生什么

来源:https://www.git-tower.com/learn/git/faq/git-squash/

简单的解决方案:

git reset --soft HEAD~5

git commit -m "commit message"

git push origin branch --force-with-lease

有没有人提到在IntelliJ IDEA UI上做这件事有多容易:

  • 转到git窗口
  • 手动选择要合并为一个的所有提交。
  • 右键单击>Squash Commits>编辑压扁的提交消息
  • 点击左侧分支名称>右键单击>推送>Force Push

在此处输入图片描述

方法1如果你有很多提交

git rebase -i master然后按键盘“i”进行编辑

你会看到这样的:

pick etc1pick etc2pick etc2

将单词选择替换为'f'并按esc y :wq

pick etc1 //this commit will the one commitf etc2f etc2

然后按下这个命令

git push origin +head

方法2,如果提交很少你可以这样做来删除一个提交,你需要做同样的删除你的第二次提交等等

git reset --soft HEAD^1 // or git reset --soft head~1git commit --amend //then press `:wq`git push -f

方法3如果你已经有一个提交并且不想再提交另一个提交

git add files...git commit --amend  //then press `:wq`git push origin +head

最简单的方法是使用GitHub Desktop。只需选择历史记录中的所有提交,右键单击并选择“Squash x提交”:

GitHub桌面壁球

我们对这个答案非常满意,这意味着你不需要通过提交来计数。

如果您在master分支上有大量提交并希望将它们全部合并为一个:

  • 确保您在master分支上并执行git状态以确保它全部干净
  • 在开始工作之前为提交复制提交SHA
  • git复位-软提交

[您会注意到此时您的所有更改都将显示在IDE的源代码控制部分-很好地查看它们以检查它是否全部存在]

  • git提交-am"您想要的消息将替换所有其他提交消息"
  • git推送(会抱怨你在提交上落后)所以强制推送

就是它了!