【问题标题】:Retrieve the commit log for a specific line in a file?检索文件中特定行的提交日志?
【发布时间】:2012-01-16 03:30:57
【问题描述】:

有没有什么方法可以让 git 给你一个提交日志,只记录触及文件中特定的提交?

git blame 类似,但git blame 将向您显示触及特定行的最后一次提交。

我真的很想获得类似的日志,而不是文件中任何位置的提交列表,而只是触及特定行的提交。

【问题讨论】:

标签: git


【解决方案1】:

您可以使用镐获得一组提交。

git log -S'the line from your file' -- path/to/your/file.txt

这将为您提供影响该文件中该文本的所有提交。如果文件在某个时候被重命名,您可以添加 --follow-parent。

如果您想在每次编辑时检查提交,可以将该结果通过管道传输到 git show:

git log ... | xargs -n 1 git show

【讨论】:

  • 我不确定这有什么帮助。如果文本受到影响,则该行不再相同,因此镐只会显示最近的更改。然后你必须做git log -S'the previous version of the line' 等等,就像你最终用git blame -L 做的一样。而且它会比git blame 慢得多,因为它必须到处寻找文本,而不仅仅是在给定的地方。
  • 您可以在其中使用正则表达式以使其更容易接受。目前,如果没有一些精心编写的脚本,就无法及时“浏览补丁”。我希望 gitk 将来会在它的补丁视图中获得这个功能。
  • 为什么叫 pick-axe ? :)
【解决方案2】:

我不相信有任何内置的东西。这很棘手,因为很少有单行多次更改而文件的其余部分也没有发生实质性变化,因此您最终会导致行号发生很大变化。

如果你足够幸运,这条线总是有一些识别特征,例如对于名称从未更改过的变量的赋值,您可以使用git blame -L 的正则表达式选项。例如:

git blame -L '/variable_name *= */',+1

但这只会找到该正则表达式的 first 匹配项,因此如果您没有匹配该行的好方法,它并没有太大帮助。

我想你可以破解一些东西。我现在没有时间写代码,但是……类似这样的东西。运行git blame -n -L $n,$n $file。第一个字段是上一个提交,第二个字段是 that 提交中的行号,因为它可能已经更改。抓住这些,然后运行git blame -n $n,$n $commit^ $file,即在上次更改文件之前从提交开始。

(请注意,如果最后一次更改该行的提交是合并提交,这将失败。如果该行作为合并冲突解决方案的一部分而更改,则可能发生这种情况的主要方式。)

编辑:我从 2011 年 3 月开始遇到 this mailing list post,其中提到 tiggit gui 有一个功能可以帮助你做到这一点。 git 本身似乎已经考虑过该功能,但尚未完成。

【讨论】:

    【解决方案3】:

    这将为每个有意义的修订调用git blame,以显示文件$FILE的行$LINE

    git log --format=format:%H $FILE | xargs -L 1 git blame $FILE -L $LINE,$LINE
    

    像往常一样,blame 会在每行的开头显示修订号。你可以追加

    | sort | uniq -c
    

    要获得汇总结果,例如更改此行的提交列表。 (不完全是,如果仅移动了代码,这可能会针对该行的不同内容显示相同的提交 ID 两次。要进行更详细的分析,您必须对相邻提交的 git blame 结果进行滞后比较. 有人吗?)

    【讨论】:

    • 我再次认为这不起作用,因为它不会跟踪该行的先前位置。因此,如果在 2 次提交前添加了一行,您将看到另一行
    • 我无法让上述内容为我工作,但这很好用。这是这里所有答案的组合:git log --format=format:%H path/to/file | xargs -L 1 -I % git blame -L '/var myVar = */',+1 % -- path/to/file 在哪里使用'/var myVar = */' 作为任何正则表达式匹配,您需要该行来解释任何更改。对我来说,我需要检查一些不断从配置文件中添加/删除的值,所以这很容易。
    【解决方案4】:

    这是一个定义 git 别名的解决方案,因此您可以像这样使用它:

    git rblame -M -n -L '/REGEX/,+1' FILE
    

    输出示例:

    00000000 18 (Not Committed Yet 2013-08-19 13:04:52 +0000 728) fooREGEXbar
    15227b97 18 (User1 2013-07-11 18:51:26 +0000 728) fooREGEX
    1748695d 23 (User2 2013-03-19 21:09:09 +0000 741) REGEXbar
    

    您可以在您的 .gitconfig 中定义别名,或者只需运行以下命令

    git config alias.rblame !sh -c 'while line=$(git blame "$@" $commit 2>/dev/null); do commit=${line:0:8}^; [ 00000000^ == $commit ] && commit=$(git rev-parse HEAD); echo $line; done' dumb_param
    

    这是一个丑陋的单行,所以这里是一个去混淆的等效 bash 函数:

    git-rblame () {
        local commit line
        while line=$(git blame "$@" $commit 2>/dev/null); do
            commit="${line:0:8}^"
            if [ "00000000^" == "$commit" ]; then
                commit=$(git rev-parse HEAD)
            fi
            echo $line
        done
    }
    

    镐解决方案( git log --pickaxe-regex -S'REGEX' )只会给你添加/删除行,而不是行的其他更改包含正则表达式。

    此解决方案的一个限制是 git blame 仅返回第一个 REGEX 匹配项,因此如果存在多个匹配项,则递归可能会“跳转”到另一行。请务必检查完整的历史输出以发现那些“跳跃”,然后修复您的 REGEX 以忽略寄生线。

    最后,这是一个替代版本,它在每次提交时运行 git show 以获得完整的差异:

    git config alias.rblameshow !sh -c 'while line=$(git blame "$@" $commit 2>/dev/null); do commit=${line:0:8}^; [ 00000000^ == $commit ] && commit=$(git rev-parse HEAD); git show $commit; done' dumb_param
    

    【讨论】:

    • 我认为这在 mac 中不起作用,对吗?
    【解决方案5】:

    另见Git: discover which commits ever touched a range of lines


    Since Git 1.8.4git log-L 可以查看一系列线路的演变。

    例如,假设您查看git blame 的输出。这里-L 150,+11的意思是“只看150到150+11行”:

    $ git blame -L 150,+11 -- git-web--browse.sh
    a180055a git-web--browse.sh (Giuseppe Bilotta 2010-12-03 17:47:36 +0100 150)            die "The browser $browser is not
    a180055a git-web--browse.sh (Giuseppe Bilotta 2010-12-03 17:47:36 +0100 151)    fi
    5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 152) fi
    5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 153) 
    5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 154) case "$browser" in
    81f42f11 git-web--browse.sh (Giuseppe Bilotta 2010-12-03 17:47:38 +0100 155) firefox|iceweasel|seamonkey|iceape)
    5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 156)    # Check version because firefox < 2.0 do
    5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 157)    vers=$(expr "$($browser_path -version)" 
    5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 158)    NEWTAB='-new-tab'
    5d6491c7 git-browse-help.sh (Christian Couder 2007-12-02 06:07:55 +0100 159)    test "$vers" -lt 2 && NEWTAB=''
    a0685a4f git-web--browse.sh (Dmitry Potapov   2008-02-09 23:22:22 -0800 160)    "$browser_path" $NEWTAB "$@" &
    

    你想知道现在第 155 行的历史。

    然后,使用git log。这里,-L 155,155:git-web--browse.sh 的意思是“在名为 git-web--browse.sh 的文件中跟踪第 155 行到第 155 行的演变”。

    $ git log --pretty=short -u -L 155,155:git-web--browse.sh
    commit 81f42f11496b9117273939c98d270af273c8a463
    Author: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>
    
        web--browse: support opera, seamonkey and elinks
    
    diff --git a/git-web--browse.sh b/git-web--browse.sh
    --- a/git-web--browse.sh
    +++ b/git-web--browse.sh
    @@ -143,1 +143,1 @@
    -firefox|iceweasel)
    +firefox|iceweasel|seamonkey|iceape)
    
    commit a180055a47c6793eaaba6289f623cff32644215b
    Author: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>
    
        web--browse: coding style
    
    diff --git a/git-web--browse.sh b/git-web--browse.sh
    --- a/git-web--browse.sh
    +++ b/git-web--browse.sh
    @@ -142,1 +142,1 @@
    -    firefox|iceweasel)
    +firefox|iceweasel)
    
    commit 5884f1fe96b33d9666a78e660042b1e3e5f9f4d9
    Author: Christian Couder <chriscool@tuxfamily.org>
    
        Rename 'git-help--browse.sh' to 'git-web--browse.sh'.
    
    diff --git a/git-web--browse.sh b/git-web--browse.sh
    --- /dev/null
    +++ b/git-web--browse.sh
    @@ -0,0 +127,1 @@
    +    firefox|iceweasel)
    

    【讨论】:

    • 'git log --topo-order --graph -u -L 155,155:git-web--browse.sh' - 这给出了一个致命错误:'invalid object name 155,155'。 Git 版本:1.8.3.2。有什么建议吗?
    • 升级到 Git 1.8.4 或更高版本。
    • 如果我想知道“commitA 中第 155 行的历史”(而不是 HEAD 中的第 155 行)怎么办。我可以简单地使用git log commitA-hash -L 155,155:file-name吗?
    • @Flimm,我没有强烈的偏好。
    • 这很好用,除非文件已被移动/重命名...似乎 --follow 不喜欢与行范围参数组合。
    【解决方案6】:

    一个非常简单的方法是使用vim-fugitive。只需在 vim 中打开文件,使用V 选择您感兴趣的行,然后输入

    :Glog
    

    现在您可以使用:cnext:cprev 查看修改该行的文件的所有修订版本。在任何时候,输入 :Gblame 以查看 sha、作者和日期信息。

    【讨论】:

    • 这太棒了!
    【解决方案7】:

    尝试使用 Git 1.8.4 中实现的以下命令。

    git log -u -L <upperLimit>,<lowerLimit>:<path_to_filename>
    

    所以,在你的情况下,upperLimit & lowerLimit 是被感动的line_number

    更多信息可以在here找到。

    【讨论】:

    • 我认为--pretty=short 在使用-L 时会被忽略。请更正
    【解决方案8】:

    您可以混合使用git blamegit log 命令来检索 git blame 命令中每个提交的摘要并附加它们。类似于以下 bash + awk 脚本。它将提交摘要附加为内联代码注释。

    git blame FILE_NAME | awk -F" " \
    '{
       commit = substr($0, 0, 8);
       if (!a[commit]) {
         query = "git log --oneline -n 1 " commit " --";
         (query | getline a[commit]);
       }
       print $0 "  // " substr(a[commit], 9);
     }'
    

    一行:

    git blame FILE_NAME | awk -F" " '{ commit = substr($0, 0, 8); if (!a[commit]) { query = "git log --oneline -n 1 " commit " --"; (query | getline a[commit]); } print $0 "  // " substr(a[commit], 9); }'
    

    【讨论】:

      【解决方案9】:

      就我而言,行号随着时间的推移发生了很大变化。 我也在 git 1.8.3 上,它不支持“git blame -L”中的正则表达式。 (RHEL7还有1.8.3)

      myfile=haproxy.cfg
      git rev-list HEAD -- $myfile | while read i
      do
          git diff -U0 ${i}^ $i $myfile | sed "s/^/$i /"
      done | grep "<sometext>"
      

      Oneliner:

      myfile=<myfile> ; git rev-list HEAD -- $myfile | while read i; do     git diff -U0 ${i}^ $i $myfile | sed "s/^/$i /"; done | grep "<sometext>"
      

      这当然可以做成脚本或函数。

      【讨论】:

        【解决方案10】:

        简化@matt 的答案 -

        git blame -L14,15 -- &lt;file_path&gt;

        在这里你会因为14 to 15的行而受到指责。

        由于-L 选项需要Range 作为参数,我们无法使用-L 选项为单行获取Blame`

        Reference

        【讨论】:

        • 如果您只对第 14 行感兴趣,git blame -L14,14 -- &lt;file_path&gt; 也可以使用。
        【解决方案11】:

        如果行的位置(行号)在文件的历史中保持不变,这将在每次提交时向您显示该行的内容:

        git log --follow --pretty=format:"%h" -- 'path/to/file' | while read -r hash; do echo $hash && git show $hash:'path/to/file' | head -n 544 | tail -n1; done
        

        544 更改为行号,将path/to/file 更改为文件路径。

        【讨论】:

          猜你喜欢
          • 2021-07-23
          • 2012-02-19
          • 1970-01-01
          • 1970-01-01
          • 2015-08-03
          • 1970-01-01
          • 2021-10-12
          • 1970-01-01
          相关资源
          最近更新 更多