找出一个文件属于哪个git提交?

8

给定一个随机文件,是否有一种标准方法可以从命令行确定文件是否属于特定提交?

这类似于stackoverflow上的问题find-out-which-git-commit-a-file-was-taken-from,但我希望能够在脚本中使用它,并且不创建临时分支。


@Bryce 我问这个问题的原因是(使用git时),我总是怀疑是否有一些命令像“git ls-tree --do-something-magical”一样,可以完全满足我的需求。就像我在Python中编写了一个方便的git sha-1计算器几分钟后发现了“git hash-object”一样! - Xevious
1
一个例子是当有人将一个配置文件(从硬件中检索)发送到支持层,直到它被移交进行事后分析或诊断。它来自哪个提交或发布版本?在早期,我们使用RCS(最终是CVS)关键字扩展来实现这个目的。我相信我们可以使用提交和检出钩子来添加虚假的关键字扩展,但这样做太过笨拙,甚至提及它都让我有点尴尬。 - Xevious
1
链接表明,并且从您的第二条评论中可以清楚地看出,您谈论的是不属于您的工作树的文件——但问题本身并没有说明这一点。听起来您只是在询问 git log -1 -- $filename(可能对“属于”的含义有些困惑)。您能否稍微重新表述一下? - trent
4个回答

4
以下是我一直在使用的脚本的摘录。我用我有限的git知识和我在网上找到的其他脚本的片段将它们拼凑在一起。它运行得足够好,但我经常发现在git中有比我通过试错学习到的更简单的方法。
FILE=$1

# git hash for file
HASH=`git hash-object $FILE`

# git revisions for file
REVS=`git log --pretty=%H -- $FILE`

# check each revision for checksum match
for rev in $REVS; do
    POSSIBLE=`git ls-tree $rev $FILE | awk '{print $3}'`
    if [[ $HASH == $POSSIBLE ]]; then
        echo $rev
    fi
done

3
除了将git log --pretty=...更改为git rev-list --all -- $FILE,我不会改变您回答中的任何内容。 - Bryce Drew

1
你的意思是文件是否在提交中被修改了吗?如果是,类似 git log --oneline -- filePathName 这样的命令应该列出从HEAD开始的包含这种情况的提交。
第二次阅读时,我认为你只是要求包含该文件的提交,无论它是否改变。如果是这样,那么你的 ls-tree 命令需要一个 -r 标志,以递归进入其子树(子目录)中。如果你只匹配sha,它将找到任何名称下的文件副本。

1
这似乎返回涉及该文件名的所有提交记录 - 但不包括包含该文件当前状态的具体提交记录。 - Xevious
啊,好的,所以你正在寻找那个文件的精确匹配。因此,你应该仍然递归(向上)并查找每个 blob 的 sha 是否与你要匹配的文件的 sha 相匹配? - David Neiss

1

DavidN答案基础上,如果文件在当前工作树中,并且工作树与HEAD同步,这将使您获得与文件当前内容对应的提交:

git log --pretty="%H" -1  -- path/to/file

但是您可能希望提前通过"git diff --exit-code /path/to/file"测试这些假设,并查看$?


1

你的方法在本地和仓库版本文件之间存在微小差异时可能会失效(例如行尾样式或由清理/涂抹过滤器引起的差异)。

以下脚本通过git diff工作,而不是依赖哈希值。它接受文件名后的diff选项。

使用示例:

# list all commits that introduce the file README.md in its local state
list_introducing_commits README.md

# list all commits that introduce the file README.md in its local state
# ignoring any difference in whitespace
list_introducing_commits README.md -w

list_introducing_commits(没有更好的名称):

#!/bin/bash

if [ $# -eq 0 ]
then
    echo "Usage: $(basename $0) path/to/file [<diff-options>]"
    exit 1
fi

file="$1"
shift 1

for rev in $(git log --pretty=%H -- "$file")
do
    if git diff --exit-code $@ $rev -- $file &> /dev/null
    then
        echo $rev
    fi
done

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接