Git:压缩/修复早期提交

27

假设你有以下内容:

A-B-C

现在您的构建/测试失败了。应该将修复程序合并到A中。我当前的工作流程如下:

$ git commit -m "fixA"

A-B-C-fixA

$ git rebase -i A~1

将修复A的补丁合并到A中,结果为:

A'-B-C

有没有类似下面命令的功能:

A-B-C + (index with fix for A)

$ git commit -supperdupper A 

结果:

A'-B-C

1
为什么要求将fixA合并到A中,这个要求很奇怪啊? - Marcin Gil
12
因为本来它应该在A处。 - elmarco
4
我也做同样的事情,不确定为什么人们认为这很奇怪。如果你试图将提交内容组织成小而具有逻辑性的部分,那么同时准备几个未发布的提交是很自然的。(你可能不知道完成C之前是否已经完成了A)。 - andy
我也总是做同样的事情 - 我一直渴望像这样的东西(我喜欢称之为 git fixup <rev>)已经有一段时间了。 - Frerich Raabe
类似:https://dev59.com/AnA75IYBdhLWcg3wy8Ui - anarcat
5个回答

21

如果你只是想寻找解决早期提交问题的简单方法,请阅读问题!它解释了所有内容。但由于Elmarco正在寻求一种简洁的方法,所以我们来看看:

从Git 1.7.0开始,--autosquash选项用于rebase,可以实现您想要的功能。 还有--fixup--squash选项用于commit,可以使事情变得更加容易。通过一些别名,您甚至可以将整个过程变成一个单一的命令。

我建议升级到最新的Git,以获得最大的优势:

git/Documentation/RelNotes $ grep -i -A1 autosquash\\\|fixup *
1.7.0.txt: * "git rebase -i" learned new action "fixup" that squashes the change
1.7.0.txt-   but does not affect existing log message.
--
1.7.0.txt: * "git rebase -i" also learned --autosquash option that is useful
1.7.0.txt:   together with the new "fixup" action.
1.7.0.txt-
--
1.7.3.txt: * "git rebase -i" peeks into rebase.autosquash configuration and acts as
1.7.3.txt:   if you gave --autosquash from the command line.
1.7.3.txt-
--
1.7.4.txt: * "git commit" learned --fixup and --squash options to help later invocation
1.7.4.txt-   of the interactive rebase.
--
1.7.4.txt: * "git rebase --autosquash" can use SHA-1 object names to name which
1.7.4.txt:   commit to fix up (e.g. "fixup! e83c5163").
1.7.4.txt-

2
不错的例子:http://technosorcery.net/blog/2010/02/07/fun-with-the-upcoming-1-7-release-of-git-rebase---interactive---autosquash/ - Martin Carpenter

5

创建了一些别名,以便更轻松地使用git 1.7中添加的git commit --fixupgit commit --squash命令。将它们添加到您的~/.gitconfig文件中:

[alias]
  fixup = !sh -c 'REV=$(git rev-parse $1) && git commit --fixup $@ && git rebase -i --autosquash $REV^' -
  squash = !sh -c 'REV=$(git rev-parse $1) && git commit --squash $@ && git rebase -i --autosquash $REV^' -

使用方法:

$ git commit -am 'bad commit'
$ git commit -am 'good commit'

$ git add .          # Stage changes to correct the bad commit
$ git fixup HEAD^    # HEAD^ can be replaced by the SHA of the bad commit

糟糕的提交可能在几个提交之前。


使用您的修复别名非常好。通过添加--autostash,它就完美了。 - kuga

4

我的当前git工作流程非常依赖于--fixup/--squash参数,因此我编写了一个新的git-fixup命令,自动处理大部分繁琐的部分:

  • git fixup 显示修改过的文件,并将它们分组到最近修改同一文件的提交下面。
  • git fixup -a 将所有这些更改作为--fixup更改提交,并指定相应的“父”提交。
  • git fixup -r 对所有fixup提交进行自动git rebase --autosquash操作。

很多更改只需要使用上述三个命令即可完成,不需要复制粘贴提交ID或查找正确的--fixup目标。

来源:https://github.com/ohmu/git-crust


2
如果您想压缩最后两次提交,则必须调用以下命令:
git rebase --interactive <3rd last commit>

然后,您需要选择最后一次提交并压缩倒数第二次提交。您无法压缩历史记录中的最顶部提交。


0

我认为问题的根源在于git(以及版本控制一般)强制你按照变更序列思考,但是一个变更集或特性分支或者无论你称之为什么的相关变更的凝聚组通常不是逻辑上的顺序。代码编写的顺序是偶然的,不一定与阅读代码的顺序有关。

我没有解决方案,但我已经编写了一个Perl脚本来帮助自动化重写历史的过程。它类似于@MikaEloranta的Python脚本,当时我还没有看到它。

commit --fixuprebase --autosquash很棒,但它们做得不够。当我有一个提交序列A-B-C并且我在我的工作树中编写了更多的变更,这些变更属于其中一个或多个现有的提交时,我必须手动查看历史记录,决定哪些变更属于哪些提交,将它们暂存并创建fixup!提交。但是git已经可以访问足够的信息来代替我完成所有这些操作。

对于git diff中的每个块,脚本使用git blame查找最后一次触及相关行的提交,并调用git commit --fixup编写适当的fixup!提交,从根本上做我之前手动完成的相同工作。

如果脚本无法将块解析为单个、明确的提交,则会将其报告为失败的块,您将不得不回到手动方法。如果您在两个不同的提交中两次更改了一行,则脚本将把该行的更改解析为这些提交中最近的一个,这可能并不总是正确的解决方案。在“正常形式”的特性分支中,您不应该在两个不同的提交中两次更改一行,每个提交应该呈现它所涉及的行的最终版本,以帮助审阅者。但是,在修复错误的分支中可能会发生这种情况,举个例子,行foo(bar());可能会被提交A(将foo重命名为fox)和提交B(将bar重命名为baz)所触及。

如果您觉得这个脚本有用,请随意改进和迭代它,也许有一天我们会在git中得到这样的功能。我很想看到一个工具,当交互式变基引入合并冲突时,它能够理解如何解决它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接