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

时间:2022-06-29 00:50:03

So I found the question about how to view the change history of a file, but the change history of this particular file is huge and I'm really only interested in the changes of a particular method. So would it be possible to see the change history for just that particular method?

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

I know this would require git to analyze the code and that the analysis would be different for different languages, but method/function declarations look very similar in most languages, so I thought maybe someone has implemented this feature.

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

The language I'm currently working with is Objective-C and the SCM I'm currently using is git, but I would be interested to know if this feature exists for any SCM/language.

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

6 个解决方案

#1


52  

Recent versions of git log learned a special form of the -L parameter:

git log的最新版本学习了-L参数的一种特殊形式:

-L :<funcname>:<file>

- l < funcname >: <文件>

Trace the evolution of the line range given by "<start>,<end>" (or the function name regex <funcname>) within the <file>. You may not give any pathspec limiters. This is currently limited to a walk starting from a single revision, i.e., you may only give zero or one positive revision arguments. You can specify this option more than once.
...
If “:<funcname>” is given in place of <start> and <end>, it is a regular expression that denotes the range from the first funcname line that matches <funcname>, up to the next funcname line. “:<funcname>” searches from the end of the previous -L range, if any, otherwise from the start of file. “^:<funcname>” searches from the start of file.

跟踪 中“ ”(或函数名regex )给出的行范围的演变。您可能不会给出任何路径规范限制器。这目前仅限于从单个修订开始的散步,即。,你可能只给出零或一个积极的修正参数。您可以多次指定此选项。如果“: ”是在 的位置上进行的,它是一个正则表达式,表示从匹配 的第一个函数名行到下一个函数名行的范围。“: ”从前一个-L范围的末尾(如果有的话)搜索,否则从文件的开头搜索。“^:< funcname >”搜索从一开始的文件。

In other words: if you ask Git to git log -L :myfunction:path/to/myfile.c, it will now happily print the change history of that function.

换句话说:如果您要求Git日志-L:myfunction:path/to/myfile。c,它现在会很高兴地打印那个函数的变化历史。

#2


14  

Using git gui blame is hard to make use of in scripts, and whilst git log -G and git log --pickaxe can each show you when the method definition appeared or disappeared, I haven't found any way to make them list all changes made to the body of your method.

在脚本中使用git gui blame是很困难的,虽然git log -G和git log -pickaxe可以分别在方法定义出现或消失时显示给您,但是我还没有找到任何方法让他们列出对您的方法主体所做的所有更改。

However, you can use gitattributes and the textconv property to piece together a solution that does just that. Although these features were originally intended to help you work with binary files, they work just as well here.

但是,您可以使用gitattributes和textconv属性将实现此目的的解决方案拼凑在一起。尽管这些特性最初是用来帮助您处理二进制文件的,但是它们在这里也是一样的。

The key is to have Git remove from the file all lines except the ones you're interested in before doing any diff operations. Then git log, git diff, etc. will see only the area you're interested in.

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

Here's the outline of what I do in another language; you can tweak it for your own needs.

这是我用另一种语言做的概述;您可以根据自己的需要调整它。

  • Write a short shell script (or other program) that takes one argument -- the name of a source file -- and outputs only the interesting part of that file (or nothing if none of it is interesting). For example, you might use sed as follows:

    编写一个简短的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 textconv过滤器。(更多细节请参见gitattributes man页面。)过滤器的名称和命令的位置可以是您喜欢的任何内容。

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

    告诉Git在计算问题文件的差异之前使用那个过滤器。

    $ 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.

    现在,如果你用-G。(请注意:)列出在应用过滤器时产生可见更改的所有提交,您将拥有您感兴趣的那些提交。使用Git的diff例程的任何其他选项,比如——patch,也会得到这个受限的视图。

    $ 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来指定感兴趣的新方法,而不必编辑脚本。例如,你可能会说:

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

Of course, the filter script can do whatever you like, take more arguments, or whatever: there's a lot of flexibility beyond what I've shown here.

当然,过滤器脚本可以做任何您喜欢做的事情,接受更多的参数,或者其他任何东西:除了我在这里展示的之外,还有很多灵活性。

#3


8  

The closest thing you can do is to determine the position of your function in the file (e.g. say your function i_am_buggy is at lines 241-263 of foo/bar.c), then run something to the effect of:

您可以做的最接近的事情是确定您的函数在文件中的位置(例如,假设您的函数i_am_buggy位于foo/bar.c的第241-263行),然后运行如下内容:

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

This will open less (or an equivalent pager). Now you can type in /i_am_buggy (or your pager equivalent) and start stepping through the changes.

这将打开更少(或等效寻呼机)。现在,您可以输入/i_am_buggy(或对等的寻呼机)并开始逐步执行更改。

This might even work, depending on your code style:

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

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

This limits the search from the first hit of that regex (ideally your function declaration) to thirty lines after that. The end argument can also be a regexp, although detecting that with regexp's is an iffier proposition.

这将搜索从regex的第一次命中(理想情况下是函数声明)限制到之后的30行。结束参数也可以是regexp,尽管使用regexp检测是一个iffier命题。

#4


8  

git log has an option '-G' could be used to find all differences.

git log有一个选项“-G”可以用来查找所有的差异。

-G Look for differences whose added or removed line matches the given <regex>.

-G查找添加或删除的行与给定的 匹配的差异。

Just give it a proper regex of the function name you care about. For example,

只要给它一个适当的函数名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

#5


2  

git blame shows you who last changed each line of the file; you can specify the lines to examine so as to avoid getting the history of lines outside your function.

git blame显示谁最后更改了文件的每一行;您可以指定要检查的行,以避免获取函数之外的行历史。

#6


2  

The correct way is to use git log -L :function:path/to/file as explained in eckes answer.

正确的方法是使用git log -L:function:path/to/file,如eckes answer中所述。

But in addition, if your function is very long, you may want to see only the changes that various commit had introduced, not the whole function lines, included unmodified, for each commit that maybe touch only one of these lines. Like a normal diff does.

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

Normally git log can view differences with -p, but this not work with -L. So you have to grep git log -L to show only involved lines and commits/files header to contextualize them. The trick here is to match only terminal colored lines, adding --color switch, with a regex. Finally:

通常git日志可以查看与-p的不同之处,但这不能与-L一起工作。因此,您必须使用grep git log -L来只显示涉及到的行和提交/文件头,以便对它们进行上下文化。这里的诀窍是只匹配终端彩色线,添加——颜色开关,使用regex。最后:

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

Note that ^[ should be actual, literal ^[. You can type them by pressing ^V^[ in bash, that is Ctrl + V, Ctrl + [. Reference here.

注意,^[应该是实际的,文字^[。你可以输入他们按V ^ ^[在bash中,Ctrl + V,Ctrl +[。参考这里。

Also last -3 switch, allows to print 3 lines of output context, before and after each matched line. You may want to adjust it to your needs.

最后一个-3开关,允许打印3行输出上下文,在每个匹配行之前和之后。你可以根据自己的需要进行调整。

#1


52  

Recent versions of git log learned a special form of the -L parameter:

git log的最新版本学习了-L参数的一种特殊形式:

-L :<funcname>:<file>

- l < funcname >: <文件>

Trace the evolution of the line range given by "<start>,<end>" (or the function name regex <funcname>) within the <file>. You may not give any pathspec limiters. This is currently limited to a walk starting from a single revision, i.e., you may only give zero or one positive revision arguments. You can specify this option more than once.
...
If “:<funcname>” is given in place of <start> and <end>, it is a regular expression that denotes the range from the first funcname line that matches <funcname>, up to the next funcname line. “:<funcname>” searches from the end of the previous -L range, if any, otherwise from the start of file. “^:<funcname>” searches from the start of file.

跟踪 中“ ”(或函数名regex )给出的行范围的演变。您可能不会给出任何路径规范限制器。这目前仅限于从单个修订开始的散步,即。,你可能只给出零或一个积极的修正参数。您可以多次指定此选项。如果“: ”是在 的位置上进行的,它是一个正则表达式,表示从匹配 的第一个函数名行到下一个函数名行的范围。“: ”从前一个-L范围的末尾(如果有的话)搜索,否则从文件的开头搜索。“^:< funcname >”搜索从一开始的文件。

In other words: if you ask Git to git log -L :myfunction:path/to/myfile.c, it will now happily print the change history of that function.

换句话说:如果您要求Git日志-L:myfunction:path/to/myfile。c,它现在会很高兴地打印那个函数的变化历史。

#2


14  

Using git gui blame is hard to make use of in scripts, and whilst git log -G and git log --pickaxe can each show you when the method definition appeared or disappeared, I haven't found any way to make them list all changes made to the body of your method.

在脚本中使用git gui blame是很困难的,虽然git log -G和git log -pickaxe可以分别在方法定义出现或消失时显示给您,但是我还没有找到任何方法让他们列出对您的方法主体所做的所有更改。

However, you can use gitattributes and the textconv property to piece together a solution that does just that. Although these features were originally intended to help you work with binary files, they work just as well here.

但是,您可以使用gitattributes和textconv属性将实现此目的的解决方案拼凑在一起。尽管这些特性最初是用来帮助您处理二进制文件的,但是它们在这里也是一样的。

The key is to have Git remove from the file all lines except the ones you're interested in before doing any diff operations. Then git log, git diff, etc. will see only the area you're interested in.

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

Here's the outline of what I do in another language; you can tweak it for your own needs.

这是我用另一种语言做的概述;您可以根据自己的需要调整它。

  • Write a short shell script (or other program) that takes one argument -- the name of a source file -- and outputs only the interesting part of that file (or nothing if none of it is interesting). For example, you might use sed as follows:

    编写一个简短的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 textconv过滤器。(更多细节请参见gitattributes man页面。)过滤器的名称和命令的位置可以是您喜欢的任何内容。

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

    告诉Git在计算问题文件的差异之前使用那个过滤器。

    $ 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.

    现在,如果你用-G。(请注意:)列出在应用过滤器时产生可见更改的所有提交,您将拥有您感兴趣的那些提交。使用Git的diff例程的任何其他选项,比如——patch,也会得到这个受限的视图。

    $ 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来指定感兴趣的新方法,而不必编辑脚本。例如,你可能会说:

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

Of course, the filter script can do whatever you like, take more arguments, or whatever: there's a lot of flexibility beyond what I've shown here.

当然,过滤器脚本可以做任何您喜欢做的事情,接受更多的参数,或者其他任何东西:除了我在这里展示的之外,还有很多灵活性。

#3


8  

The closest thing you can do is to determine the position of your function in the file (e.g. say your function i_am_buggy is at lines 241-263 of foo/bar.c), then run something to the effect of:

您可以做的最接近的事情是确定您的函数在文件中的位置(例如,假设您的函数i_am_buggy位于foo/bar.c的第241-263行),然后运行如下内容:

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

This will open less (or an equivalent pager). Now you can type in /i_am_buggy (or your pager equivalent) and start stepping through the changes.

这将打开更少(或等效寻呼机)。现在,您可以输入/i_am_buggy(或对等的寻呼机)并开始逐步执行更改。

This might even work, depending on your code style:

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

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

This limits the search from the first hit of that regex (ideally your function declaration) to thirty lines after that. The end argument can also be a regexp, although detecting that with regexp's is an iffier proposition.

这将搜索从regex的第一次命中(理想情况下是函数声明)限制到之后的30行。结束参数也可以是regexp,尽管使用regexp检测是一个iffier命题。

#4


8  

git log has an option '-G' could be used to find all differences.

git log有一个选项“-G”可以用来查找所有的差异。

-G Look for differences whose added or removed line matches the given <regex>.

-G查找添加或删除的行与给定的 匹配的差异。

Just give it a proper regex of the function name you care about. For example,

只要给它一个适当的函数名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

#5


2  

git blame shows you who last changed each line of the file; you can specify the lines to examine so as to avoid getting the history of lines outside your function.

git blame显示谁最后更改了文件的每一行;您可以指定要检查的行,以避免获取函数之外的行历史。

#6


2  

The correct way is to use git log -L :function:path/to/file as explained in eckes answer.

正确的方法是使用git log -L:function:path/to/file,如eckes answer中所述。

But in addition, if your function is very long, you may want to see only the changes that various commit had introduced, not the whole function lines, included unmodified, for each commit that maybe touch only one of these lines. Like a normal diff does.

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

Normally git log can view differences with -p, but this not work with -L. So you have to grep git log -L to show only involved lines and commits/files header to contextualize them. The trick here is to match only terminal colored lines, adding --color switch, with a regex. Finally:

通常git日志可以查看与-p的不同之处,但这不能与-L一起工作。因此,您必须使用grep git log -L来只显示涉及到的行和提交/文件头,以便对它们进行上下文化。这里的诀窍是只匹配终端彩色线,添加——颜色开关,使用regex。最后:

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

Note that ^[ should be actual, literal ^[. You can type them by pressing ^V^[ in bash, that is Ctrl + V, Ctrl + [. Reference here.

注意,^[应该是实际的,文字^[。你可以输入他们按V ^ ^[在bash中,Ctrl + V,Ctrl +[。参考这里。

Also last -3 switch, allows to print 3 lines of output context, before and after each matched line. You may want to adjust it to your needs.

最后一个-3开关,允许打印3行输出上下文,在每个匹配行之前和之后。你可以根据自己的需要进行调整。