【问题标题】:Squash the first two commits in Git? [duplicate]压缩 Git 中的前两个提交? [复制]
【发布时间】:2010-10-10 13:33:55
【问题描述】:

使用git rebase --interactive <commit>,您可以将任意数量的提交压缩成一个。

这一切都很好,除非您想将提交压缩到初始提交中。这似乎是不可能的。

有什么方法可以实现吗?


中度相关:

在一个相关问题中,我设法提出了一种不同的方法来解决第一次提交的压缩需求,也就是说,使其成为第二次提交。

如果您有兴趣:git: how to insert a commit as the first, shifting all the others?

【问题讨论】:

  • 单行:git squash 2,别名为squash = !"f() { NL=$1; GIT_EDITOR=\"sed -i '2,$NL s/pick/squash/;/# This is the 2nd commit message:/,$ {d}'\"; git rebase -i HEAD~$NL; }; f"。见stackoverflow.com/a/28789349/670229
  • 'squash' 脚本:两次提交后,该脚本创建的 HEAD~2 不存在,因此在这一特定情况下它不会运行。

标签: git rebase git-rebase squash


【解决方案1】:

2012 年 7 月更新 (git 1.7.12+)

您现在可以将所有提交变基到 root,并选择第二个提交 Y 与第一个 X 压缩。

git rebase -i --root master

pick sha1 X
squash sha1 Y
pick sha1 Z
git rebase [-i] --root $tip

此命令现在可用于重写从“$tip”到根提交的所有历史记录。

Chris Webb (arachsys) 看到commit df5df20c1308f936ea542c86df1e9c6974168472 on GitHub

in the comments 所述,如果您需要在远程存储库中发布该返工,则在任何rebase 操作之后都需要git push --force-with-lease(比--force 更安全,如Mikko Mantalainen remind us)。


原始答案(2009 年 2 月)

我相信您会在 SO 问题“How do I combine the first two commits of a git repository?”中找到不同的食谱

Charles Bailey 提供了最多的detailed answer,提醒我们提交是一棵完整的树(不仅仅是与先前状态的差异)。
在这里,旧提交(“初始提交”)和新提交(挤压的结果)将没有共同的祖先。
这意味着您不能将初始提交“commit --amend”转换为新的提交,然后将先前初始提交的历史重新定位到新的初始提交(很多冲突)

(最后一句话对于git rebase -i --root <aBranch> 不再适用)

而是(A 是原始“初始提交”,B 需要将后续提交压缩到初始提交):

  1. 回到我们要形成初始提交的最后一个提交(分离 HEAD):

     git checkout <sha1_for_B>
    
  2. 将分支指针重置为初始提交,但保持索引和工作树不变:

     git reset --soft <sha1_for_A>
    
  3. 使用“B”中的树修改初始树:

     git commit --amend
    
  4. 临时标记这个新的初始提交(或者您可以手动记住新的提交 sha1):

     git tag tmp
    
  5. 回到原来的分支(本例假设master):

     git checkout master
    
  6. 将 B 之后的所有提交重播到新的初始提交:

     git rebase --onto tmp <sha1_for_B>
    
  7. 移除临时标签:

     git tag -d tmp
    

这样,“rebase --onto”不会在合并期间引入冲突,因为它会将历史最后一次提交 (B) 压缩到初始提交 (是 A) 到 tmp(表示被压扁的新初始提交):仅用于微不足道的快进合并。

这适用于“A-B”,也适用于“A-...-...-...-B”(任何数量的提交都可以通过这种方式压缩到最初的提交中)

【讨论】:

  • 很棒的提示。会记住的。唉,我在“git svn”repo 上尝试过,但确实中断了与 svn 的连接。不用担心,我有备份...
  • 这对我不起作用。当我继续使用git push 时,我收到一条错误消息,提示我需要先使用git pull。如果我这样做然后推送,我最终会重复几个提交,而不是减少提交的数量。
  • @MattHuggins 但是如果你变基,你必须推动--force,你不能只是推动。历史已更改(不同的 SHA1),因此推送不再是快进。我确认如果你拉,然后推,你最终会出现重复的提交。见stackoverflow.com/q/7462553/6309
  • @MattHuggins 并不是说​​如果你的上游仓库(你推送的那个)可以在本地访问,其他人不会知道你做了一个push --force ;) 见stackoverflow.com/q/15028246/6309
  • @MikkoRantalainen 谢谢,好点子。我已将您的评论包含在答案中以获得更多可见性,以及指向 --force--force-with-lease 之间区别的链接。
【解决方案2】:

我重新编写了 VonC 的脚本,让所有事情都自动完成,并且不要求我做任何事情。你给它两个提交 SHA1,它会将它们之间的所有内容压缩到一个名为“压缩历史”的提交中:

#!/bin/sh
# Go back to the last commit that we want
# to form the initial commit (detach HEAD)
git checkout $2

# reset the branch pointer to the initial commit (= $1),
# but leaving the index and working tree intact.
git reset --soft $1

# amend the initial tree using the tree from $2
git commit --amend -m "squashed history"

# remember the new commit sha1
TARGET=`git rev-list HEAD --max-count=1`

# go back to the original branch (assume master for this example)
git checkout master

# Replay all the commits after $2 onto the new initial commit
git rebase --onto $TARGET $2

【讨论】:

  • +1 用于创建它。您应该提到它不适用于在历史记录中的某个地方重新提交提交,只有最近的提交。
  • 另见this answer
【解决方案3】:

如果您只是想将所有提交压缩成一个初始提交,只需重置存储库并修改第一个提交:

git reset hash-of-first-commit
git add -A
git commit --amend

Git reset 将使工作树保持原样,所以一切都还在。因此,只需使用 git add 命令添加文件,并使用这些更改修改第一次提交。与 rebase -i 相比,您将失去合并 git cmets 的能力。

【讨论】:

    【解决方案4】:

    不管怎样,我总是通过创建一个“无操作”的首次提交来避免这个问题,其中存储库中唯一的东西是一个空的 .gitignore:

    https://github.com/DarwinAwardWinner/git-custom-commands/blob/master/bin/git-myinit

    这样一来,就没有任何理由搞乱第一次提交了。

    【讨论】:

    • git 应该自动执行此操作,如果它不那么疯狂的话。有一种很好的方法可以将这样的初始提交插入到现有的存储库中......stackoverflow.com/questions/645450/…
    • git commit --allow-empty -m empty 通常是我的第一次提交。这甚至可以避免使用.gitignore 文件“污染”提交。请注意,一些旧工具无法像这样查看空树。
    【解决方案5】:

    这会将第二次提交压缩到第一次:

    A-B-C-... -&gt; AB-C-...

    git filter-branch --commit-filter '
        if [ "$GIT_COMMIT" = <sha1ofA> ];
        then
            skip_commit "$@";
        else
            git commit-tree "$@";
        fi
    ' HEAD
    

    AB 的提交消息将从 B 获取(尽管我更喜欢从 A 获取)。

    与 Uwe Kleine-König 的答案具有相同的效果,但也适用于非初始 A。

    【讨论】:

    • @Anothony - 嗨,作为一个 git 新手,我不确定这是否适合我的需求,但它看起来很有希望。你能再解释一下吗?我试图将我所有的 git 提交压缩为一个,以便将樱桃挑选到现有项目中(将初始提交留在那里很好)。但是,我需要一些可编写脚本的东西,因为有很多项目, git rebase -i 不是。这个命令对我有用吗?我是否为第一个提交(A)指定哈希,其中 C 是 HEAD?你能提供的任何进一步的解释都会很棒!非常感谢!
    • 合并两个项目通常不需要将所有提交压缩为一个。在单独的问题中解释为什么需要它。 “我如何为第一次提交(A)指定哈希,其中 C 是 HEAD?”也是一个单独的问题。 git rev-list --reverse HEAD|head -n1 可能是答案
    【解决方案6】:

    压缩第一次和第二次提交将导致第一次提交被重写。如果您有多个基于第一次提交的分支,您将切断该分支。

    考虑以下示例:

    a---b---HEAD
     \
      \
       '---d
    

    将 a 和 b 压缩成一个新的提交 "ab" 会产生两个不同的树,这在大多数情况下是不可取的,因为 git-mergegit-rebase 会不再跨两个分支工作。

    ab---HEAD
    
    a---d
    

    如果你真的想要这个,它可以做到。看看 git-filter-branch 了解一个强大(但危险)的历史重写工具。

    【讨论】:

    • 好点。 +1。我想您需要从 ab 分支,并将 a---d 重新设置到该分支上,以便从新的公共点 ab 重播 a-d。然后删除 a-d 分支,此时无用。
    【解决方案7】:

    您可以为此使用 git filter-branch。例如

    git filter-branch --parent-filter \
    'if test $GIT_COMMIT != <sha1ofB>; then cat; fi'
    

    这会导致 AB-C 丢弃 A 的提交日志。

    【讨论】:

    • 这对我不起作用。 git filter-branch 表示分支没有变化。
    • @Leo:你用实际的哈希值替换了 吗?
    • 这需要更多的赞成票!由于我复杂的 git 历史,Rebase 对我不起作用,但它成功了。
    【解决方案8】:

    您可以使用 rebase interactive 在最后两个提交被推送到远程之前修改它们

    git rebase HEAD^^ -i
    

    【讨论】:

    • 是的,但是 kch 询问了关于压缩 first 两个提交,而不是 last(最近的)两个提交。
    • 像这样超级简单,你只需要使用你想要合并的提交的哈希值并使用它而不是 HEAD^^
    • @SebastianBlask,我不相信它这么简单。如果您使用第一次提交的 SHA1,那么您只会从 second 提交开始。不幸的是,无法压缩/修复该提交。
    【解决方案9】:

    有一种更简单的方法可以做到这一点。假设您在 master 分支上

    创建一个新的孤立分支,它将删除所有提交历史记录:

    $ git checkout --orphan new_branch
    

    添加您的初始提交消息:

    $ git commit -a
    

    摆脱旧的未合并的主分支:

    $ git branch -D master
    

    将您当前的分支new_branch 重命名为master

    $ git branch -m master
    

    【讨论】:

    • 然后你丢失了整个提交历史?
    猜你喜欢
    • 2017-09-26
    • 2012-12-18
    • 1970-01-01
    • 1970-01-01
    • 2012-11-20
    • 2013-01-08
    • 2021-04-30
    • 2014-09-13
    • 1970-01-01
    相关资源
    最近更新 更多