ChatGPT解决这个技术问题 Extra ChatGPT

如何 grep Git 提交差异或某个单词的内容

在 Git 代码存储库中,我想列出所有包含某个单词的提交。我试过这个

git log -p | grep --context=4 "word"

但它并不一定会给我返回文件名(除非它距离我搜索的单词不到五行。我也试过了

git grep "word"

但它只给我现在的文件而不是历史。

如何搜索整个历史记录,以便跟踪特定单词的更改?我打算在我的代码库中搜索单词以跟踪更改(在文件历史记录中搜索)。


I
Inigo

如果要查找提交消息包含给定单词的所有提交,请使用

$ git log --grep=word

如果要查找在文件内容中添加或删除“word”的所有提交(更准确地说:“word”的出现次数发生变化的地方),即搜索提交内容,请使用所谓的 '镐'搜索

$ git log -Sword

在现代 Git 中还有

$ git log -Gword

查找添加或删除的行与“word”匹配的差异(也提交内容)。

需要注意的几点:

-G 默认接受正则表达式,而 -S 接受字符串,但可以使用 --pickaxe-regex 对其进行修改以接受正则表达式。

-S 查找“word”出现次数发生变化的提交,而 -G 查找“word”出现在 diff 中的提交。

这意味着 -S --pickaxe-regex 和 -G 做的事情并不完全相同。

git diff documentationa nice explanation 的区别:

为了说明 -S --pickaxe-regex 和 -G 之间的区别,请考虑在同一文件中具有以下差异的提交: + return frotz(nitfol, two->ptr, 1, 0); ... - hit = frotz(nitfol, mf2.ptr, 1, 0);虽然 git log -G"frotz\(nitfol" 将显示此提交,但 git log -S"frotz\(nitfol" --pickaxe-regex 不会(因为该字符串的出现次数没有改变)。


@TankorSmash -S<string> 寻找引入或删除 <string> 实例的差异。 -G<string> 查找添加或删除的行与给定的 <regex> 匹配的差异。
@m-ric 哦,我明白了,一个字符串实例,而不是整行!谢谢
@m-ric,@TankorSmash:不同之处在于 -S<string> 更快,因为它只检查 <string> 的出现次数是否更改,而 -G<string> 在每个提交差异中搜索添加和删除行。
如果您需要搜索中间有空格的单词,git log --grep="my words"
@MEM,--grep 不同于 -S-G。您可以将字符串引用到这些参数中的每一个。
A
Arslan Ali

git log 的镐将找到带有更改的提交,包括带有 git log -Sword 的“word”


这并不完全准确。 -S 查找引入或删除 实例的差异。请注意,这与仅出现在 diff 输出中的字符串不同;
虽然这通常是正确的答案,但我投反对票只是为了鼓励其他人阅读这个答案 (stackoverflow.com/a/1340245/586983),它有 3 种不同的方式并解释了它们的微妙之处。
天哪!我认为这不是否决正确答案的好理由……您不确定在评论中包含链接是否足以鼓励?
@jakeonrails,这个答案应该是对这个(旧的)答案的编辑,所以我们没有这些烦人的重复。但是人们只想要声誉,而不是干净的答案页面。
责备人民而不是制度的例子。 Stack Overflow 应该有更多样化和细致入微的方式来:转移注意力、奖励改进、限定和量化、提升本质、澄清和深入研究。并在不减损的情况下离题,眨眼眨眼。
C
CharlesW

经过大量实验后,我可以推荐以下内容,它显示了引入或删除包含给定正则表达式的行的提交,并显示每个中的文本更改,颜色显示添加和删除的单词。

git log --pickaxe-regex -p --color-words -S "<regexp to search for>"

虽然需要一段时间才能运行...... ;-)


这是迄今为止最好的感谢之一。提示:要仅列出所有结果而不分页,请在命令前添加 GIT_PAGER=cat 或在其后添加 | cat
指定路径或文件会更快git log --pickaxe-regex -p --color-words -S "<regexp to search for>" <file or fiepath>
可以将其修改为仅显示与模式匹配的行,而不是整个差异吗? (我在这里找到了答案:stackoverflow.com/a/51603771/1231241
您可以为输出添加限制以防止其失控:git log -n 1000 --pickaxe-regex -p --color-words -S "<regexp to search for>"
1
1u-

另一种方法/语法是:git log -S "word"
像这样,您可以搜索例如 git log -S "with whitespaces and stuff @/#ü !"


k
kenorb

您可以尝试以下命令:

git log --patch --color=always | less +/searching_string

或以下列方式使用 grep

git rev-list --all | GIT_PAGER=cat xargs git grep 'search_string'

在要搜索的父目录中运行此命令。


我喜欢这种方法,因为我正在查看的提交有数百行不相关的更改,而且我只对涉及我正在搜索的单词的实际补丁感兴趣。要获得颜色,请使用 git log --patch --color=always | less +/searching_string
要在垃圾提交中查找某些内容,请使用:git fsck | grep -Po '(?<=commit ).*' | GIT_PAGER xargs git grep 'search_string'
P
Peter Mortensen

要在正则表达式上使用布尔连接器:

git log --grep '[0-9]*\|[a-z]*'

此正则表达式在提交消息中搜索正则表达式 [0-9]* 或 [az]*。


P
Peter Mortensen

这在与 BFG(Git 过滤器分支 - 不要与 git-filter-branch 混淆)和 git-filter-repo 结合使用时很有用。它只是获取文件路径,以便您可以将它们提供给我刚才提到的两个工具之一。

A. 相对的、唯一的、排序的路径:

# Get all unique filepaths of files matching 'password'
# Source: https://stackoverflow.com/a/69714869/10830091
git rev-list --all | (
    while read revision; do
        git grep -F --files-with-matches 'password' $revision | cat | sed "s/[^:]*://"
    done
) | sort | uniq

B. 唯一的、排序的文件名(不是路径):

# Get all unique filenames matching 'password'
# Source: https://stackoverflow.com/a/69714869/10830091
git rev-list --all | (
    while read revision; do
        git grep -F --files-with-matches 'password' $revision | cat | sed "s/[^:]*://"
    done
) | xargs basename | sort | uniq

第二个命令对 BFG 很有用,因为它只接受文件名而不接受 repo-relative/system-absolute 路径。

你去吧。享受使用这些 Bash 片段给我带来的痛苦。我讨厌 Bash,那我为什么还要继续使用它呢?

解剖

仅获取文件名/路径

以下任何选项的含义相同 (git-rep documentation):

-l

--files-with-matches

--name-only

不显示每个匹配的行,只显示包含 Blockquote 的文件的名称

你的模式是:A. Regex vs B. Fixed String?

至于 -F,它只是意味着使用固定字符串而不是正则表达式来进行模式解释。 A source

属于此处的另一个有用的注释:您可以输入 -i--ignore-casecase insensitive

摆脱那个愚蠢的领先提交哈希

sed "s/[^:]*://"

Source

让他们获得独特的路径!

| sort | uniq

谁想要重复的路径?不是你,不是我!哦,嘿,看,它们也被分类了!享受。

来源:我。只要我记得,我就一直在使用它。 (man sortman uniq

没有路径的文件名怎么办?

xargs basename

您会认为 | basename 会起作用,但不是。它不接受输入 standard input,而是作为命令行参数。这是an explanation。去搞清楚! basename 基本上返回没有前导路径的词干文件名。 man basename

对于方法 A.,我想要绝对路径不是相对的。

当然,只需在最后打一个 realpath。像这样:

) | sort | uniq | xargs realpath

当然,您必须使用 xargs,因为 realpath 不使用标准输入进行输入。它使用命令行参数。就像 dirname

启示

看看这个很棒的替代答案。

在所有 Git 历史记录中搜索一个字符串

仅使用 Git grep 的文件名


感谢@Peter Mortensen 的编辑!我的答案现在看起来更加清晰,这些错别字和裸 URL 已修复。您的编辑描述也很准确,因为它们帮助我避免重复那些更正的问题。
L
Lerner Zhang

vim-fugitive 对于 Vim 中的这种检查是通用的。

使用 :Ggrep 来做到这一点。有关更多信息,您可以安装 vim-fugitive 并通过 :help Grep 查找教程。而这一集:exploring-the-history-of-a-git-repository 将指导您完成所有这些工作。


P
Peter Mortensen

如果您想搜索敏感数据以将其从您的 Git 历史记录中删除(这就是我来到这里的原因),那么有一些工具可以做到这一点。 GitHub 作为 a dedicated help page for that issue

以下是文章的要点:

BFG Repo-Cleaner 是一种更快、更简单的 git filter-branch 替代方案,用于删除不需要的数据。例如,要删除包含敏感数据的文件并保持最新提交不变),请运行:

bfg --delete-files YOUR-FILE-WITH-SENSITIVE-DATA

要替换 passwords.txt 中列出的所有文本,只要它可以在存储库的历史记录中找到,请运行:

bfg --replace-text passwords.txt

有关完整使用和下载说明,请参阅 BFG Repo-Cleaner 的文档。


您可能想将此答案添加到 stackoverflow.com/questions/872565/… 而不是此处