【问题标题】:Empty commits removed after interactive rebase, even though --keep-empty is used交互式 rebase 后删除空提交,即使使用了 --keep-empty
【发布时间】:2017-08-15 11:06:34
【问题描述】:

我在使用git rebase--keep-empty 选项时遇到了一些问题,我是 不知道我是否误解了这个选项的作用,或者有一个错误。

这是一个最小的例子:

设置

  1. 创建一个新的 Git 存储库和一个初始的、不相关的提交。

    $ git init
    $ echo something >base.txt
    $ git add base.txt
    $ git commit -m 'some base commit to not run into the root corner case'
    
  2. 创建一个添加两个新文件的新提交。

    $ echo A >a.txt; echo B >b.txt
    $ git add a.txt b.txt
    $ git commit -m 'add A and B'
    
  3. 修改其中一个文件。

    $ echo A1 >a.txt
    $ git add a.txt
    $ git commit -m 'change A'
    
  4. 修改其他文件。

    $ echo B1 >b.txt
    $ git add b.txt
    $ git commit -m 'change B'
    

变基

$ git checkout -b rebased master
$ git rebase --keep-empty -i :/base

... 选择edit 添加AB 的提交,并将其更改为仅添加B(在实际情况下,原因可能是A 是机密的):

$ git rm a.txt
$ git commit --amend
$ git rebase --continue

当然,现在修改A 的下一次提交会产生冲突:

error: could not apply 182aaa1... change A

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
Could not apply 182aaa1701ad100fc02a5d5500cacebdd317a24b... change A

…选择不添加修改版a.txt

$ git mergetool
Merging:
a.txt

Deleted merge conflict for 'a.txt':
  {local}: deleted
  {remote}: modified file
Use (m)odified or (d)eleted file, or (a)bort? d

修改A 的提交现在为空:

$ git diff --cached
# nothing

… 并完成变基:

$ git rebase --continue
Successfully rebased and updated refs/heads/rebased.

问题

所以现在我有两个版本的历史记录,不同的是其中一个没有A 的踪迹。然而,因为我选择了--keep-empty 选项,我仍然希望rebased 中存在一个空提交,这表明A 会被修改,如果它在那里。

但显然情况并非如此:

$ git log --oneline master
f893569 change B
182aaa1 change A
3340b71 add A and B
38cb5da some base commit to not run into the root corner case

$ git log --oneline rebased
73a2c05 change B
55f502b add A and B
38cb5da some base commit to not run into the root corner case

这不是--keep-empty 应该做的,还是不起作用 正确吗?


相关:Rebase on the root and keep empty commits 是一个非常相似的问题,但它涉及我在这里明确避免的--root 极端情况。它没有答案,只有一些 cmets 表明我在这里展示的内容应该有效。另一个区别是,在另一个问题中,提交首先是空的,而这里只有在解决冲突后才变为空。

【问题讨论】:

    标签: git git-rebase


    【解决方案1】:

    这是一个错误,由于某种功能。 :-)

    当您运行交互式 rebase 并且它“暂停”时,实际上,它完成,但留下一些文件让 new git rebase 意识到它更多毕竟是延续。就目前而言,这很好;你需要稍后运行git rebase --continue 来启动一个新的 rebase 并告诉它:你不是真正的新人,去阅读状态并表现得像你正在继续原来的 rebase。

    还有,让我们看一下“交互式变基”。实际上,这主要是一系列精选操作:pick 命令实际上指示旧的变基 shell 脚本(现在正在逐步淘汰)运行 git cherry-pick

    好的,到目前为止没什么大不了的。但是让我们考虑为什么交互式变基停止。有两个原因:

    1. 您将提交标记为“编辑”。它实际上提交了选择,并停止让您修改提交或以其他方式大惊小怪。

    2. 或者,有一个问题(例如合并冲突)迫使停止。

    在情况 (1) 中,当您运行 git rebase --continue 时,Git 不应该进行自己的提交。

    在情况 (2) 中,当您运行 git rebase --continue 时,Git 应该 进行自己的提交。也就是说,它应该除非——这是特性部分——你首先做出自己的提交。在这种情况下,对于情况 (2),Git 应该进行自己的提交。

    Git 可以而且或许应该记录停机原因,以便区分这两种情况……但事实并非如此。相反,它只查看--continue 上的状态。

    对于-交互式 rebase,Git 知道它只会在发生冲突时停止,因此它知道尝试进行提交,并在没有任何内容可提交时抱怨。这就是--keep-empty-k 标志有用的地方。 (在内部,默认情况下,非交互式案例使用 git format-patchgit am,尽管您可以强制它使用带有 --preserve-merges 的交互式机制。我在这里提到这一点是因为它是一个实现原因 em> Git 必须知道你是否正在“交互”:正如经常发生的那样,这里 Git 让实现决定行为。如果 Git 不需要这种区别,--continue 可以使用相同的代码交互式和非交互式 rebase,但 Git 确实需要区分,因此不使用相同的代码。)

    不过,对于交互式 rebase,Git 允许您在运行 git rebase --continue(这是功能部分)之前在情况 (2) 中进行自己的提交。如果是这样,--continue 步骤应该继续进行下一次提交。所以--continue 只是检查现在是否有东西要提交,而不是早期的交互式 rebase 是否退出了 case (1) vs case (2)。这个简单的实现技巧启用了该功能,但也意味着--keep-empty 不能在这里工作:Git 只是不知道其中的区别。

    解决方法是在解决合并后执行您自己的git commit --allow-empty。换句话说,将案例 (2) 转换为模拟案例 (1),使用“您可以自己提交”功能。

    【讨论】:

    • 太棒了,谢谢!这也解释了为什么 Git 实际上没有抱怨空提交,当我在最初的尝试中没有使用 --keep-empty 时,这让我有点惊讶。
    • 倒数第二段的第一句你把(1)和(2)搞混了吗?
    • 嗯,我认为我没有这样做,案例 (2) 是“由于合并冲突而停止”,这就是您在解决冲突后被允许但不要求提交的地方。
    • 这句话当然是正确的,但我认为你想区分交互式和非交互式 rebase(给定句子的开头),而 case (1) 是它们不同的地方。但是好吧,如果这句话是你想要的,我只是想确保它没有错别字。
    【解决方案2】:

    但是,因为我选择了 --keep-empty 选项,我仍然希望在 rebase 中存在一个空提交,这表明如果 A 存在的话,它会被修改。

    但显然情况并非如此:

    使用 Git 2.18(2018 年第二季度)仔细检查,考虑到如果另一方包含空提交(由于“does an equivalent patch exist already?”检查),“git rebase --keep-empty”仍然删除了一个空提交(由于“does an equivalent patch exist already?”检查),已更正

    参见Phillip Wood (phillipwood)commit 3d94616commit 76ea235commit bb2ac4f(2018 年 3 月 20 日)。
    (由 Junio C Hamano -- gitster -- 合并于 commit d892bee,2018 年 4 月 25 日)

    rebase -i --keep-empty: 不要修剪空提交

    如果$upstream...HEAD 的左侧有空提交,则我们要保留的右侧的空提交将被--cherry-pick 修剪。
    通过使用--cherry-mark 而不是--cherry-pick 并保持为空或未标记为cherry-picks 的提交来解决此问题。

    还有:

    rebase --keep-empty: 总是使用交互式变基

    rebase --merge 接受 --keep-empty 但只是忽略它,通过使用 隐式交互式 rebase 用户仍然获得重命名检测 基于合并的变基,但支持--keep-empty

    如果 rebase --keep-empty 没有 --interactive--merge 停止 用户解决合并冲突然后'git rebase --continue'将 失败。这是因为它使用了不同的代码路径, 创建$git_dir/rebase-apply.
    由于 rebase --keep-empty 是使用 cherry-pick 实现的,因此它从未支持 am 选项,现在交互式 rebase 支持 --signoff 使用隐式交互式 rebase 不会丢失功能。


    注意:这是在 Git 2.18 中添加到 git rebase 的更大功能的一部分:
    请参阅“What exactly does Git's “rebase --preserve-merges” do (and why?)”。
    使用git --rebase-merges(最终将取代旧的git --preserve-merges),您现在可以在其他地方重新定位提交图的整个拓扑。


    使用 Git 2.27(2020 年第二季度),“git rebase”(再次)学会尊重“--no-keep-empty”,这让用户可以丢弃从一开始就为空的提交(而不是那些变成空的提交)因为变基)。

    交互式变基还会在todo 中标记empty 的提交。

    参见Elijah Newren (newren)commit 50ed761commit b9cbd29commit 1b5735f(2020 年 4 月 11 日)。
    (由 Junio C Hamano -- gitster -- 合并于 commit c7d8f69,2020 年 4 月 22 日)

    rebase: 恢复 --no-keep-empty

    ​​>

    报告人:Bryan Turner
    报告人:Sami Boukortt
    签字人:Elijah Newren

    Commit d48e5e21da ("rebase (interactive-backend): make --keep-empty the default", 2020-02-15, Git v2.26.0-rc0 -- merge 列在batch #8) 转--keep-empty(用于保持以空开头的提交)为默认值。

    支持该提交的逻辑是:

    1. 'git commit' 在创建没有覆盖标志的空提交时出错
    2. 一旦有人确定重写是值得的,要求他们采取额外步骤以保留此类提交(并在每次变基时重复此类步骤)是令人讨厌和/或有害的。

    虽然做出决定的逻辑是合理的,但结果有点过度纠正。

    它没有跳到让--keep-empty 成为默认行为,而是跳到使--keep-empty 成为唯一可用的行为。

    不过,有一个简单的解决方法,当时被认为已经足够好了。

    人们仍然可以删除开始为空的提交,就像删除任何提交一样:通过触发交互式 rebase 并从列表中挑选出他们不想要的提交。

    但是,在某些情况下,外部工具可能会创建足够多的空提交,因此将它们全部剔除是很痛苦的。

    因此,有一个标志来自动删除 start-empty 提交可能是有益的。

    使用存在多年的标志为用户提供一种方法来删除开始为空的提交:--no-keep-empty

    --keep-empty 解释为取消任何以前的--no-keep-empty,否则将--keep-empty 保留为默认值。

    这可能会导致一些轻微的奇怪,因为像这样的命令:

    git rebase --empty=drop --keep-empty
    git rebase --empty=keep --no-keep-empty
    

    尽管很有意义,但看起来确实很奇怪(第一个将删除变为空的提交,但保留开始为空的提交;第二个将保留变为空的提交,但删除开始为空的提交)。

    但是,--no-keep-empty 是多年前命名的,我们主要保留它是为了向后兼容;我们还怀疑它只会很少使用,因为人们已经有了一种简单的方法来通过交互式 rebase 删除他们不想要的提交。

    【讨论】:

      【解决方案3】:

      我也遇到过这个问题。

      • 3328dbe - 为 jpa 添加 hello world 测试代码 — Minghui Ma (HEAD) -(14 分钟前)
      • 3fd2d95 - 初始化空提交

      当我使用 cmd " git rebase -i --root --keep-empty" 没有提交“3fd2d95 - 初始化空提交”

      所以我强制在第一行插入一行“pick 3fd2d95 init empty commit”。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-22
        • 2023-02-08
        • 2011-09-23
        • 1970-01-01
        • 2017-08-19
        • 2014-07-14
        相关资源
        最近更新 更多