【发布时间】:2012-01-16 03:30:57
【问题描述】:
有没有什么方法可以让 git 给你一个提交日志,只记录触及文件中特定行的提交?
与git blame 类似,但git blame 将向您显示触及特定行的最后一次提交。
我真的很想获得类似的日志,而不是文件中任何位置的提交列表,而只是触及特定行的提交。
【问题讨论】:
标签: git
有没有什么方法可以让 git 给你一个提交日志,只记录触及文件中特定行的提交?
与git blame 类似,但git blame 将向您显示触及特定行的最后一次提交。
我真的很想获得类似的日志,而不是文件中任何位置的提交列表,而只是触及特定行的提交。
【问题讨论】:
标签: git
您可以使用镐获得一组提交。
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 慢得多,因为它必须到处寻找文本,而不仅仅是在给定的地方。
我不相信有任何内置的东西。这很棘手,因为很少有单行多次更改而文件的其余部分也没有发生实质性变化,因此您最终会导致行号发生很大变化。
如果你足够幸运,这条线总是有一些识别特征,例如对于名称从未更改过的变量的赋值,您可以使用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,其中提到 tig 和 git gui 有一个功能可以帮助你做到这一点。 git 本身似乎已经考虑过该功能,但尚未完成。
【讨论】:
这将为每个有意义的修订调用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 结果进行滞后比较. 有人吗?)
【讨论】:
git log --format=format:%H path/to/file | xargs -L 1 -I % git blame -L '/var myVar = */',+1 % -- path/to/file 在哪里使用'/var myVar = */' 作为任何正则表达式匹配,您需要该行来解释任何更改。对我来说,我需要检查一些不断从配置文件中添加/删除的值,所以这很容易。
这是一个定义 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
【讨论】:
另见Git: discover which commits ever touched a range of lines。
Since Git 1.8.4、git 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 commitA-hash -L 155,155:file-name吗?
一个非常简单的方法是使用vim-fugitive。只需在 vim 中打开文件,使用V 选择您感兴趣的行,然后输入
:Glog
现在您可以使用:cnext 和:cprev 查看修改该行的文件的所有修订版本。在任何时候,输入 :Gblame 以查看 sha、作者和日期信息。
【讨论】:
尝试使用 Git 1.8.4 中实现的以下命令。
git log -u -L <upperLimit>,<lowerLimit>:<path_to_filename>
所以,在你的情况下,upperLimit & lowerLimit 是被感动的line_number
更多信息可以在here找到。
【讨论】:
--pretty=short 在使用-L 时会被忽略。请更正
您可以混合使用git blame 和git 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); }'
【讨论】:
就我而言,行号随着时间的推移发生了很大变化。 我也在 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>"
这当然可以做成脚本或函数。
【讨论】:
简化@matt 的答案 -
git blame -L14,15 -- <file_path>
在这里你会因为14 to 15的行而受到指责。
由于-L 选项需要Range 作为参数,我们无法使用-L 选项为单行获取Blame`。
【讨论】:
git blame -L14,14 -- <file_path> 也可以使用。
如果行的位置(行号)在文件的历史中保持不变,这将在每次提交时向您显示该行的内容:
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 更改为文件路径。
【讨论】: