【问题标题】:Can somebody explain the usage of git range-diff?有人可以解释 git range-diff 的用法吗?
【发布时间】:2018-09-13 23:23:36
【问题描述】:

Git 2.19 版引入了git range-diff,它应该用于比较两个提交范围。我一直在阅读文档,但我不明白这个新功能的目的是什么。

我查了官方Git documentation,但我无法理解它的语法(省略标志):

git range-diff ( <range1> <range2> | <rev1>...​<rev2> | <base> <rev1> <rev2> )

什么是rev1rev2

有人能解释一下它们什么时候有用吗?我指的是每种情况?

【问题讨论】:

  • 注意:对于 Git 2.31(2021 年第一季度),git range diff 带有新的 --(left|right)-only 选项。见my edited answer below

标签: git


【解决方案1】:

我还没有真正使用它们,但它们是对旧的git cherry* 流程的改进,用于分析/比较一些上游或下游变更集与您现在拥有的变更集。为了使范围集有用,我们需要一些“这是我的提交”和“这是他们的”,尽可能简单地表达。

一个 range1 range2 集合可以写成,例如:

git range-diff theirs~5..theirs ours~4..ours

如果你有,例如:

          T1--T2--T3--T4--T5   <-- theirs
         /
...--o--*   <-- base
         \
          O1--O2--O3--O4   <-- ours

O 提交是“我们的”,T 提交是“他们的”。

然而,鉴于这个完全相同的配置,我们也可以这样写:

git range-diff theirs...ours    # or ours...theirs

(注意三个点)。 (例如,这是与 git rev-list --cherry-mark --left-right 一起使用的语法。)

或者,同样的情况,我们可以这样写:

git range-diff base theirs ours   # or base ours theirs

这里base是他们和我们的停止点,避免了倒数5。

如果情况更复杂——如图所示:

          X1--T1--T2--T3   <-- theirs
         /
...--o--*   <-- base
         \
          Y1--Y2--O1--O2--O3--O4   <-- ours

三点和base ours theirs 类型的语法都不太有效,因此最好使用两组范围 (theirs~3..theirs ours~4..ours)。

【讨论】:

    【解决方案2】:

    范围差异在解决合并冲突(在rebasecherry-pick 等之后)之后非常有用,尤其是当您有多个冲突提交时,并且您要确保在此过程中没有意外损坏某些东西。

    如果您在一个分支中进行多次提交,通常会发生这种情况。
    假设我们有一个名为“our”的分支,我们的分支位于 master 分支的后面:

    m1-m2-m3-m4  <- "master" branch
      \
       o1-o2-o3  <- "our" current branch
    

    在变基之前,我们对我们的分支进行备份(只需复制一个名为“our_bkp”的分支)

    git branch our_bkp
    

    然后我们开始使用 master 进行 rebase

    git rebase master
    

    并解决提交“o1”上的一些合并冲突...
    注意:如果“o1”上的冲突文件也在“o2”中使用/更改,或者"o3",
    那么我们也必须重新解决它们的相同合并冲突。

    现在,让我们说在一个令人筋疲力尽的变基过程之后,我们有这样的事情:

                 _<- branch "master"
                /
     m1-m2-m3-m4-o1'-o2'-o3'  <- branch "our" (after rebase)
      \
       o1-o2-o3   <- branch "our_bkp"
       
    

    由于有很多合并冲突,我们是否遗漏了一些东西并不清楚。

    这就是范围差异的亮点。

    为了确保我们没有错过任何更改或意外损坏任何东西,我们可以简单地 比较旧版本分支与新版本的提交:

    git range-diff our_bkp~3..our_bkp our~3..our
    

    git range-diff o1..o3 o1'..o3'
    

    如果差异仅与冲突的部分有关,那么我们很好,除了它们之外没有更改任何内容。
    但如果我们看到其他一些意想不到的差异,那么我们或 git 做错了什么,我们需要修复它们。

    备注

    • 上面的两个 range-diff 命令的作用完全相同。
    • o1..o3是指那些提交的数量,例如:0277a5883d132bebdb34e35ee228f4382dd2bb7..e415aee3fa53a213dc53ca6a7944301066b72f24
    • our_bkp~3 中的 ~3 表示 git 在 our_bkp 分支上的最后一个提交之前接受 3 提交的提交。将数字替换为您在分支上的提交数量,当然不要忘记将分支名称 our_bkp 替换为您的备份分支的名称。
    • 您可以将 range-diff 视为两个 diff-s 的差异。这样就更容易记住和理解它在做什么。

    【讨论】:

      【解决方案3】:

      Git 用语中的“范围”是一对修订标识符(开始和结束)。

      git range-diff 的第一种用法是&lt;range1&gt; &lt;range2&gt;。因为我们知道范围是一对修订标识符,所以一些可能的例子是:

      abc1234..def5678 9876foo..5432bar
      HEAD..def5678 my_release_1_1..my_release_1_2
      

      当四个修订标识符中的一些彼此相同时,其他两种使用形式是为了方便。即:

      1. 对于abc..def def..abc 这样的情况,您可以简单地指定def...abc
      2. 对于像abc..def abc..xyz 这样的情况,您可以指定abc def xyz。这对我来说似乎很常见:您想比较两个从同一点开始的范围。

      【讨论】:

      • 错字:在1 下,您需要三个点来表示更简单的版本。
      • @torek:谢谢,已修复。你知道吗,既然你有 167k 代表,你可以简单地添加缺少的点而不是评论它?
      • 我更喜欢让其他人编辑自己的答案,尤其是对于任何潜在的技术问题。例如,也许您对git range-diff 的范围参数有所了解,但我不知道 :)
      【解决方案4】:

      您可以看到 here comparing two patches 的命令 git range-diff 已在 Git 2.23(2019 年第三季度)中进行了重新访问,以便更轻松地识别所显示的补丁程序的哪个部分。

      请参阅commit 499352ccommit 444e096commit b66885acommit 430be36commit e1db263commit 44b67cbcommit 1ca6922commit ef283b3commit ef283b3commit 80e1841(2019 年 7 月 11 日)和@98432 commit 570fe99commit 85c3713commit d6c88c4commit 5af4087(2019 年 7 月 8 日)Thomas Gummerer (tgummerer)
      (由 Junio C Hamano -- gitster -- 合并于 commit 43ba21c,2019 年 7 月 25 日)

      range-diff: 将文件名添加到内部差异

      在范围差异中,并不总是清楚哪个文件是某个funcname 内部差异属于,因为差异标头(或先前提交中添加的部分标头)在范围差异中并不总是可见。

      将文件名添加到内部 diffs 标头中,因此它始终可见 用户。

      这也允许我们将文件名+函数名添加到外部 使用自定义 userdiff 模式区分大块标头,这将完成 在下一次提交中。

      range-diff: 给外部大块头添加头

      将我们在之前的提交中引入的section headers/hunk headers添加到外部diff的hunk headers。
      这使我们更容易理解我们实际正在查看的更改。例如,外部大块标头现在可能如下所示:

      @@  Documentation/config/interactive.txt
      

      虽然以前只是

      @@
      

      这并没有为接下来的变化提供很多背景。

      t3206-range-diff.sh 为例。

      还有:

      range-diff: 添加section header而不是diff header

      目前 range-diff 保持内部 diff 的 diff 标头完好无损(除了以索引开头的剥离线)。
      这个 diff 标头有点用处,尤其是当文件变得不同时 不同范围的名称。

      但是,没有必要为此保留整个 diff 标头。
      我们目前这样做的主要原因可能是因为它很容易做到。

      引入一个新的范围 diff hunk 标头,由“##”括起来,类似于 diff hunk 中的行号如何由“@@”括起来,并提供文件究竟发生了什么的人类可读信息,包括文件名。

      这通过提供更简洁的内容来提高范围差异的可读性 向用户提供信息。
      例如,如果一个文件在一次迭代中被重命名,但在另一次迭代中没有被重命名,那么标头的差异就会非常嘈杂。
      但是单行的差异比较简洁,应该更容易理解。

      同样,t3206-range-diff.sh 提供了一个示例:

          git range-diff --no-color --submodule=log topic...renamed-file >actual &&
        sed s/Z/\ /g >expected <<-EOF &&
        1:  4de457d = 1:  f258d75 s/5/A/
        2:  fccce22 ! 2:  017b62d s/4/A/
            @@ Metadata
            ZAuthor: Thomas Rast <trast@inf.ethz.ch>
            Z
            Z ## Commit message ##
            -    s/4/A/
            +    s/4/A/ + rename file
            Z
            - ## file ##
            + ## file => renamed-file ##
            Z@@
            Z 1
            Z 2
      

      但是:请注意 diff.noprefix 配置设置:git range-diff 会在 2.24(2019 年第四季度)之前与 Git 发生段错误!

      git range-diff”在使用diff.noprefix 配置时出现段错误,因为它盲目地期望它内部生成的补丁具有标准的a/b/ 前缀。
      该命令现在强制构建没有任何前缀的内部补丁,不受任何最终用户配置的影响。

      参见Johannes Schindelin (dscho)commit 937b76e(2019 年 10 月 2 日)。
      (由 Junio C Hamano -- gitster -- 合并到 commit 159cdab,2019 年 10 月 11 日)

      范围差异:内部强制diff.noprefix=true

      在解析差异时,range-diff 期望在差异标头中看到前缀 a/b/

      这些前缀可以通过配置设置diff.noprefix=true强制关闭。
      由于range-diff 没有为这种情况做好准备,这将导致分段错误。

      让我们通过将--no-prefix 选项传递给生成range-diff 想要解析的差异的git log 进程来避免这种情况。
      然后当然期望输出没有前缀。


      而“git range-diff”未能处理仅模式更改,这已被 使用 Git 2.24(2019 年第四季度)更正:

      参见Thomas Gummerer (tgummerer)commit 2b6a9b1(2019 年 10 月 8 日)。
      (由 Junio C Hamano -- gitster -- 合并到 commit b6d712f,2019 年 10 月 15 日)

      range-diff: 仅模式更改时不要出现段错误

      报告人:Uwe Kleine-König
      签字人:Thomas Gummerer
      签字人:Johannes Schindelin

      ef283b3699 ("apply: make parse_git_diff_header public", 2019-07-11, Git v2.23.0-rc0 -- merge 列在batch #7) 中的'parse_git_diff_header' 函数被apply.c之外的调用者公开和使用。

      但是遗漏了它(当时)唯一的调用者“find_header”进行了一些错误处理,并适当地完成了“struct patch”。

      range-diff 然后开始使用这个函数,并尝试自己适当地处理这个问题,但在某些情况下失败了。

      当范围内仅模式更改时,这反过来会导致range-diff segfaulting

      将结构的错误处理和完成移动到'parse_git_diff_header'函数中,以便其他调用者可以利用它。

      这修复了“git range-diff”中的段错误。


      使用 Git 2.25(2020 年第一季度),“git range-diff”学会了采用“--notes=&lt;ref&gt;”和“--no-notes”选项来控制包含在比较的日志消息中的提交注释。

      请参阅commit 5b583e6commit bd36191commit 9f726e1commit 3bdbdfbcommit 75c5aa0commit 79f3950commit 3a6e48ecommit 26d9485(2019 年 11 月 20 日)和commit 9d45ac4、@987 2019 年 11 月 19 日)Denton Liu (Denton-L)
      (由 Junio C Hamano -- gitster -- 合并,commit f3c7bfd,2019 年 12 月 5 日)

      range-diff:输出## Notes ##标头

      签字人:Denton Liu

      当注释包含在range-diff 的输出中时,它们只是与提交消息的其余部分混合在一起。结果,用户将无法清楚地区分提交消息的结束位置和注释的开始位置。

      检测到笔记时输出## Notes ## header,以便更清楚地比较笔记。

      请注意,我们也使用此代码处理 Notes (&lt;ref&gt;): -&gt; ## Notes (&lt;ref&gt;) ## 的大小写。但是,我们无法在此补丁中对此进行测试,因为目前无法将不同的注释引用传递给git log。这将在未来的补丁中修复。

      还有:

      参见Denton Liu (Denton-L)commit abcf857commit f867534commit 828765d(2019 年 12 月 6 日)。
      (由 Junio C Hamano -- gitster -- 合并于 commit d1c0fe8,2019 年 12 月 16 日)

      range-diff:在函数结束时清除 other_arg

      签字人:Denton Liu

      我们在使用完 other_arg 后没有清除它,导致内存泄漏。
      用完后清除。

      请注意,这不是绝对必要的,因为一旦命令退出,内存将被回收。
      但是,由于我们发布了strbufs,我们还应该清除other_arg 以保持一致性。


      使用 Git 2.27(2020 年第二季度),“git range-diff”更加健壮。

      参见commit 8d1675ecommit 8cf5156(2020 年 4 月 15 日)Vasil Dimov (vasild)
      (由 Junio C Hamano -- gitster -- 合并到 commit 93d1f19,2020 年 4 月 28 日)

      range-diff:修复解析git-log输出时的崩溃

      签字人:Vasil Dimov

      git range-diff 在内部调用git log 并尝试解析其输出。

      但是git log 的输出可以由用户在他们的git config 中自定义,并且对于某些配置,git range-diff 将返回一个错误或者它会崩溃。

      要解决此问题,请将内部执行的git log 的输出格式设置为--pretty=medium
      因为这取消了--notes,所以在末尾明确添加--notes

      另外,请确保我们永远不会以同样的方式崩溃 - 尝试取消引用 util,它从未被创建并保持为 NULL
      如果git log 输出的第一行不以'commit'开头,就会发生这种情况。

      考虑过但放弃的替代方案 - 以某种方式禁用所有 git 配置并表现得好像内部执行的 git log 中不存在任何配置,但这似乎是不可能的。
      GIT_CONFIG_NOSYSTEM 是最接近它的,但即便如此,我们仍然会阅读.git/config


      在 Git 2.31(2021 年第一季度)中,“git range-diff(man) 命令学习了 --(left|right)-only 选项仅显示比较范围的一侧.

      参见commit 1e79f97commit 3e6046ecommit f1ce6c1(2021 年 2 月 5 日)和 commit 5189bb8commit a2d474acommit 8c29b49(2021 年 2 月 4 日)Johannes Schindelin (dscho)
      (由Junio C Hamano -- gitster --commit dadc91f 合并,2021 年 2 月 17 日)

      range-diff: 提供--left-only/--right-only 选项

      签字人:约翰内斯·辛德林

      在比较提交范围时,人们通常只对一侧感兴趣,例如问“我提交到 Git 邮件列表的这个补丁是否已应用?”:人们只会关心输出中的那部分对应于本地分支中的提交。

      要实现这一点,请模仿 git rev-list(man) 选项 --left-only--right-only

      此地址gitgitgadget/git issue 206: "range-diff: 添加对--left-only--right-only 的支持"

      git range-diff 现在包含在其man page 中:

      --left-only

      抑制第一个指定范围内缺失的提交 (或使用&lt;rev1&gt;...&lt;rev2&gt; 格式时的“左范围”)。

      --right-only

      抑制第二个指定范围内缺失的提交 (或使用&lt;rev1&gt;...&lt;rev2&gt; 格式时的“正确范围”)。


      Git 2.34(2021 年第四季度)处理“git range-diff(man) 的极端情况,涉及未终止的行。

      参见Jeff King (peff)commit c4d5907commit 7c86d36commit 47ac23d(2021 年 8 月 9 日)。
      (由 Junio C Hamano -- gitster -- 合并于 commit fb0b14d,2021 年 8 月 30 日)

      range-diff:处理read_patches() 中未终止的行

      签字人:Jeff King
      签字人:Derrick Stolee

      当解析来自git-log 的输出缓冲区时,我们有一个find_end_of_line() 帮助器,它可以找到下一个换行符,并为我们提供要移过它的字节数,或者如果有,则提供整个剩余缓冲区的大小没有换行符。

      但是尝试处理这两种情况会导致一些奇怪的情况:

      • 我们尝试在调用者中用NUL 覆盖换行符,覆盖line[len-1]
        这充其量是多余的,因为如果助手看到换行符,它就已经这样做了。
      • 但如果它没有看到换行符,那就是错误的;我们将覆盖(未终止的)行末尾的字节。

      要解决这个问题,调用者需要知道我们是否真的找到了换行符。
      我们可以修改find_end_of_line() 以返回该信息,但我们可以进一步观察到它只有一个调用者。
      所以让我们把它内联到那个调用者中。

      似乎没有人注意到这种情况,可能是因为 'git-log'(man) 永远不会产生不以换行符结尾的输入。

      【讨论】:

      • 你为什么要在这个答案中保留更新日志?
      • @Xerus 跟踪git range-diff 命令的演变。
      • wiki(通过 git ^^ 公开)、emacs org-mode、idk 怎么样? SO是你的日记吗? ;)
      • @Xerus Stack Overflow 的所有想法就是它会一直存在。外部 wiki 可以来来去去。目标是在此处写入始终可用的永久信息。这是记录这些演变的正确地方,并将帮助其他开发人员。
      • 确保使用 Git 2.34:修复 segfault:github.com/git/git/commit/…
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-29
      • 2010-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多