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

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

我可以重设到第一次提交,但这会给我留下2次提交。 有没有办法在第一次提交之前引用提交?

399489 次浏览

我读过一些关于使用移植物的东西,但从来没有调查过。

无论如何,你可以用这样的方式手动压缩最后2次提交:

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

也许最简单的方法是使用工作副本的当前状态创建一个新的存储库。如果您想保留所有提交消息,您可以首先执行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

首先,使用git rebase --interactive将所有提交压缩到单个提交中。现在你只剩下两个提交要压缩。为此,请阅读以下任何

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

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

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

git update-ref -d refs/heads/master
git commit -m "New initial commit"
echo "message" | git commit-tree HEAD^{tree}

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

git reset SHA-1

要在一个步骤中完成上述操作:

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

git1.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'

One Liner

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

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

太长别读

不需要压扁,使用git commit-tree创建一个孤儿提交并使用它。

解释

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

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

根据提供的树对象创建一个新的提交对象 并在stdout上发出新的提交对象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或其他方式)并将模板存储库的历史记录折叠到您的初始提交中。

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

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 --oneline

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

git reset --soft <#sha#>

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

git status

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

git commit --amend

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

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

这个答案改进了上面的一对夫妇(请投票给他们),假设除了创建一个提交(无父母无历史),你想要保留该提交的所有提交数据:

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

当然,新的/单个提交的提交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/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强制推送您的更改

创建备份

git branch backup

重置为指定提交

git reset --soft <#root>

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

git add .

提交而不更新消息

git commit --amend --no-edit

将提交压扁的新分支推送到repo

git push -f

一行六个字:

git checkout --orphan new_root_branch  &&  git commit

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

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

然后编辑第一个提交nam如果需要并保存文件。

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

git rebase -i HEAD~3

第一次提交仍然存在,我接受了3次最新提交。

如果你被困在接下来出现的编辑器中,你会看到类似的smth:

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

这对我来说效果最好。

git rebase -X ours -i master

这将git会更喜欢您的功能分支而不是master;避免艰苦的合并编辑。您的分支需要与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 merge origin/master && git reset --soft origin/master

前面的合并cmd确保在提交时不会有来自master的最近更改!之后,只需提交更改并执行git push -f

假设您的分支中有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

当我从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是一个中间分支

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

zsh: no matches found: HEAD^{tree}

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

最简单的修复是运行此命令:

unsetopt nomatch

假设,你有这些提交从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