git命令有一个很有用的命令,可以在文件重命名后跟踪文件,例如git log --follow path/to/some/file
。不幸的是,它只适用于单个文件。我想能够执行等效于git log --follow some/directory
的操作。
可能的一种方法是通过使用git ls-tree
命令输出并在循环中执行此操作,但问题是涉及多个文件的提交将无法"合并"成一个提交。
是否有更好的方法?注意:使用git 2.7.4版本。
git命令有一个很有用的命令,可以在文件重命名后跟踪文件,例如git log --follow path/to/some/file
。不幸的是,它只适用于单个文件。我想能够执行等效于git log --follow some/directory
的操作。
可能的一种方法是通过使用git ls-tree
命令输出并在循环中执行此操作,但问题是涉及多个文件的提交将无法"合并"成一个提交。
是否有更好的方法?注意:使用git 2.7.4版本。
不。
Git 不存储目录,只存储文件。当你在任何要查看整个提交记录的命令(如git log
或git diff
)中使用路径名称时,Git 会从整个提交记录开始,然后缩小到与该路径匹配的文件。这里的目录只是选择其中的每个文件。
--follow
选项只能跟踪一个文件。因此,如果你可以设法将其应用于一个目录,Git 首先会将该目录转换为一组文件,然后选择其中一个文件并跟踪它。
(实际的 --follow
代码非常复杂。它利用了重命名检测代码,但仅在比较提交记录时是从新到旧顺序时才起作用:如果添加了 --reverse
,则 --follow
根本不起作用。可能需要彻底重写整个代码。通过重新编码,也许可以使其处理多个文件名,甚至是包含文件的目录。)
git log --follow
works with a directory without any problem, simplest example: git log --follow .
and I get git log pertaining to all files in the current dir (git version 2.25.1). Similarly the pathspec option works git log -- ...
- bloody似乎没有内置的方法可以做到这一点。
可以使用脚本并遵循简单的算法来完成此操作:
以下是使用 Python 3 和 sh
模块的 hacky 方法,由于各种原因,在 Windows 上无法工作。
#!/usr/bin/env python3
import os
import shlex
import sys
import tempfile
import sh
def _get_commits(fname):
# Ask for the commit and the timestamp
# .. the timestamp doesn't guarantee ordering, but good enough
for c in sh.git('log', '--follow', '--pretty=%ct %h', fname,
_tty_out=False, _iter=True):
c = c.strip().split()
yield int(c[0]), c[1]
def git_log_follow_multi(filenames):
if len(filenames) == 0:
print("Specify at least one file to log")
elif len(filenames) <= 1:
os.system('git log --follow -p %s' % filenames[0])
else:
# Use git log to generate lists of commits for each file, sort
commits = []
for fname in filenames:
commits += _get_commits(fname)
# Sort the lists (python's sort is stable)
commits.sort(reverse=True)
# Uniquify (http://www.peterbe.com/plog/uniqifiers-benchmark)
seen = set()
seen_add = seen.add
commits = [c for c in commits if not (c in seen or seen_add(c))]
# Finally, display them
tname = None
try:
file_list = ' '.join(shlex.quote(fname) for fname in filenames)
with tempfile.NamedTemporaryFile(mode='w', delete=False) as fp:
tname = fp.name
for _, commit in commits:
fp.write('git log -p -1 --color %s %s\n' % (commit, file_list))
# Use os.system to make our lives easier
os.system('bash %s | less -FRX' % tname)
finally:
if tname:
os.unlink(tname)
if __name__ == '__main__':
git_log_follow_multi(sys.argv[1:])
./script.py src/*
正如torek所提到的那样, git diff --follow
可以跟踪重命名,但仅适用于单个文件。在Bash(或Windows上的Git Bash)中组合几个命令可以得到包含目录中所有文件的组合结果:
find /path/to/directory -type f \
-exec git log --oneline --follow '{}' \; \
| cut -d ' ' -f 1 \
| sort --unique > /tmp/commits ; \
git log --oneline \
| cut -d ' ' -f 1 \
| grep -F -f /tmp/commits \
| xargs -I {} git --no-pager show {} --no-patch ; \
rm /tmp/commits
find /path/to/directory -type f
可以找到所选目录下的所有文件。
这可以进行调整,例如,-maxdepth 1
排除子目录中的文件,! -name '*.bin'
排除某些文件。
-exec git log --oneline --follow '{}' \;
对所有找到的文件执行 git log
,跟踪每个文件的重命名。
cut -d ' ' -f 1
提取提交哈希值。
sort --unique > /tmp/commits
删除重复项(并按字母数字顺序排序哈希值)。结果存储在临时文件中。
git log --oneline
获取完整日志 按正确顺序。
cut -d ' ' -f 1
提取提交哈希值。
grep -F -f /tmp/commits
提取影响我们感兴趣的文件的条目。它们现在按正确顺序排列。
xargs -I {} git --no-pager show {} --no-patch
显示提交详细信息。
这可以进行调整,例如,删除 --no-patch
以查看更改内容,或使用 --format=short
格式化输出。
rm /tmp/commits
再次删除临时文件。