我想跟踪添加和删除的数量排除移动了线路。因此,如果一次提交有 10 个添加、5 个删除和 3 个移动行,那么我有 7 个添加、2 个删除(不包括移动行)。 10 和 5 由以下代码给出。我需要生成 3(我只需要弱移动行检测,例如,在同一提交中同时从一个位置删除并添加到另一个位置的任何行)。
我正在使用以下命令来跟踪 git 存储库中重要文件的添加和删除次数。
git log --since=2014-08-01 --date=short --pretty=format:"%ad%x09" --numstat -- file.tex
这会产生以下结果,其中第一个数字是添加,第二个数字是删除。
2014-08-19
72 0 file.tex
2014-08-19
211 290 file.tex
...
我想添加第三列,称之为移动行。可以通过在每次提交的循环中执行以下操作来找到每次提交移动的行:
- Grep 更改以 + 或 - 开头的行
- 条带前导 + 或 -
sort
uniq -d
wc -l
有没有一种快速而优雅的方法来运行这个伪代码,或者我只需要转储和解析一堆完整的 git diff 来获得我需要的东西?
答案1
我在中编写了以下实现TXR语言。首先,我使用您的算法来确定移动计数。但是,我注意到它产生的结果没有用:例如,它在仅包含+
行的更改中识别“移动行”的正值,仅仅是因为某些+
行彼此重复。新算法在最后的注释中讨论。
完整的程序:
#!/usr/bin/env txr
@(bind option-spec
@(list (opt nil "since" :str
"Specifies the starting date (passed \
\ through to git); it is mandatory.")
(opt nil "help" :bool
"Prints this help text")))
@(bind parsed-opts @(getopts option-spec *args*))
@(if (or [parsed-opts "help"] (not [parsed-opts "since"])))
@ (output)
usage: @{self-path} --since=<date> -- git arguments
@ (end)
@ (do (opthelp option-spec)
(exit 0))
@(end)
@(do
(defun histogram (strings)
[group-reduce (hash :equal-based) identity (op succ @1) strings 0])
(defun moved (a b)
(let* ((hist-a (histogram a))
(hist-b (histogram b))
(isec [hash-isec hist-a hist-b min]))
[reduce-left + (hash-values isec) 0])))
@(next (open-command `git log --since=@[parsed-opts "since"] \
\ --date=short --pretty=format:"%H:%ad%x09" \
\ --numstat @{parsed-opts.out-args}`))
@(repeat)
@sha:@date@\t
@ (collect :gap 0)
@added@\t@removed@\t@rawpath
@ (next :string rawpath)
@ (cases)
@pro/{@before => @after}/@epi
@ (bind path `@pro/@after/@epi`)
@ (or)
@before => @after
@ (bind path after)
@ (or)
@ (bind path rawpath)
@ (end)
@ (next (open-command `git show -p @sha -- @path`))
@ (collect :vars ((+line nil) (-line nil)))
@ (cases)
+@{+line}
@ (or)
-@{-line}
@ (end)
@ (end)
@ (flatten -line +line)
@ (bind moved @(moved +line -line))
@ (end)
@ (output)
@date@\t
@ (repeat)
@added@\t@removed@\t@moved@\t@rawpath
@ (end)
@ (end)
@(end)
我将其放在一个名为的标记可执行文件中movedlines.txr
,示例用法是:
$ ./movedlines.txr --since=2017-01-01 path/to
该--since
选项为强制选项; thepath/to
是传递给 的可选参数git
。如果您未指定强制选项,或者指定了--help
,则程序将打印帮助摘要并退出。
笔记:
我稍微更改了示例命令的输出格式,
git
将 SHA 包含在日期左侧,并用冒号分隔:请参阅%H
.程序将其解析出来,然后可以使用 SHAgit show -p
对每个集合中的每个文件执行操作。当程序通过附加移动的列反刍输出的模拟时,SHA 被省略。git 的输出显示重命名是一个复杂的问题。语法分为三种情况,使用该
@(cases)
构造的情况可以清楚、轻松地解析这三种情况。如果重命名整个路径,则重命名为from => to
.如果只是重命名一些组件的话,那就是be/fore/{from => to}/after
。我不知道多个大括号语法是否出现在一个路径中;我没见过。我们必须修改这个语法并将其转换为普通路径,因为 git 不理解它。即我们必须转换be/fore/{from => to}/after
为be/fore/to/after
.也许有一个 git 选项可以让路径以这种方式输出而无需符号;我懒得去找。由于使用了
open-command
.为此,我们需要open-process
一个参数列表。该算法计算 diff
-
和+
line 的单独频率直方图(减去它们的前导-
或+
)。然后它计算这些集合的交集,这仅保留两个直方图中出现的那些直方图条目。交集的连接函数是min
。例如,假设线条abc
添加了 5 次并删除了 3 次。(min 3 5)
是3
,这就是移动的行数abc
。这通勤。如果删除 3 次abc
并添加 5 次,则意味着 3 次移动。这绝不是一种用于检测动作的完美算法。它有明显的缺陷,比如不检测线事实上的移动,但也会经历一个微小的空格变化,比如缩进。