如何使用git将最后的x提交压缩为一个提交?
- 类似问题:stackoverflow.com/questions/7275508/…
- 相关:git-在推送之前合并多个提交。
- @马特·托托是你的工具。它提供了一个"合并到一个提交"的单一功能,将在后台自动调用所有步骤。很遗憾,只有Windows可用。请看下面我的答案。
- @托马斯:请解释当必须对提交的代码执行评审时,如何"很好地使用Git",我们需要根据评审意见进行更新并再次提交,但不想推动基于评审的客户流失?
- 要压缩到第一次提交,请参见-stackoverflow.com/questions/1657017/…
- 压缩后需要强制执行push stackoverflow.com/questions/10298291/…
- 请使用git rebase -i。
- 除了发布的答案之外,GUI客户机还可以很容易地做到这一点。我只需点击四下就可以在Gitkraken中压缩数十个提交。
如果没有git rebase或git 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 --squash-recent,甚至是git commit --amend-many。
- 进行软重置时,自动确定目标提交ID(即最后一次推送提交的ID)的最佳方法是什么?例如,我不想查找和计数3。
- @A-B-B:如果您的分支机构设置了"上游",那么您可以使用branch@{upstream}(或仅使用@{upstream}作为当前分支机构;在这两种情况下,最后一部分可以缩写为@{u};见gitrevisions)。这可能不同于您的"最后一次推送提交"(例如,如果其他人推了构建在您最近一次推送之上的某个东西,然后您获取了它),但看起来它可能接近您想要的。
- 我一般喜欢这个解决方案,但喜欢提供我自己的提交消息。下面是一个设置别名的解决方案,以便您可以执行git squash 3"我的提交消息"
- 这一次需要我去埃多克斯,但除此之外,它很可爱,谢谢。
- 这里有一个别名,以防你想修复而不是挤压:fixup ="!f() { base=${1:-2}; git reset --soft HEAD~$base && git commit -C ORIG_HEAD~$(expr $base - 1); }; f"。
- git log --one-line或类似的东西将显示提交ID和消息的列表,每行一个。这个解决方案非常有用。
- 当我连续运行第二个命令时,我在编辑器中有大量的提交历史,而不是最后三个。
- @2rs2ts git push-f听起来很危险。注意只压扁本地提交。千万不要碰推的承诺!
- 是否可以将以前的提交与当前未提交的更改合并?
- 如果其中一个提交将父变更拉回到分支中,会怎么样?
- 如何清理和粉碎你历史中间的承诺?例如,我想摆脱那些烦人的"合并提交"。还是我应该离开他们?
- 在尝试推送获取错误时执行此操作之后,错误:未能将某些引用推送到"[email protected]:…"。提示:由于当前分支的提示落后,更新被拒绝。如何解决这个问题?
- 找到了答案stackoverflow.com/questions/5667884/…
- 之后我还需要使用git push --force,这样就需要
- 确保您的工作副本没有其他更改(git stash更改或其他更改)。否则,您也有机会将它们提交到压扁的提交中,这可能不是您打算做的事情。
- @克里斯约翰森,你应该加上扎克·索西尔的话,这对我也是真的!
- 我想投反对票,但我不能让自己成为改变1337的反对票计数的人。
- 当以东十一〔12〕给我以东十一〔13〕的时候,这帮助了我。可能是因为我试图压扁的其中一个承诺是合并已解决的冲突。
- 感谢您提供的解决方案不需要为每个提交手动键入squash/fixup,这与其他地方的所有交互式重新平衡建议不同。
- Git重置——软头~3在要解决的冲突数量方面肯定更好。我们的测试用例是head~6,但其中一个提交是合并提交,所以我们有45个提交,而rebase-i head~6给了我们很多冲突,最终导致构建失败。当我们使用reset--soft时,它给我们0个冲突来解决。
- git push -f似乎没有更改upsteam存储库的提交日志。
- @Chrisjohnsen如何保存更改,在提交消息后键入?
- 我把它作为脚本放到~/bin/git-funge中,因为对于pull请求我们必须有单个commit消息。现在,我只需编写git funge HEAD~3并编辑我的消息,以设置一条pr消息并保存,所有的消息都会排序。:)谢谢你的回答。(冯是我的意思可替换的方式,但作为动词读起来更好:p)
- 我认为,比那些说"强制推动"只是创建一个新的特性分支要好。所以像git checkout -b feature-name-squashed这样的东西,遵循这些指示,然后推动分支的新压缩版本。如果您这样做,您将有权访问功能的完整提交日志(在功能名上),如果您需要的话。
- 重写历史后,必须使用git push -f始终是一项要求。重写历史可能是不好的,当然,如果您是在共享存储库中这样做的话。但这是一个独立的主题,与这个答案无关(这很好)。
- 如果您已经推送了要挤压的提交,并且希望避免执行强制推送,请在提交时创建一个新分支并推送它:git checkout-b new branch name&git push-u
- 我认为这是目前最好的方法。但请注意我们的警告:如果您有未过期或未提交的更改,Git不会阻止您执行这些操作,并且您未提交的更改将合并到压缩的提交中。这可能是你想要的,也可能不是你想要的。
- 这种方法与"真正的"REBASE之间的一个显著区别是,原始提交的作者在这里丢失了。
- 不确定添加了什么版本,但是您可以使用这个一行程序可靠地重用最新的提交消息和作者:git reset --soft {base-commit} && git commit --reuse=HEAD@{1}。
- git reset --soft岩石。
- 试着习惯于git push --force-with-lease,而不是git push -f!
- 如果你的其他承诺已经被推到远程,也就是说,git push origin +name-of-branch,不要忘记+。
- @阿德里安拉特纳帕拉,我还没有试过,但伊桑在下面补充了一个答案,基于这个答案,用.gitconfig做一些类似的事情。
- 我不小心做了两次。请注意它们堆叠在一起。
- 这个解决方案帮助了我很多,谢谢!
- 对于那些对HEAD..HEAD@{1}好奇的人,r1..r2规定了一系列可以从r2获得的承诺,不包括从r1获得的承诺,@{}规定了该参考的第n个优先值。因此,HEAD..HEAD@{1}指当前HEAD和HEAD之间的所有承诺。就在重置之前。
- 你知道吗,如果我决定让git rebase -i HEAD~3有时它会选择前10+而不是前3个?
如手册所述,使用git rebase -i 并在第二次和后续提交时用"挤压"或"修复"替换"拾取"。
在这个例子中,要么是sha1散列,要么是当前分支头部的相对位置,从中分析REBASE命令的提交。例如,如果用户希望在过去查看来自当前头部的5个提交,则命令是git rebase -i HEAD~5。
- 我想,这个问题回答得更好stackoverflow.com/a/5201642/295797
- 是什么意思?
- 是commit x+1,即您要压缩的最旧commit的父级。
- 但如果我已经推动了这些承诺?
- 如果已经推送了提交,则需要推送-f以强制将远程分支移动到新提交。不过,这会让任何在旧承诺之上工作的人感到不安。
- 我肯定更喜欢reset --soft,其次是commit,而不是再平衡…但不管怎样,因为它起作用了
- 如果以前的提交是一个补丁(例如git add -p补丁),而您不想再次进行交互式补丁选择,那么rebase非常有用。
- 我个人发现这篇文章(git scm.com/book/en/v2/git工具重写历史)比链接的(同一个站点)更容易理解。
- 我觉得这个答复太短了,不太明白。举个例子会有所帮助。
- 这个rebase -i方法和reset --soft方法的区别是,rebase -i允许我保留提交者,而reset --soft允许我重新提交。有时,我需要压扁请求提交,同时维护作者信息。有时我需要重新设置软自己的承诺。不管怎样,对两个伟大的答案都持赞成态度。
- 我总是会因为某些原因而把钢筋弄乱,重置——对于大多数情况来说,软钢筋似乎更直观。
- 埃多克斯1〔9〕嗯…WUT?
- 有没有一种方法可以在不使用交互模式的情况下使用git-rebase进行压缩?
- @Cheeso,本文有助于解释交互式钢筋网中使用的squash和fixup名称。具体来说,squash和fixup的名称告诉Git如何组合提交消息。
- 对的解释是不必要的复杂。如果有一个更简单的解释(所选的词几乎是无需解释的,但给出的解释过于复杂),这个答案将是完美的。
- 应该是。
- 我觉得你的意思是"江户一号"(16),但你把这种说法描述为过去的承诺。
- 下面是一个实际的例子:feed.cloud.geek.nz/posts/combining multiple commits into-‌&8203;one
- 这是一个令人困惑的答案。
- 错误:如果没有以前的提交,则无法"挤压"
- 谁是皮克?我不明白这个答案:|
- 挤压后你该怎么做才能推它?
- @谢谢,这个链接提供了唯一有意义的描述。现在我甚至可以告诉人们,正是这个意思。它是非包容性的,并且允许您在提交之后选择和挤压提交,只要它们都不是初始提交。如果要包括初始提交,则需要使用git reset方法。
- 我以超然的头脑结束,那我该怎么办?
- 请注意,在交互重新平衡时,您可以重新排序提交-例如,如果您进行了提交A、B、然后C,则可以通过在提示时交换B和C行的顺序将提交C挤压到A(并将C更改为"挤压")。如果您在尝试挤压提交之后提交了更多的工作,则非常有用。
- 埃多克斯1〔19〕
你可以用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 rebase -i更"优雅",但你没有给出原因。试探性地说,这是因为在我看来,事实恰恰相反,这是一种黑客行为;难道你不只是为了强迫git merge做git rebase专门设计的一件事而执行更多的命令吗?
- @马克·阿米里:我说这更优雅的原因有很多。例如,它不涉及不必要地生成一个编辑器,然后在"to-do"文件中搜索和替换字符串。在脚本中使用git merge --squash也更容易使用。从本质上讲,理由是您根本不需要git rebase -i的"交互性"。
- 尽管我很欣赏为这样的重大更改提供详细的提交消息的优势,但与Chris相比,这种方法还有一个真正的缺点:执行硬重置(git reset --hard会接触更多的文件。例如,如果你使用的是Unity3D,你会喜欢少接触一些文件。
- 另一个优势是,与重新设定相比,git merge --squash在面临移动/删除/重命名时不太可能产生合并冲突,特别是当您从本地分支合并时。(免责声明:仅基于一个经验,如果在一般情况下这不是真的,请更正我!)
- 我总是很不情愿的,当它涉及到硬重置-我会使用一个时间标签而不是HEAD@{1}只是为了安全起见,例如当你的工作流程被断电中断一个小时等。
- @鲍尔德雷珀,好吧,比方说,由于停电或其他中断,你不能完成这项工作,下次你在那台机器上,出于习惯,你做了一个git pull,获得了一些承诺,但不是你想要压扁的承诺。我不知道是否也有一个HEAD@{2}等,但有一个临时标签,你不必记住…当然,只要你不动,你还能找回你的头,但这只是更乏味的事。
- @Tobiaskienzler,你可能对git reflog感兴趣。为此,Git已经"标记"了最近的提交。git gc --prune不应该改变这一点,除非你要求终止reflog条目。这可能都是一个偏好问题。
- @Pauldraper感谢您提供的信息,是的,这可能是首选项——而且python的"显式优于隐式"
- 精彩的。。这毁了我的承诺。- 1
- @摧毁你的承诺?(我不知道你说的是什么意思。你承诺的任何事情都可以很容易地从Git的回流中恢复过来。如果您有未提交的工作,但文件是分段的,那么您仍然可以取回它们的内容,尽管这将是更多的工作。但是,如果你的工作甚至没有进行阶段性的工作,那么恐怕就没有什么可以做的了;这就是为什么前面的回答是这样的:"首先检查一下Git状态是否干净(因为Git重置——硬的将丢弃阶段性和未经调整的更改)"。
- 也许过时了?我做了重设——很难,当进行合并——挤压时,我得到了这个错误"致命的:你不能组合——挤压和——没有FF"。
- 仅供参考:如果您执行git merge --squash HEAD@{1}并返回error: unknown switch 'c,则可能正在PowerShell控制台中运行。可能最简单的方法是通过cmd暂时移动到普通命令shell,然后执行git merge --squash HEAD@{1},然后通过exit离开命令shell返回PowerShell。(我没有费心去弄清楚如何通过PowerShell运行git merge --squash HEAD@{1}。
我建议尽可能避免使用git reset,尤其是对于git新手。除非您真的需要基于许多提交实现流程的自动化,否则还有一种不那么奇特的方法…
将要挤压的提交放到一个工作的分支上(如果它们还没有),为此使用gitk
查看目标分支(例如"master")。
埃多克斯1〔19〕
埃多克斯1〔14〕
将根据挤压预填充提交消息。
- 这是最安全的方法:不重置软/硬(!!)或使用了reflog!
- 如果你扩展到(1),那就太好了。
- @亚当:基本上,这意味着使用gitk的GUI接口来标记您要压缩的代码行,并标记要压缩到的基础。在正常情况下,这两个标签都已经存在,因此可以跳过步骤(1)。
- 请注意,此方法不会将工作分支标记为完全合并,因此删除它需要强制删除。:(
- 为了(1),我找到了最方便的方法。然而,它确实再次涉及git重置,这个答案试图避免。
- @Kyrstellaine:是的,合并不会影响工作分支——因此,如果您希望删除它,则必须手动删除它(可选步骤5)。
根据克里斯·约翰森的回答,
从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" |
。
用途:
…它会自动压缩最后一个N承诺,包括。
注意:结果提交消息按顺序是所有压缩提交的组合。如果您对此不满意,您可以随时使用cx1(17)手动修改它。(或者,编辑别名以符合您的口味。)
- 有趣的是,但是我宁愿自己键入压扁的提交消息,作为我多次提交的描述性摘要,也不愿让它自动为我输入。因此,我宁愿指定git squash -m"New summary.",并将N自动确定为未取消的提交数。
- @A-B-B,这听起来像是一个单独的问题。(我不认为这正是OP所要求的;在我的Git-Squash工作流中,我从来没有感觉到需要它。)
- 这很甜。就我个人而言,我希望有一个版本使用压缩在一起提交的第一个提交消息。对于像空格这样的调整是有好处的。
- @Funroll同意了。删除最后一个commit msg对我来说是非常常见的需求。我们应该能够设计出…
- @这附近的史蒂文克莱:埃多克斯1〔23〕。
- 你可能想数到树枝上,或者用类似于git rev-list @{1}..head --count的东西承诺。
- 我希望下面的方法能奏效,但不完全有效:f(){c=$(git rev-list ${1}..head --count); git reset --soft HEAD~$c && git commit --edit -m"$(git log --format=%B --reverse ${1}..head)"; }。
- 是否有一种方法可以消除该分支的所有提交,而不是最后一个x提交?
- @穆罕默德马哈拉维这是什么意思?所有人都承诺X分支,因为它偏离了Y分支?您仍然需要输入分支Y的名称。对于我来说,只需计算提交的数量就更简单了。
- @是的,与分支祖先合并(在初始分支时)是非常有用的。问题是我们不能依赖祖先的名字,因为它可能会被改变。现在最好的方法是查看git日志,计算并使用您的奇妙的squash别名。
- @A-B-B您可以使用git commit --amend来进一步更改消息,但是这个别名让您可以很好地了解提交消息中应该包含的内容。
- @非常感谢(无意中)提醒人们可能不熟悉--amend。我已经更新了答案以提供提示。
- 作为一个git n00b,这绝对是一个勾选框。
多亏了这篇便利的博客文章,我发现您可以使用此命令压缩最后3次提交:
。
这很方便,因为即使您在没有跟踪信息/远程回购的本地分支上,它也能工作。
该命令将打开交互式钢筋网编辑器,然后允许您根据正常情况重新排序、挤压、返工等。
使用交互式钢筋网编辑器:
交互式钢筋网编辑器显示最后三次提交。这个约束是由HEAD~3在运行命令git rebase -i HEAD~3时确定的。
最新的提交,HEAD首先显示在第1行。以#开头的行是注释/文档。
显示的文档非常清楚。在任何给定的行上,您都可以将命令从pick更改为您选择的命令。
我更喜欢使用命令fixup,因为这样会"压扁"上面一行中commit对commit的更改,并丢弃commit的消息。
因为第1行的commit是HEAD,所以在大多数情况下,您会将其保留为pick。您不能使用squash或fixup,因为没有其他承诺可以将该承诺压扁。
氧化镁
- 你要挑最上面的一个然后把剩下的压扁吗?您应该编辑您的答案,以更详细地解释如何使用交互式钢筋编辑器。
- 是的,把pick留在1号线。如果您在第1行选择squash或fixup进行提交,Git将显示一条消息:"错误:没有以前的提交就不能‘修复’"。然后它会给你修复它的选项:"你可以用‘git-rebase--edit todo’修复这个问题,然后运行‘git-rebase--continue’",或者你只需中止并重新开始:"或者你可以用‘git-rebase--abort’中止这个基码。"
如果使用TortoisGit,则可以使用函数Combine to one commit:
打开TortoisGit上下文菜单
选择Show Log。
在日志视图中标记相关提交
从上下文菜单中选择Combine to one commit。
。
此函数自动执行所有必需的单个git步骤。不幸的是,只能用于Windows。
- 据我所知,这对合并提交不起作用。
- 尽管它没有被任何其他人评论过,但这甚至适用于那些不在头脑中的承诺。例如,我需要压缩一些WIP提交,在推送之前我做了一个更理智的描述。工作得很漂亮。当然,我仍然希望我能学会如何通过命令来完成它。
- 这个函数执行什么Git代码?
基于本文,我发现这个方法对于我的用例来说更容易。
我的"dev"分支在96次提交之前领先于"origin/dev"(因此这些提交尚未推送到远程)。
我想在推动变革之前把这些承诺压缩成一个。我宁愿将分支重置为"origin/dev"状态(这将使96提交中的所有更改保持未过时),然后立即提交更改:
1 2 3
| git reset origin/dev
git add --all
git commit -m 'my commit message' |
。
- 只是我需要的。压扁我的特性分支中的提交,然后我将cherry-pick提交给我的主人。
- 这不会压扁以前的提交!
- 你能在igorganapolsky详细描述一下吗?
- @Trudolf这不是真正的压扁(选择个人承诺压扁)。这更像是一次提交所有更改。
- 是的,因此它把你所有的承诺都压缩成一个。祝贺你!
- 出于某种原因,这将覆盖在主服务器上的现有更改,这些更改在我从主服务器上分支以创建功能分支之后被推到主服务器上。但不知道为什么。正如链接文章中提到的,我将尝试用另一种方式来做。
- 作为我评论的延续。当我创建一个合并分支,然后将特征分支合并到其中时,压扁就可以了。它需要更多的工作,但仍然比樱桃采摘所有手工。
为此,可以使用以下git命令。
。
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。如果您使用的是不同的操作系统(Windows或Mac),那么上面的命令除了编辑器外是相同的。你可能会得到不同的编辑。
- 它只更新最后两次提交,即使我将提交ID重置为第6次提交,也不知道为什么
- 甚至可以重新排列提交顺序。它工作得很好。
失范的答案是好的,但我对此感到不安全,所以我决定添加一些截图。
步骤0:git日志
看看你和git log在哪里。最重要的是,找到不想挤压的第一个提交的提交哈希。所以只有:
。
第一步:Git Rebase
执行git rebase -i [your hash],在我的情况下:
1
| $ git rebase -i 2d23ea524936e612fae1ac63c95b705db44d937d |
第二步:挑选/挤压你想要的
在我的例子中,我想把第一次提交的所有内容都压缩掉。顺序从头到尾,与git log中的顺序完全相反。就我而言,我想:
氧化镁
步骤3:调整消息
如果只选择了一个提交并压扁了其余的提交,则可以调整一个提交消息:
氧化镁
就这样。一旦你保存了这个(:wq),你就完成了。用git log看一下。
- 很高兴看到最后的结果,如git log。
- 不,谢谢。这正是我失去承诺的原因。
- @Axalix你把所有的线都去掉了吗?这就是你失去承诺的原因。
如果您在一个从黄金存储库(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 |
。
- 因为我刚刚压缩了大约100个提交(用于通过git-svn同步svn分支),这比交互式重新平衡快得多!
- 往下看,我看到了@chris的评论,这是我过去经常做的(rebase——软的……)——太糟糕了,stackoverflow不再把数百个上票的答案放在顶部……
- 同意你@sage,希望他们将来能做这件事
- 这是正确的方法。REBASE方法很好,但只能用于壁球作为最后的解决方案。
在要合并提交的分支中,运行:
1
| git rebase -i HEAD~(n number of commits back to review) |
号
例子:
这将打开文本编辑器,如果希望将这些提交合并在一起,则必须在每次提交之前使用"挤压"切换"pick"。来自文档:
p,pick=使用提交s,squash=使用commit,但合并到以前的commit中
例如,如果要将所有提交合并为一个提交,则"pick"是您所做的第一个提交,并且所有将来的提交(放在第一个提交下面)都应设置为"squash"。如果使用VIM,请在插入模式下使用:x保存并退出编辑器。
然后继续重新设置钢筋:
。
有关重写提交历史记录的更多信息,请参阅这篇有用的文章。
- 请解释一下--continue和vim :x的作用。
- 当您使用git rebase --continue移动到下一个提交并开始合并文件中的正确配置后,当它通过分支上的提交时,将以块的形式发生重新平衡。:x是一个在使用vim时保存文件更改的命令。请参见
真正方便的是:找到你想要压扁的提交哈希表,比如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:这很有趣,也很有启发性,因为我一点也不清楚,你可以把比程序名更复杂的东西放在git_编辑器环境变量中。
如果您希望将每个提交挤到一个提交中(例如,第一次公开发布项目时),请尝试:
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" |
如果还希望使用压缩的提交更新远程分支:
。
简单的一个始终有效的行程序,假设您当前在要挤压的分支上,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。
- 这是100%最简单的方法。如果您当前的头部是您想要的正确状态,那么您可以跳过1。
对于与这样的工作流相关的问题的答案如何?
许多本地提交,混合了来自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用户应该可以接受。)
- 我试过这个别名,但我不确定sed替换是否有任何效果。他们应该怎么做?
- 第一个SED只是将历史数据转储到控制台。第二个SED将所有"pick"替换为"f"(fixup),并就地重写编辑器文件(i选项)。所以第二个完成了所有的工作。
- 您是对的,计算n个特定提交的数量非常容易出错。它把我搞砸了好几次,浪费了几个小时试图解开钢筋。
- 嗨,Ethan,我想知道此工作流是否会隐藏合并时可能发生的冲突。所以请考虑你是否有主从两个分支。如果奴隶与主人有冲突,我们在奴隶登记时使用git squash master。会发生什么?我们要隐藏冲突吗?
- @Sergio这是一个重写历史的案例,因此如果您压缩已经被推送的提交,然后尝试合并/重新设置压缩的版本,那么可能会发生冲突。(有些琐碎的案件可能会得逞。)
- @塞吉奥挤压可以隐藏一些冲突,如果你做的是重新平衡而不是合并。合并只在最后一次提交时发生,所以它相当于合并压缩的版本。但是,一个REBASE必须处理每个提交,所以您可能会有暂时的冲突,这些冲突将由以后的提交展开,并且在处理REBASE时,您将在每个冲突上得到一个冲突。重新设定压扁的版本将隐藏此中间历史记录。
- 是的,我和git push --force-with-lease解决了这个问题。我不必关心历史:)
有问题的是,"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:
保存并退出后,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更改(分支后和合并前完全丢失对更改的提交)。
号
其中^的数目是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选项,如下所示。提交消息将是合并请求的标题。
。
只需将这个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' |
号
你完成了。