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

时间:2009-11-01 08:38:07

标签: git rebase git-rebase squash git-rewrite-history

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

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

20 个答案:

答案 0 :(得分:566)

在最新版本的git中,您可以使用git rebase --root -i

对于除第一次提交之外的每次提交,请将pick更改为squash

答案 1 :(得分:269)

更新

我已经制作了别名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"

警告:记得提供评论,否则将使用默认提交消息“新的开始”。

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

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; DR

无需压缩,使用git commit-tree创建孤立提交并继续使用。

解释

  1. 通过git commit-tree

    创建一次提交

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

      

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

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

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

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

  3. 这样,工作区中没有任何东西被触摸,也没有触及   需要rebase / squash,这让它变得非常快。所需的时间与存储库大小或历史深度无关。

    变化:项目模板的新回购

    这对于使用另一个存储库作为模板/ archetype / seed / skeleton在新项目中创建“初始提交”非常有用。例如:

    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或其他),并将模板仓库的历史记录折叠到初始提交中。

答案 2 :(得分:145)

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

git rebase --interactive --root

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

当您压缩大量提交时,以下是两种更快捷,更有效的解决方案:

替代解决方案#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

文档:

答案 3 :(得分:121)

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

答案 4 :(得分:46)

echo "message" | git commit-tree HEAD^{tree}

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

git reset SHA-1

答案 5 :(得分:36)

以下是我最终如何做到这一点,以防它适用于其他人:

请记住,做这样的事情总是有风险,在开始之前创建保存分支绝对不错。

首先记录

git log --oneline

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

git reset --soft <#sha#>

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

git status

确保所有内容都为绿色,否则运行git add -A

git commit --amend

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

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

答案 6 :(得分:32)

最简单的方法是使用'plumbing'命令update-ref删除当前分支。

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

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

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

答案 7 :(得分:28)

我读过一些关于使用移植物的内容,但从未对它进行过多的研究。

无论如何,你可以手动压缩最后两次提交:

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

答案 8 :(得分:16)

首先,使用git rebase --interactive将所有提交压缩到一次提交中。现在你有两个提交壁纸。为此,请阅读

中的任何一个

答案 9 :(得分:10)

一行6个单词

git checkout --orphan new_root_branch  &&  git commit

答案 10 :(得分:6)

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

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

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

答案 11 :(得分:4)

使用移植物挤压

添加文件.git/info/grafts,将要成为根的提交哈希放在那里

git log现在将从该提交开始

使其“真实”运行git filter-branch

答案 12 :(得分:3)

创建备份

git branch backup

重置为指定的提交

git reset --soft <#root>

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

git add .

提交而不更新消息

git commit --amend --no-edit

使用压缩的新分支推送回购

git push -f

答案 13 :(得分:1)

这个答案在以上几个方面有所改进(请投票),假设除了创建一个提交(没有父母无历史记录)之外,你想要保留所有提交的提交数据:

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

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

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

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
')

答案 14 :(得分:1)

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

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

答案 15 :(得分:1)

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

示例:

var lineHeight = 25;

var baseX = circle.x + circleRadius/2 * Math.cos(angleRadians);
var baseY = circle.y + circleRadius/2 * Math.sin(angleRadians);

var x1 = baseX + lineHeight*Math.cos(angleRadians);
var y1 = baseY + lineHeight*Math.sin(angleRadians);


this.testgraphics = this.add.graphics();
this.testgraphics.lineStyle(4, 0xff0000);
this.testgraphics.beginPath();
this.testgraphics.moveTo(baseX, baseY);
this.testgraphics.lineTo(x1, y1);
this.testgraphics.closePath();
this.testgraphics.strokePath();

将显示如下结果:

git log -4

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

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

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

git reset --soft <master_branch_commit_sha>

使用消息提交所有更改:

git status

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

git commit -m 'specify details'

答案 16 :(得分:0)

我通常这样做:

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

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

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

    < / LI>
  • 运行git push -f以强行推送您的更改

答案 17 :(得分:0)

这对我来说效果最好。

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.

答案 18 :(得分:0)

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

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

前面的合并cmd确保提交时,master的最新更改都不会对您造成影响!之后,只需提交更改并执行git push -f

答案 19 :(得分:0)

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