在 Git 中压缩前两次提交?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/598672/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 06:13:10  来源:igfitidea点击:

Squash the first two commits in Git?

gitrebasegit-rebasesquash

提问by kch

With git rebase --interactive <commit>you can squash any number of commits together into a single one.

随着git rebase --interactive <commit>你能够压制任意数量的提交连成一个单一的一个。

That's all great unless you want to squash commits into the initial commit. That seems impossible to do.

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

Are there any ways to achieve it?

有什么方法可以实现吗?



Moderately related:

中度相关:

In a related question, I managed to come up with a different approach to the need of squashing against the first commit, which is, well, to make it the second one.

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

If you're interested: git: how to insert a commit as the first, shifting all the others?

如果您有兴趣:git:如何将提交作为第一个插入,转移所有其他提交?

回答by VonC

Update July 2012 (git 1.7.12+)

2012 年 7 月更新(git 1.7.12+

You now can rebase all commits up to root, and select the second commit Yto be squashed with the first X.

您现在可以将所有提交重新设置为 root,并选择第二个提交Y与第一个X.

git rebase -i --root master

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

This command can now be used to rewrite all the history leading from "$tip" down to the root commit.

git rebase [-i] --root $tip

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

See commit df5df20c1308f936ea542c86df1e9c6974168472 on GitHubfrom Chris Webb (arachsys).

提交df5df20c1308f936ea542c86df1e9c6974168472在GitHub上克里斯·韦伯(arachsys



Original answer (February 2009)

原始答案(2009 年 2 月)

I believe you will find different recipes for that in the SO question "How do I combine the first two commits of a git repository?"

我相信您会在 SO 问题“如何合并 git 存储库的前两个提交?”中找到不同的方法

Charles Baileyprovided there the most detailed answer, reminding us that a commit is a full tree (not just diffs from a previous states).
And here the old commit (the "initial commit") and the new commit (result of the squashing) will have no common ancestor.
That mean you can not "commit --amend" the initial commit into new one, and then rebase onto the new initial commit the history of the previous initial commit (lots of conflicts)

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

(That last sentence is no longer true with git rebase -i --root <aBranch>)

(最后一句话不再正确git rebase -i --root <aBranch>

Rather (with Athe original "initial commit", and Ba subsequent commit needed to be squashed into the initial one):

相反(使用A原始的“初始提交”,并且B需要将后续提交压缩到初始提交中):

  1. Go back to the last commit that we want to form the initial commit (detach HEAD):

    git checkout <sha1_for_B>
    
  2. Reset the branch pointer to the initial commit, but leaving the index and working tree intact:

    git reset --soft <sha1_for_A>
    
  3. Amend the initial tree using the tree from 'B':

    git commit --amend
    
  4. Temporarily tag this new initial commit (or you could remember the new commit sha1 manually):

    git tag tmp
    
  5. Go back to the original branch (assume master for this example):

    git checkout master
    
  6. Replay all the commits after B onto the new initial commit:

    git rebase --onto tmp <sha1_for_B>
    
  7. Remove the temporary tag:

    git tag -d tmp
    
  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
    

That way, the "rebase --onto" does not introduce conflicts during the merge, since it rebases history made afterthe last commit (B) to be squashed into the initial one (which was A) to tmp(representing the squashed new initial commit): trivial fast-forward merges only.

这样,“ rebase --onto” 不会在合并期间引入冲突,因为它将在最后一次提交 ( )之后创建的历史变基,B以将其压缩到初始提交 ( A) 到tmp(代表压缩的新初始提交):琐碎的快进仅合并。

That works for "A-B", but also "A-...-...-...-B" (any number of commits can be squashed into the initial one this way)

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

回答by fonsinchen

I've reworked VonC's script to do everything automatically and not ask me for anything. You give it two commit SHA1s and it will squash everything between them into one commit named "squashed history":

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

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

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

# amend the initial tree using the tree from 
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  onto the new initial commit
git rebase --onto $TARGET 

回答by Ryan C. Thompson

For what it's worth, I avoid this problem by always creating a "no-op" first commit, in which the only thing in the repository is an empty .gitignore:

对于它的价值,我总是通过创建一个“无操作”的第一次提交来避免这个问题,其中存储库中唯一的东西是一个空的 .gitignore:

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

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

That way, there's never any reason to mess with the first commit.

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

回答by Mike Looijmans

If you simply want to squash all commits into a single, initial commit, just reset the repository and amend the first commit:

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

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

Git reset will leave the working tree intact, so everything is still there. So just add the files using git add commands, and amend the first commit with these changes. Compared to rebase -i you'll lose the ability to merge the git comments though.

Git reset 将使工作树完好无损,所以一切都还在那里。所以只需使用 git add 命令添加文件,并使用这些更改修改第一次提交。与 rebase -i 相比,您将失去合并 git 注释的能力。

回答by Antony Hatchkins

This will squash second commit into the first one:

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

A-B-C-... -> AB-C-...

A-B-C-... -> AB-C-...

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

Commit message for AB will be taken from B (although I'd prefer from A).

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

Has the same effect as Uwe Kleine-K?nig's answer, but works for non-initial A as well.

与 Uwe Kleine-K?nig 的答案具有相同的效果,但也适用于非首字母 A。

回答by Uwe Kleine-K?nig

You can use git filter-branch for that. e.g.

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

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

This results in AB-C throwing away the commit log of A.

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

回答by hillu

Squashing the first and second commit would result in the first commit being rewritten. If you have more than one branch that is based off the first commit, you'd cut off that branch.

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

Consider the following example:

考虑以下示例:

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

Squashing a and b into a new commit "ab" would result in two distinct trees which in most cases is not desirable since git-mergeand git-rebasewill no longer work across the two branches.

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

ab---HEAD

a---d

If you really want this, it can be done. Have a look at git-filter-branchfor a powerful (and dangerous) tool for history rewriting.

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

回答by todd

You could use rebase interactive to modify the last two commits before they've been pushed to a remote

您可以使用 rebase 交互来修改最后两次提交,然后再将它们推送到远程

git rebase HEAD^^ -i

回答by decandia

There is an easier way to do this. Let's assume you're on the masterbranch

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

Create a new orphaned branch which will remove all commit history:

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

$ git checkout --orphan new_branch

Add your initial commit message:

添加您的初始提交消息:

$ git commit -a

Get rid of the old unmerged master branch:

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

$ git branch -D master

Rename your current branch new_branchto master:

将您当前的分支重命名new_branchmaster

$ git branch -m master