Git-如何查看方法/函数的更改历史?

所以我发现了如何查看文件的变更历史的问题,但是这个特定文件的变更历史是巨大的,我真的只对特定方法的变更感兴趣。那么有没有可能只看到这个特定方法的变更历史呢?

我知道这需要 git 来分析代码,而且对不同语言的分析会有所不同,但是方法/函数声明在大多数语言中看起来非常相似,所以我想也许有人已经实现了这个特性。

我当前使用的语言是 Objective-C,我当前使用的 SCM 是 git,但我想知道这个特性是否存在于任何 SCM/语言中。

42006 次浏览

Git fault 显示了最后一次更改文件中每一行的人; 您可以指定要检查的行,以避免获得函数之外的行的历史记录。

Git log 有一个选项‘-G’可用于查找所有差异。

- G 查找其添加或删除的行与 根据 <regex>

只要给它一个你所关心的函数名的正则表达式,例如,

$ git log --oneline -G'^int commit_tree'
40d52ff make commit_tree a library function
81b50f3 Move 'builtin-*' into a 'builtin/' subdirectory
7b9c0a6 git-commit-tree: make it usable from other builtins

使用 git gui blame很难在脚本中使用,虽然 git log -Ggit log --pickaxe都可以显示方法定义何时出现或消失,但我还没有找到任何方法让它们列出对方法的 尸体所做的所有更改。

但是,您可以使用 gitattributestextconv属性组合完成这项工作的解决方案。虽然这些特性最初是为了帮助您处理二进制文件,但是在这里它们也可以很好地工作。

关键是在执行任何 diff 操作之前,让 Git 从文件中删除除了您感兴趣的行之外的所有行。然后 git loggit diff等等将只看到你感兴趣的区域。

下面是我用另一种语言做的概要,你可以根据自己的需要进行调整。

  • 编写一个简短的 shell 脚本(或其他程序) ,该脚本接受一个参数——源文件的名称——并仅输出该文件中有趣的部分(如果没有有趣的部分,则不输出任何内容)。例如,可以按以下方式使用 sed:

    #!/bin/sh
    sed -n -e '/^int my_func(/,/^}/ p' "$1"
    
  • Define a Git textconv filter for your new script. (See the gitattributes man page for more details.) The name of the filter and the location of the command can be anything you like.

    $ git config diff.my_filter.textconv /path/to/my_script
    
  • Tell Git to use that filter before calculating diffs for the file in question.

    $ echo "my_file diff=my_filter" >> .gitattributes
    
  • Now, if you use -G. (note the .) to list all the commits that produce visible changes when your filter is applied, you will have exactly those commits that you're interested in. Any other options that use Git's diff routines, such as --patch, will also get this restricted view.

    $ git log -G. --patch my_file
    
  • Voilà!

One useful improvement you might want to make is to have your filter script take a method name as its first argument (and the file as its second). This lets you specify a new method of interest just by calling git config, rather than having to edit your script. For example, you might say:

$ git config diff.my_filter.textconv "/path/to/my_command other_func"

当然,过滤器脚本可以执行任何您喜欢的操作,接受更多的参数,或者其他任何操作: 除了我在这里展示的内容之外,还有很大的灵活性。

最接近的方法是确定函数在文件中的位置(例如,假设函数 i_am_buggyfoo/bar.c的第241-263行) ,然后运行一些程序,效果如下:

git log -p -L 200,300:foo/bar.c

这将打开较少(或等效的寻呼机)。现在您可以键入 /i_am_buggy(或等效的寻呼机)并开始逐步完成更改。

这甚至可以工作,这取决于您的代码风格:

git log -p -L /int i_am_buggy\(/,+30:foo/bar.c

这将搜索从该正则表达式的第一次命中(理想情况下是函数声明)限制到之后的30行。最终参数也可以是 regexp,但是使用 regexp 检测 那个是一个比较棘手的命题。

最近版本的 git log了解到 -L参数的一种特殊形式:

- L: < funname > : < file >

跟踪 <file>中由 "<start>,<end>"(或函数名 regex <funcname>)给出的行范围的演变。你不能给任何路径规格限制器。这目前仅限于从一个修订开始的遍历,也就是说,您只能给出零个或一个正修订参数。可以多次指定此选项。
...
如果给定的是 “:<funcname>”而不是 <start><end>,那么它是一个正则表达式,表示从第一个与 <funcname>匹配的 function 名行到下一个 function 名行的范围。“:<funcname>”从上一个 -L范围的末尾开始搜索,如果有的话,则从文件的开始搜索。“^:<funcname>”从文件开始搜索。

换句话说: 如果您要求 Git 执行 git log -L :myfunction:path/to/myfile.c,它现在将很高兴地打印该函数的更改历史记录。

正确的方法是按照 Eckes 回答中的解释使用 git log -L :function:path/to/file

但是另外,如果函数非常长,您可能希望只看到各种提交引入的更改,而不是每个可能只触及其中一行的提交所包含的未修改的整个函数行。就像正常的 diff一样。

通常,git log可以查看与 -p的差异,但这不适用于 -L。 因此,您必须使用 grep git log -L来仅显示涉及的行,并使用提交/文件头来将它们上下文化。这里的诀窍是只匹配终端彩色线,添加 --color开关,与正则表达式。最后:

git log -L :function:path/to/file --color | grep --color=never -E -e "^(^[\[[0-9;]*[a-zA-Z])+" -3

注意,^[应该是实际的字面 ^[。您可以通过在 bash 中按下 ^ V ^ [来键入它们,即 Ctrl + VCtrl + [。参考资料 给你

还有最后的 -3开关,允许打印3行输出上下文,前后每匹配一行。你可能需要根据自己的需要调整一下。

  1. 使用 git log -L :<funcname>:<file>显示函数历史,如 Ekes 的回答和 < a href = “ https://www.git-scm.com/docs/git-log # Document/git-log.txt-[ ltstartgtltendgtltfilegt”rel = “ norefrer”> git doc 所示

    如果没有显示任何内容,请参考 定义自定义大块头,在 .gitattributes文件中添加类似于 *.java diff=java的内容,以支持您的语言。

  2. 使用 git log commit1..commit2 -L :functionName:filePath显示提交之间的函数历史记录

  3. 使用 git log -L :sum\(double:filepath显示重载函数历史(可能有许多函数具有相同的名称,但具有不同的参数)