Git - 在特定提交之前压缩历史中的所有提交

Gre*_*aro 5 git

我有一个要转换为 Git 的 Mercurial 存储库。提交历史非常大,我不需要新仓库中的所有提交历史。一旦我将提交历史转换为 Git(并且在推送到新存储库之前),我想将某个标签之前的所有提交压缩为一个提交。

所以,如果我有:

commit 6
commit 5
commit 4
commit 3
commit 2
commit 1 -- First commit ever
Run Code Online (Sandbox Code Playgroud)

我想结束:

commit 6
commit 5
commit X -- squashed 1, 2, 3, 4
Run Code Online (Sandbox Code Playgroud)

注意:我需要压缩数以千计的提交。因此,手动挑选/标记它们不是一种选择。

tor*_*rek 7

到目前为止,其他答案都建议 rebase。这可以工作,在某些情况下,这取决于在转换到Git仓库提交图表。新的发烧友 rebase with--rebase-merges绝对可以做到。但这是一种笨拙的方法。执行此操作的理想方法是从您要保留的第一个提交开始转换提交 也就是说,让您的 Mercurial 导出器导出到 Git,作为 Git 的第一次提交,您要假装的修订版是根。让 Mercurial 导出器继续将提交的后代导出到导入器中,一次一个,就像导出器总是要做这项工作一样(无论以何种方式)。

无论你如何做到这一点取决于你使用的转换什么工具(S)。(我实际上并没有进行任何这些转换,但大多数人似乎都在使用hg-fast-exportand git fast-import。我没有过多研究 的内部细节,hg-fast-export但没有明显的原因它不能这样做。)


从根本上(内部),Mercurial 将提交存储为变更集。这是不是对Git的情况:Git的存储快照来代替。但是,Mercurial通过根据需要将变更集汇总在一起来检查(即提取)快照,因此如果您的工具通过执行hg checkout(或其内部等效项)工作,那么首先这里没有问题:您只需避免检查修订在您想要的第一个快照之前,并将它们导入 Git,生成的 Git 历史记录将从所需的点开始。


但是,如果您使用的工具使这变得不方便,请注意,在将整个存储库历史记录(包括所有分支和合并)转换为 Git 快照后,您的Git存储库将相对容易地作为第二遍。例如,您的 Git 历史记录可能如下所示:

          o-..-o            o--o   <-- br1
         /      \          /
...--o--o--....--o--*--o--o--o--o   <-- br2
      \         /             \
       o--...--o               o   <-- master
Run Code Online (Sandbox Code Playgroud)

其中 commit*是您希望在 Git 存储库中看到的第一个提交。(请注意,如果之前有多个历史可追溯*,则您会遇到不同的问题,如果没有额外的历史修改,就无法首先进行这种转换。但只要*处于某种阻塞点,就像在这张图,很容易在这里剪下图。)

要删除之前的所有内容*,只需使用git replace来进行非常commit的替代提交*,但没有父项:

git replace --graft <hash-of-*>
Run Code Online (Sandbox Code Playgroud)

你现在有一个大多数 Git 将使用的替代品,而不是*,它没有父提交。然后git filter-branch使用无操作过滤器运行所有分支和标签:

git filter-branch --tag-name-filter cat -- --all
Run Code Online (Sandbox Code Playgroud)

或者,曾经git filter-repo包含在 Git 中(或者如果您已经安装了它):

git filter-repo --force
Run Code Online (Sandbox Code Playgroud)

(使用时要小心这个--force选项filter-repo:这会破坏这个存储库中的旧历史,但在这个 csae 中,这就是我们想要的)。

这将复制每个可访问的提交,包括替代*但排除*和它自己的历史,到新的提交,然后更新你的分支和标签名称。

如果使用过滤器分支,删除refs/originals/名称空间(见git filter-branch文档获取详细信息),迫使原始对象的早期清除如果你喜欢(额外提交最终会走自己的),你就大功告成了。


Elp*_*Kay 6

假设原来的分支是master,新的分支是new

git checkout --orphan new commit4
git commit -m "squash commits"
git branch tmp master
git rebase commit4 tmp --onto new
git checkout new
git merge tmp
git branch -D tmp
Run Code Online (Sandbox Code Playgroud)

如果你想保留合并提交,“git rebase”中需要选项“-p”。


pPa*_*eta 5

为了准确地完成所有这些,步骤将是

  1. 签出到特定的提交
  2. 将它之前的所有内容压缩到此特定提交
  3. 樱桃挑选在此之后发生的提交
  4. 删除现有分支
  5. 将你最近煮熟的头保存到相同的分支名称中

function git_squash_from() {
    COMMIT_TO_SQUASH=$1
    SQUASH_MESSAGE=$2

    STARTING_BRANCH=$(git rev-parse --abbrev-ref HEAD) # This will be overwritten
    CURRENT_HEAD=$(git rev-parse HEAD)

    echo From $CURRENT_HEAD to the successor of  $COMMIT_TO_SQUASH will retain, from $COMMIT_TO_SQUASH to beginging will be squashed

    git checkout $COMMIT_TO_SQUASH
    git reset $(git commit-tree HEAD^{tree} -m "$SQUASH_MESSAGE")
    git cherry-pick $CURRENT_HEAD...$COMMIT_TO_SQUASH
    git branch -D $STARTING_BRANCH
    git checkout -b $STARTING_BRANCH    
}

git_squash_from 87ef7fa "Squash ... "

Run Code Online (Sandbox Code Playgroud)

您可以进一步扩展它以从所有提交消息构建 SQUASH_MESSAGE。