繁体   English   中英

如何将所有 git 提交压缩为一个?

[英]How to squash all git commits into one?

您如何将整个存储库压缩到第一次提交?

我可以 rebase 到第一个提交,但这会让我有 2 个提交。 有没有办法在第一个提交之前引用提交?

git 1.6.2开始,您可以使用git rebase --root -i

对于除第一个以外的每个提交,将pick更改为squash

更新

我做了一个别名git squash-all
用法示例git squash-all "a brand new start"

[alias]
  squash-all = "!f(){ git reset $(git commit-tree HEAD^{tree} -m \"${1:-A new start}\");};f"

注意:可选消息用于提交消息,如果省略,它将默认为“A new start”。

或者您可以使用以下命令创建别名:

git config --global alias.squash-all '!f(){ git reset $(git commit-tree HEAD^{tree} -m "${1:-A new start}");};f'

一个班轮

git reset $(git commit-tree HEAD^{tree} -m "A new start")

这里,提交消息“ A new start ”只是一个示例,请随意使用您自己的语言。

TL;博士

无需 squash,使用git commit-tree创建一个孤儿提交并使用它。

解释

  1. 通过git commit-tree创建单个提交

    git commit-tree HEAD^{tree} -m "A new start"作用是:

根据提供的树对象创建一个新的提交对象,并在标准输出上发出新的提交对象 ID。 除非给出 -m 或 -F 选项,否则从标准输入读取日志消息。

表达式HEAD^{tree}表示与HEAD对应的树对象,即当前分支的尖端。 请参阅树对象提交对象

  1. 将当前分支重置为新的提交

然后git reset简单地将当前分支重置为新创建的提交对象。

这样,工作区中的任何内容都不会被触及,也不需要 rebase/squash,这使得它非常快。 所需时间与存储库大小或历史深度无关。

变体:来自项目模板的新仓库

这对于使用另一个存储库作为模板/原型/种子/骨架在新项目中创建“初始提交”很有用。 例如:

cd my-new-project
git init
git fetch --depth=1 -n https://github.com/toolbear/panda.git
git reset --hard $(git commit-tree FETCH_HEAD^{tree} -m "initial commit")

这避免了将模板存储库添加为远程( origin或其他)并将模板存储库的历史折叠到您的初始提交中。

如果您只想将所有提交压缩到根提交,那么当

git rebase --interactive --root

可以工作,对于大量提交(例如,数百次提交)是不切实际的,因为 rebase 操作可能会运行非常缓慢以生成交互式 rebase 编辑器提交列表,以及运行 rebase 本身。

当您压缩大量提交时,这里有两个更快、更有效的解决方案:

替代解决方案#1:孤儿分支

您可以简单地在当前分支的尖端(即最近的提交)处创建一个新的孤立分支。 这个孤立分支形成了一个全新且独立的提交历史树的初始根提交,这实际上等同于压缩所有提交:

git checkout --orphan new-master master
git commit -m "Enter commit message for your new initial commit"

# Overwrite the old master branch reference with the new one
git branch -M new-master master

文档:

替代解决方案#2:软复位

另一个有效的解决方案是简单地对根提交<root>使用混合或软重置:

git branch beforeReset

git reset --soft <root>
git commit --amend

# Verify that the new amended root is no different
# from the previous branch state
git diff beforeReset

文档:

也许最简单的方法是创建一个具有当前工作副本状态的新存储库。 如果您想保留所有提交消息,您可以先执行git log > original.log然后在新存储库中为您的初始提交消息编辑它:

rm -rf .git
git init
git add .
git commit

或者

git log > original.log
# edit original.log as desired
rm -rf .git
git init
git add .
git commit -F original.log
echo "message" | git commit-tree HEAD^{tree}

这将使用 HEAD 树创建一个孤立的提交,并在标准输出上输出其名称 (SHA-1)。 然后在那里重置你的分支。

git reset SHA-1

一步完成上述操作:

git reset $(git commit-tree HEAD^{tree} -m "Initial commit.")

这是我最终这样做的方式,以防万一它适用于其他人:

请记住,做这样的事情总是有风险的,在开始之前创建一个保存分支从来都不是一个坏主意。

从记录开始

git log --oneline

滚动到第一个提交,复制 SHA

git reset --soft <#sha#>

用从日志中复制的 SHA 替换<#sha#>

git status

确保一切都是绿色的,否则运行git add -A

git commit --amend

将所有当前更改修改为当前第一次提交

现在强制推送这个分支,它将覆盖那里的内容。

我读过一些关于使用移植物的文章,但从未对其进行过深入研究。

无论如何,您可以使用以下方式手动压缩最后 2 次提交:

git reset HEAD~1
git add -A
git commit --amend

最简单的方法是使用“管道”命令update-ref删除当前分支。

您不能使用git branch -D因为它有一个安全阀来阻止您删除当前分支。

这使您回到“初始提交”状态,您可以从新的初始提交开始。

git update-ref -d refs/heads/master
git commit -m "New initial commit"

一行6个字:

git checkout --orphan new_root_branch  &&  git commit

首先,使用git rebase --interactive将所有提交压缩成一个提交。 现在你剩下两个提交到壁球。 为此,请阅读任何

创建备份

git branch backup

重置为指定的提交

git reset --soft <#root>

然后将所有文件添加到暂存

git add .

提交而不更新消息

git commit --amend --no-edit

将带有压缩提交的新分支推送到 repo

git push -f

为此,您可以将本地 git 存储库重置为第一个提交主题标签,这样您在该提交之后的所有更改都将被取消,然后您可以使用 --amend 选项提交。

git reset your-first-commit-hashtag
git add .
git commit --amend

然后根据需要编辑第一个提交名称并保存文件。

使用移植物压扁

添加一个文件.git/info/grafts ,把你想成为你的根的提交哈希放在那里

git log现在将从该提交开始

为了让它“真实”运行git filter-branch

我通常这样做:

  • 确保所有内容都已提交,并记下最新的提交 ID,以防出现问题,或创建一个单独的分支作为备份

  • 运行git reset --soft `git rev-list --max-parents=0 --abbrev-commit HEAD`将你的头部重置为第一次提交,但保持索引不变。 自第一次提交以来的所有更改现在看起来都可以提交了。

  • 运行git commit --amend -m "initial commit"将您的提交修改为第一次提交并更改提交消息,或者如果您想保留现有的提交消息,您可以运行git commit --amend --no-edit

  • 运行git push -f强制推送您的更改

对我来说,它是这样工作的:我总共有 4 次提交,并使用了交互式变基:

git rebase -i HEAD~3

第一个提交仍然存在,我进行了 3 个最新提交。

如果您卡在接下来出现的编辑器中,您会看到如下内容:

pick fda59df commit 1
pick x536897 commit 2
pick c01a668 commit 3

您必须首先提交并将其他人压扁。 你应该拥有的是:

pick fda59df commit 1
squash x536897 commit 2
squash c01a668 commit 3

为此,使用 INSERT 键更改“插入”和“编辑”模式。

要保存并退出编辑器,请使用:wq 如果您的光标位于这些提交行之间或其他地方,请按 ESC 并重试。

结果我有两个提交:第一个仍然存在,第二个带有消息“这是 3 个提交的组合。”。

在此处查看详细信息: https ://makandracards.com/makandra/527-squash-several-git-commits-into-a-single-commit

比方说,您有从 06 到 10 的这些提交。

您想要将 06 后的提交合并到一个提交 ID。

commit 10
commit 09  
commit 08  
commit 07  
commit 06

您可以执行 git 软重置。

git reset --soft "06"

然后,运行以下命令将这些更改推送到远程分支。

git push origin HEAD --force

现在,您之前所做的所有提交都应该作为您的本地更改可用,并且您可以将所有这些提交合并为一个提交。

现在,新的提交结构应该如下所示:

commit <new_commit_containing_all_7_8_9_10>
commit 06

假设您的分支中有 3 个提交,并且它已经被推送到远程分支。

例子:

git log -4

将显示如下结果:

<your_third_commit_sha>
<your_second_commit_sha>
<your_first_commit_sha>
<master_branch_commit_sha - your branch created from master>

您想将最后 3 个提交压缩为一个提交并推送到远程分支。 以下是步骤。

git reset --soft <master_branch_commit_sha>

现在所有提交更改都已集成但未提交。 通过以下方式验证:

git status

使用消息提交所有更改:

git commit -m 'specify details'

强制将单个提交推送到远程分支:

git push -f

这个答案改进了上面的几个(请投票),假设除了创建一个提交(no-parents no-history)之外,您希望保留该提交的所有提交数据:

  • 作者(姓名和电子邮件)
  • 创作日期
  • 提交者(姓名和电子邮件)
  • 承诺日期
  • 提交日志消息

当然,新/单一提交的提交 SHA 会改变,因为它代表一个新的(非)历史,成为无父/根提交。

这可以通过阅读git log并为git commit-tree设置一些变量来完成。 假设您想在新分支one-commit中从master创建单个提交,保留上面的提交数据:

git checkout -b one-commit master ## create new branch to reset
git reset --hard \
$(eval "$(git log master -n1 --format='\
COMMIT_MESSAGE="%B" \
GIT_AUTHOR_NAME="%an" \
GIT_AUTHOR_EMAIL="%ae" \
GIT_AUTHOR_DATE="%ad" \
GIT_COMMITTER_NAME="%cn" \
GIT_COMMITTER_EMAIL="%ce" \
GIT_COMMITTER_DATE="%cd"')" 'git commit-tree master^{tree} <<COMMITMESSAGE
$COMMIT_MESSAGE
COMMITMESSAGE
')

这对我来说效果最好。

git rebase -X ours -i master

这将使 git 更喜欢您的功能分支来掌握; 避免繁重的合并编辑。 您的分支需要与 master 保持同步。

ours
           This resolves any number of heads, but the resulting tree of the merge is always that of the current
           branch head, effectively ignoring all changes from all other branches. It is meant to be used to
           supersede old development history of side branches. Note that this is different from the -Xours
           option to the recursive merge strategy.

当我从 git 存储库恢复模板时,我通常会压缩整个树,以获得更清晰的历史记录并确保符合法律规定。 我的工作流程如下所示:

git clone https://git.invalid/my-awesome-template.git my-awesome-foo
cd !$
git branch -M master my-awesome-template/master
git checkout --orphan master
git rm -rf /
git commit --allow-empty --allow-empty-message -m 'Initial commit'
git merge --squash --allow-unrelated-histories my-awesome-template/master
git commit
git branch -D my-awesome-template/master
# you can now `drop' that "Initial commit":
git rebase -i --root

这会将您的整个历史记录压缩成一条大的提交消息。

在这个特定的例子中:

  • master是工作分支
  • my-awesome-template/master是一个中间分支

由于我在这里提出的解决方案遇到了一些问题,我想分享一个非常简单的解决方案(将功能分支上的所有提交压缩为一个):

git merge origin/master && git reset --soft origin/master

前面的 merge cmd 确保在提交时不会影响您最近从 master 所做的更改! 之后,只需提交更改并执行git push -f

我用 HEAD^{tree} 尝试了不同的命令,但从 zsh 得到了这个错误:

zsh: no matches found: HEAD^{tree}

这是 ohmyzsh 处理扩展的方式,有关详细信息,请参阅此问题: https ://github.com/ohmyzsh/ohmyzsh/issues/449

最简单的解决方法是运行以下命令:

unsetopt nomatch

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM