git commit / rebase / squash的单个命令

时间:2015-09-03 01:00:27

标签: git

我经常执行以下模式:

  1. git commit -am "well formatted message"
  2. 做一个小改动(我忘记了)
  3. git commit -am "foo"
  4. git rebase -i(并压缩foo提交)
  5. 编辑提交消息,以删除" foo"
  6. 我可以使用单个命令或脚本执行步骤3到5吗?即提交所有并添加到先前的提交。

2 个答案:

答案 0 :(得分:5)

在我提供以下答案之前,请注意git commit有一个选项,--amend,(实际上):

  • 执行软重置以返回一个提交,然后
  • 执行您想要的提交

有效地用固定的一个替换提交。 (“有效”是因为它正确处理合并。)例如:

$ git commit -am "well formatted message"

现在提交图如下所示:

o - o - A    <-- HEAD=yourbranch

你意识到你忘记了什么,所以你修复它并使用:

$ git commit -a --amend --no-edit

--no-edit避免启动编辑器)。 Git有效地备份了一个提交并进行了新的提交A'

        A   [abandoned, can only be found in reflog]
      /
o - o - A'  <-- HEAD=yourbranch

这通常是您需要的工具。但是如果你做了第二次“好”的提交并且需要在历史上进一步修复某些东西,它就不起作用。为此,请继续阅读。

由于git版本为1.7.0(但是在1.8.4或更高版本中有一些修复,所以最好在1.8.4或更高版本中执行此操作),git rebase已经选择拼写--autosquashgit commit命令包含--fixup=--squash=个选项,git rebase有一个配置项rebase.autoSquash,可以打开--autosquash默认情况下,您不必每次都指定它。

这个 the git rebase documentation中描述,但它有点模糊:

   --autosquash, --no-autosquash
       When the commit log message begins with "squash! ..." (or "fixup!
       ..."), and there is a commit whose title begins with the same ...,
       automatically modify the todo list of rebase -i so that the commit
       marked for squashing comes right after the commit to be modified,
       and change the action of the moved commit from pick to squash (or
       fixup). Ignores subsequent "fixup! " or "squash! " after the first,
       in case you referred to an earlier fixup/squash with git commit
       --fixup/--squash.
       This option is only valid when the --interactive option is used.

它的工作方式非常简单,我认为一看到它的工作方式就更有意义了。交互式rebase脚本(它是一个shell脚本,如果你想查看详细信息,它可以很容易地看到它是如何做到的)首先收集提交列表,以及它们在提交消息中的主题行。这与你在git log --oneline输出中看到的内容完全相同(这里是git git树的一个片段):

88d5072 am --skip: revert changes introduced by failed 3way merge
19bf6c9 fsck: report errors if reflog entries point at invalid objects
d66ae59 fsck_handle_reflog_sha1(): new function

假设在88d5072之后,我们有一行:

1234567 fixup! fsck_handle_reflog_sha1(): new function

这将是d66ae59之后的提交(因为它在日志输出中更高);它的主题行与d66ae59的主题行完全匹配,除了前面的单词“fixup”(以及感叹号和空格)。

rebase脚本对此做什么是为了使提交稍微改变,以便fixup!提交在原始提交之后,并且通常在说pick,说fixup。然后,当它通过提交进行处理时,它会照常应用原始提交,并将修正应用为修正,就像您已在提交列表中移动提交并将pick更改为{{1}一样}。

简而言之,它只是为您自动编辑rebase“待办事项”列表。当交互式rebase编辑会话开始时,您将看到修正。

fixupgit commit一起使用时,git会使提交消息以正确的方式与--fixup=<...>一起使用。您必须在提交时执行此操作,而不是在rebase时执行此操作。

因此,假设您没有使用原始方案(--autosquash效果良好),而是执行此操作:

git commit --amend

这为您提供了三个提交系列:

... edit ...
git commit ... # make slightly flawed commit A
... edit ...
git commit ... # make good commit B
... realize that there's a flaw in A; edit ...
$ git commit -a --fixup=HEAD^

其中,提交o - o - A - B - C <-- HEAD=yourbranch 只是C的修正。如果您运行A,您会看到它具有git log --oneline喜欢的格式;现在,当您运行git rebase --autosquash时,您的TODO列表将包含:

git rebase -i --autosquash

你只需退出编辑器就可以得到你想要的东西。

答案 1 :(得分:1)

torek's answer详细解释了这一点,我想补充一条我每天使用的实用建议,以使其更加简单。

我使用了一个执行Torek所描述的别名 - 它添加了您对先前提交所做的任何更改,包括相同的提交消息。这是别名(在~/.bash_profile或类似内容中定义):

alias gcaa='git commit -a --amend -C HEAD'

在你的例子中,我会这样使用它:

  1. git commit -am "well formatted message"
  2. 做一个小改动(我忘记了)
  3. gcaa
  4. 在修改提交时保留通常的建议:只有在没有推送第一次提交时才这样做。 amend选项正在创建新提交,它正在更改历史记录。如果您已经推送了第一个提交然后修改它,它将更改存储库历史记录,您将不得不force下一次推送,对已经撤消您的第一次提交的repo的其他用户可能产生影响。记住这一点,你应该做得很好。