关于rebase:如何将所有git提交压缩成一个?

How to squash all git commits into one?

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

我可以重设为第一个提交,但这将留给我两个提交。在第一个提交之前是否有引用提交的方法?


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

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


更新

我化名为git squash-all
示例用法:git squash-all"a brand new start"

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

警告:请记住提供注释,否则将使用默认的提交消息"新的开始"。

也可以使用以下命令创建别名:

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

一个衬垫

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

注:这里"A new start只是一个例子,请随意使用您自己的语言。

DR

不需要挤压,使用git commit-tree创建一个孤立的提交并执行它。

解释

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

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


    Creates a new commit object based on the provided tree object
    and emits the new commit object id on stdout. The log message is
    read from the standard input, unless -m or -F options are given.

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

  • 将当前分支重置为新提交

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

  • 这样,工作区中的任何东西都不会被触摸,也不会需要重新平衡/挤压,这使得它非常快。所需的时间与存储库的大小或历史深度无关。

    变更:项目模板的新回购

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

    1
    2
    3
    4
    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")

    这样可以避免将模板repo添加为远程(origin或其他类型),并将模板repo的历史记录折叠到初始提交中。


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

    1
    git rebase --interactive --root

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

    当您压缩大量提交时,以下是两个更快、更高效的解决方案:

    备选方案1:孤立分支

    您只需在当前分支的提示(即最新提交)处创建一个新的孤立分支。这个孤立分支形成了一个全新的独立提交历史树的初始根提交,这实际上相当于压扁所有提交:

    1
    2
    3
    4
    5
    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

    文档:

    • Git结账(1)手册页。

    备选方案2:软重置

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

    1
    2
    3
    4
    5
    6
    7
    8
    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重置(1)手册页。


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

    1
    2
    3
    4
    rm -rf .git
    git init
    git add .
    git commit

    1
    2
    3
    4
    5
    6
    git log > original.log
    # edit original.log as desired
    rm -rf .git
    git init
    git add .
    git commit -F original.log


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

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

    1
    git reset SHA-1


    我就是这样做的,以防它对其他人有用:

    记住这样做总是有风险的,在开始之前创建一个save分支绝对不是一个坏主意。

    从日志开始

    1
    git log --oneline

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

    1
    git reset --soft <#sha#>

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

    1
    git status

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

    1
    git commit --amend

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

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


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

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

    这将使您回到"初始提交"状态,在该状态下,您可以从新的初始提交开始。

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


    我读过一些关于使用移植物的文章,但从来没有做过太多的调查。

    无论如何,您可以手动挤压最后2个提交,如下所示:

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


    首先,使用git rebase --interactive将所有提交压缩为一个提交。现在你只剩下两个要被压扁的任务了。为此,请阅读

    • 如何组合Git存储库的前两个提交?
    • 吉特:如何压扁前两个承诺?

    一行6个字

    1
    git checkout --orphan new_root_branch  &&  git commit


    用嫁接物压扁

    添加一个文件.git/info/grafts,将您想要成为根目录的提交哈希放到这里。

    git log现在将从该承诺开始

    使之"真实"运行git filter-branch


    创建备份

    1
    git branch backup

    重置为指定的提交

    1
    git reset --soft <#root>

    然后将所有文件添加到临时

    1
    git add .

    提交而不更新消息

    1
    git commit --amend --no-edit

    以压缩的回购承诺推动新的分支机构

    1
    git push -f


    这个答案在上面的几项上有所改进(请投票给他们),假设除了创建一个提交(没有父母没有历史记录),您还希望保留该提交的所有提交数据:

    • 作者(姓名和电子邮件)
    • 作者日期
    • 专员(姓名和电子邮件)
    • 承诺日期
    • 通信日志消息

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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
    ')

    我通常这样做:

    • 确保所有内容都已提交,并在出现问题时写下最新的提交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以强制推行更改