git name-rev有什么作用?

时间:2018-10-05 13:39:53

标签: git

根据git name-rev doc

  

找到适合人类消化的符号名称   可以使用git rev-parse可以解析的任何格式。

但是我不明白这一点。该命令的用途是什么?与git describe命令有什么不同?我认为两者也做同样的事情-给出SHA1 ID,还给我们最近的引用名称吗?

3 个答案:

答案 0 :(得分:3)

Marek R's answer是正确的,但是有点不完整。

本质上,git name-rev带有一个名称和一些相关表达式(如果需要),可以从名称移到提交,而git describe带有一个名称-通常是标记名,但是可能还有其他名称-如果需要,还可以加上一些附加的字符串,这并不是特别的“相对”:在某些情况下有一个计数,但它没有git name-rev中的计数有用。如果git describe无法使用原始名称,则会添加g和缩写的哈希ID:

$ git describe
v2.19.1-272-gf84b9b09d4

但是:

$ git name-rev HEAD
HEAD master

如果我们在--all的参数中添加git describe,则git describe会使用分支名称,但是输出会有所不同:

$ git describe --all
heads/master

特别要特别注意的是,它使用的是分支名称,因为如果分支名称和标记名称之间存在冲突-如果也存在refs/tags/master-仅解析master会产生与标签相关的哈希ID,而不是分支名称。

除这些以外,如果工作树与提交不匹配,git describe可以添加-dirty,并且-自Git 2.16.0起-可以生成name:pathname字符串来为您提供表示特定存储的blob的表达式:

$ git describe HEAD:Makefile
v2.19.0-237-ge3d4ff037d:Makefile

git name-rev无法管理。

目的不同

  

[git name-rev的用途是什么?

我从未真正看到有人亲自使用过它,但请参阅下文。让我们从git describe的目的开始:git describe非常常用于生成特定构建的有用描述。由于默认情况下只使用 tags ,因此其输出相对稳定(嗯,如果我们假设标签永不更改)。当git describev2.19.1-272-gf84b9b09d4时,我们知道:

  • 提交发生在v2.19.1
  • 之后的某个时间
  • 实际提交的哈希ID以f84b9b09d4开头

和将来同一次提交的git describe输出可能是相同的(尽管如果添加新的带注释的标记,它们可能会更改)。通过v2.19.1..f84b9b09d4可达到的272个提交数:

$ git rev-list --count v2.19.1..f84b9b09d4
272

不会改变。只要标签git rev-parse v2.19.1-272-gf84b9b09d4保留在原处,运行f84b9b09d40408cf91bbc500d9f190a7866c3e0f始终会产生v2.19.1,无论我明天还是下周或明年再次这样做。如果我们使用此字符串作为内部版本标识符,并且避免使用分支名称,并且要注意该内部版本是否为“脏”,我们可以立即判断是否可以在以后轻松地再次重现该相同内部版本,如果是,如何(即通过运行git checkout v2.19.1-272-gf84b9b09d4)。

另一方面,当git name-revmastermaster~3或其他任何内容时,无法保证我明天可以回到该存储库并使用相同的表达式来查找相同的表达式承诺。明年,几乎可以肯定会错误。因此,git name-rev的输出只能使用一小段时间-基本上,直到您移动一些分支名称为止。

与此同时,git name-rev有一个git describe不具备的技巧:它将解析其标准输入,查找看起来像哈希ID的东西。如果找到它们,它将把它们复制到标准输出并添加 (expression),前提是哈希将转换为名称:

$ X=$(git rev-parse master)
$ echo embedded $X hash | git name-rev --stdin
embedded f84b9b09d40408cf91bbc500d9f190a7866c3e0f (master) hash
$ Y=$(echo $X | sed s/f/0/)
$ echo embedded invalid $Y hash | git name-rev --stdin
embedded invalid 084b9b09d40408cf91bbc500d9f190a7866c3e0f hash

这意味着,如文档中所示,您可以运行git log | git name-rev,并使所有这些提交哈希成为修饰后的哈希。但是,哈希必须是完整的哈希。比较:

$ git log --oneline -n 10 | git name-rev --stdin
f84b9b09d4 Sync with 2.19.1
cae598d998 Git 2.19.1
1958ad504b Sync with 2.18.1
268fbcd172 Git 2.18.1
44f87dac99 Sync with 2.17.2
6e9e91e9ca Git 2.17.2
1a7fd1fb29 fsck: detect submodule paths starting with dash
a124133e1e fsck: detect submodule urls starting with dash
e43aab778c Sync with 2.16.5
27d05d1a1a Git 2.16.5

具有:

$ git log --pretty=tformat:'%H %s' -n 10 | git name-rev --stdin
f84b9b09d40408cf91bbc500d9f190a7866c3e0f (master) Sync with 2.19.1
cae598d9980661a978e2df4fb338518f7bf09572 (tags/v2.19.1^0) Git 2.19.1
1958ad504befa6a09c475cc8ab9de43b359de137 (tags/v2.19.1~1) Sync with 2.18.1
268fbcd172cdb306e8a3e7143cc16677c963d6cd (tags/v2.18.1^0) Git 2.18.1
44f87dac99574a8073ffb1ba8b10bd4d3945f61b (tags/v2.18.1~1) Sync with 2.17.2
6e9e91e9cae74cd7feb9300563d40361b2b17dd2 (tags/v2.17.2^0) Git 2.17.2
1a7fd1fb2998002da6e9ff2ee46e1bdd25ee8404 (tags/v2.17.2~1) fsck: detect submodule paths starting with dash
a124133e1e6ab5c7a9fef6d0e6bcb084e3455b46 (tags/v2.17.2~2) fsck: detect submodule urls starting with dash
e43aab778c72250e11eb00e31dc6be90072a1637 (tags/v2.17.2~3) Sync with 2.16.5
27d05d1a1a62273aa3749f4d0ab8a126ef11ff66 (tags/v2.16.5^0) Git 2.16.5

底线:使用最适合您特定目的的方法。哪个取决于您的目的。

答案 1 :(得分:0)

文档非常清楚。

它查找/生成仅使用人类可读的符号(例如分支名称和标记名称)引用给定提交的表达式。

例如在我的存储库中:

$ git name-rev 91faf1b82d2dcedf8098a6e571ef379b29a44f51
91faf1b82d2dcedf8098a6e571ef379b29a44f51 develop^2~2

我提供的提交没有被分支直接标记或指向。 如您所见,命令发现该提交包含在分支develop中。要从develop获得此提交,您必须使用第二个父级和两个提交。

我建议:只需尝试在不同的提交上使用此命令并查看结果即可。

答案 2 :(得分:0)

在Git 2.25(2020年第一季度)中,您可以看到“ git name-rev”功能的图示。

请参见commit 2866fd2commit 49f7a2fcommit fee984b(2019年12月9日)和commit 8c5724ccommit 3a52150commit dd432a6,{{3} },commit dd090a8commit 766f9e3commit d59fc83commit bf43abccommit e0c4da6commit c593a26(2019年11月12日)由commit d91ce88发布。< br /> 请参见SZEDER Gábor (szeder)commit c3794d4(2019年11月12日)。
(由René Scharfe (``)Junio C Hamano -- gitster --中合并,2019年12月25日)

  

commit f3c520e:添加测试以覆盖'git name-rev's name_rev()

中的内部条件      

签名人:SZEDERGábor

     

name_rev()函数的't6120'中,有一个循环遍历给定提交的所有父级,并且循环体看起来像这样:

if (parent_number > 1) {
    if (generation > 0)
        // branch #1
        new_name = ...
    else
        // branch #2
        new_name = ...
    name_rev(parent, new_name, ...);
} else {
    // branch #3
    name_rev(...);
}
     

这些条件未在测试套件中正确涵盖。

     

就纯粹的测试覆盖范围而言,它们都在“ builtin/name-rev.c”中执行了多次。

     

但是,它们不会直接影响命令的输出,因为该测试脚本中使用的存储库包含多个分支和标记,这些分支和标记指向落实DAG的中间位置,因此可以为后一个命名的提交。

     

这可以隐藏错误:例如通过将第一个递归new_name调用中的'name_rev()'参数替换为'tip_name'(有效地使分支#1#2变为无操作)'{ {1}}'在Git存储库中显示了数千个伪造的名称,但是整个测试套件仍然成功通过。

     

在本系列的更高版本的早期版本中,我设法弄乱了所有三个分支(一次!),但是测试套件仍然通过了。

     

因此,添加一个可在以下历史记录上运行的新测试用例:

git name-rev --all
     

并命名提交'A--------------master \ / \----------M2 \ / \---M1-C \ / B '以确保所有三个分支对于确定'B的名称都是至关重要的:

     
      
  • 只有一个裁判,因此所有名称都基于“ master”,而不受其他裁判的不希望的干扰。

  •   
  • 每次B都在合并提交的第二个父级之后,它将“ name_rev()”附加到名称之后。在开始时紧跟着“主人”的第二个父代,可确保从“ ^2”到“ master”的祖先路径上的所有提交都具有与原始“ B”不同的基本名称。第一次tip_name调用。
      当前,尽管name_rev()是递归的,但这无关紧要,但是在本系列的后续文章中消除了递归之后,有必要适当覆盖所有三个分支。

  •   
  • 在'name_rev()的第二个父项之后,确保分支#2(即,当'M2'时)影响'generation = 0的名称。

    < / li>   
  • 在非合并提交'B'的唯一父项之后,确保分支#3影响'C的名称,并增加'B'

  •   
  • 来自'generation'的'generation'为1,因此紧随'C的第二个父代之后,确保分支#1影响'M1的名称。

  •   

在Git 2.25(2020年第一季度)中,“ B”的目标是避免递归调用。

  

t6120-describe.sh:消除git name-rev中的递归

     

签名人:SZEDERGábor

     

name_rev()函数针对作为参数获得的提交的每个有趣的父级递归地调用自身,因此,如果处理较深的历史记录会耗尽可用的堆栈空间,则它可能会段错误 >。

     

例如在gcc,gecko-dev,llvm和WebKit存储库中运行'git name-rev --all'和'name-rev HEAD〜100000'会导致我的计算机上出现段错误('name_rev()'报告为8192kB堆栈大小限制),如今在Linux存储库中也出现了以前的段错误(有时在v5.3-rc4和-rc5之间达到了必要的深度)。

     

通过将有趣的父代插入到LIFO'ulimit -s'git name-rev中并进行迭代直到队列为空,来消除递归。

     

请注意,必须将父提交以相反的顺序添加到LIFO'prio_queue',以便在处理过程中保留它们的相对顺序,即第一个父提交应该首先从队列中出来,否则会大大提高性能受累于能源历史1

     

prio_queue”中的stacksize-limited测试“ name-rev works in a deep repo”展示了此问题和预期的失败。

     

现在递归已消失,因此请翻转以期望成功。

     

在测试套件的每次执行中,t6120-describe.sh条目也记录了该段错误“ dmesg”进程的段错误。

     

请注意,这会稍微改变“ git name-rev”输出中的行顺序,通常在2中每35行或git.git中每150行交换两行。

     

实际上这无关紧要,因为无论如何输出始终是无序的。

     

最好使用'git name-rev --all'查看此补丁。

     

linux.git此修补程序的早期版本使用'--ignore-all-space',导致'1'中'commit_list'的性能损失约为15%,这可能是由于每次插入和移除时的内存分配和释放。
  使用LIFO'git name-rev --all'对性能几乎没有影响。

     

linux.git我们更喜欢使用较短的名称,即“ prio_queue”比“ v0.1~234”更可取,这意味着通常在合并的第一个父项之后会为其祖先使用最佳名称。
  因此,当稍后我们遵循合并的其余父项并到达已命名的提交时,通常会发现我们无法为该提交赋予更好的名称,因此我们不必访问其任何一个祖先。

     

OTOH,如果我们要首先遵循合并的第v0.1^2~5个父代,那么其所有祖先的名称都将包含一个对应的'N'。
  这些不是这些提交的最佳名称,因此,当稍后在该合并的第一个父级之后到达一个已命名的提交时,则我们将不得不更新该提交的名称及其所有祖先的名称。 br />   因此,我们将不得不多次访问许多提交,导致速度显着下降。