如何将更改的行与C代码的git存储库中的函数相关联?

Gre*_*con 11 c c++ regex git parsing

我试图从存储在git存储库中的多年历史中构建一个"热图",其中粒度单位是单独的函数.函数应该变得更热,因为它们更频繁地更改次数,并且更改非空行更多.

首先,我检查了输出

git log --patch -M --find-renames --find-copies-harder --function-context -- *.c
Run Code Online (Sandbox Code Playgroud)

我看过使用Hackage中的Language.C,但它似乎想要一个完整的翻译单元扩展标题和所有 - 而不是能够处理源片段.

--function-context选项是自1.7.8版以来的新选项.v1.7.9.4中的实现基础是一个正则表达式:

PATTERNS("cpp",
         /* Jump targets or access declarations */
         "!^[ \t]*[A-Za-z_][A-Za-z_0-9]*:.*$\n"
         /* C/++ functions/methods at top level */
         "^([A-Za-z_][A-Za-z_0-9]*([ \t*]+[A-Za-z_][A-Za-z_0-9]*([ \t]*::[ \t]*[^[:space:]]+)?){1,}[ \t]*\\([^;]*)$\n"
         /* compound type at top level */
         "^((struct|class|enum)[^;]*)$",
         /* -- */
         "[a-zA-Z_][a-zA-Z0-9_]*"
         "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
         "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
Run Code Online (Sandbox Code Playgroud)

这似乎可以很好地识别边界,但并不总是将函数作为差异块的第一行,例如,#include顶部的指令或包含多个函数定义的块.告诉diff为每个更改的函数发出单独的数据库的选项非常有用.

这不是安全关键,所以我可以忍受一些失误.这是否意味着我可能有Zawinski的"两个问题"

gbu*_*mer 2

我意识到这个建议有点离题,但它可能有助于澄清需求并对其进行排序。这适用于 C 或 C++ ...

使用编译器生成二进制块,而不是尝试查找作为函数的文本块并比较它们。具体来说,对于更改集中的每个 C/C++ 源文件,将其编译为对象。然后使用目标代码作为比较的基础。

这对您来说可能不可行,但 IIRC gcc 上有一个编译选项,以便每个函数都编译为生成的目标代码文件中的“独立块”。链接器可以将每个“块”拉入程序中。(现在已经很晚了,所以如果你对这个想法感兴趣的话,我会在早上查一下。)

因此,假设我们可以做到这一点,您将拥有由二进制代码块定义的许多函数,因此一个简单的“热度”比较是“任何函数的版本之间的代码长或多短?”

我还认为使用 objdump 重构函数的汇编器可能是实用的。我可能会在这个阶段使用一些正则表达式来修剪寄存器名称,以便寄存器分配的更改不会导致太多误报(更改)。

我什至可能尝试对函数体中的汇编指令进行排序,并对它们进行比较以获得两个函数实现之间“删除”与“添加”的模式。这将给出一种变化的度量,它几乎独立于布局,甚至在某种程度上独立于某些源的顺序。

因此,看看同一函数的两个替代实现(即来自不同的更改集)是否是相同的指令可能会很有趣:-)

这种方法也应该适用于 C++,因为所有名称都已被适当地修改,这应该保证比较相同的函数。

因此,正则表达式可能会非常简单:-)

假设所有这些都很简单,那么这种方法可能无法给您带来什么?

旁注:这个基本策略适用于任何以机器代码为目标的语言,以及 VM 指令集,如 Java VM 字节码、.NET CLR 代码等。