【问题标题】:Git: how to list all files under version control along with their author date?Git:如何列出版本控制下的所有文件及其作者日期?
【发布时间】:2017-03-17 00:57:34
【问题描述】:

给定一个 git repo,我需要生成每个版本控制文件的最后修改日期的字典,作为映射到其文件路径的 unix 时间戳。就 git 而言,我需要最后修改日期 - 而不是文件系统。

为了做到这一点,我想让 git 输出受版本控制的所有文件的列表以及每个文件的作者日期。如果git ls-filesgit ls-tree -r master 的输出在每一行都包含时间戳,那么它们的输出将是完美的。

有没有办法从 git 获取这个输出?

更新以获得更多上下文:我有一个当前的实现,它包含一个 python 脚本,它遍历源代码控制下的每个文件并在每个文件上执行 git log,但我发现这不能很好地扩展。存储库中的文件越多,我必须拨打的git log 就越多。所以这导致我寻找一种方法来从 git 收集这些信息,调用次数更少(理想情况下只有 1 次)。

【问题讨论】:

  • @derekvanvivliet 文件作者是什么意思?可以有多个人对一个文件进行了提交。
  • 不幸的是 git 缺少“树责备”,即“git blame <directory>”来获得输出,例如GitHub 树视图。您可以使用具有自定义格式的 git log -1 --tformat=... --follow=<filename> 获取每个单独文件的数据,迭代 git ls-tree --names-only -r HEAD,但这不会很快。
  • @ansh0l 不是文件作者,作者日期。我的意思是文件最后在源代码管理中修改的日期
  • @JakubNarębski 我试过这种方法,但正如你提到的,它并不快。我需要比这更高效的东西。

标签: git


【解决方案1】:

受版本控制的所有文件的列表以及每个文件的作者日期

缩放不是这个问题:

#!/bin/sh
temp="${TMPDIR:-/tmp}/@@@commit-at@@@$$"
trap "rm '$temp'" 0 1 2 3 15
git log --pretty=format:"%H%x09%at" --topo-order --reverse "$@" >"$temp"
cut -f1 "$temp" \
| git diff-tree -r --root --name-status --stdin \
| awk '
        BEGIN {FS="\t"; OFS="\t"}
        FNR==1{++f}
        f==1  {at[$1]=$2; next}
        NF==1 {commit=$1; next}
        $1=="D"{$1=""; delete last[$0]; next} # comment to also show deleted files
              {did=$1;$1=""; last[$0]=at[commit]"\t"did}
        END   {for (f in last) print last[f]f}
 ' "$temp" - \
| sort -t"`printf '\t'`" -k3

【讨论】:

  • 效果很好。你能解释一下你在用git loggit diff-tree 做什么吗?如果它也能在附近的任何地方执行,我想在 python 中实现它。
  • 基本上所有的效率都是批量差异树处理,如果python维护一个对象缓存它也可以做到。日志从根开始转储 SHA + unix-timestamp-format author date,因此最新实际提交的 %at 是已注册的,然后 cut|diff-tree 为每个 SHA 行吐出,然后将名称-状态对放入awk 首先加载 sha/timestamp 对,然后加载 name->timestamp-via-commit-SHA 查找+给定状态表并在最后转储该表。查看发生了什么的最好方法是自己在玩具仓库上运行命令。
【解决方案2】:

我编写了以下脚本来为每个文件输出路径、短标签和日期。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Author: R.F. Smith <rsmith@xs4all.nl>
# $Date: 2013-03-23 01:09:59 +0100 $
#
# To the extent possible under law, Roland Smith has waived all
# copyright and related or neighboring rights to gitdates.py. This
# work is published from the Netherlands. See
# http://creativecommons.org/publicdomain/zero/1.0/

"""For each file in a directory managed by git, get the short hash and
data of the most recent commit of that file."""

import os
import sys
import subprocess
import time
from multiprocessing import Pool

# Suppres terminal windows on MS windows.
startupinfo = None
if os.name == 'nt':
    startupinfo = subprocess.STARTUPINFO()
    startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW

def filecheck(fname):
    """Start a git process to get file info. Return a string
    containing the filename, the abbreviated commit hash and the
    author date in ISO 8601 format.

    Arguments:
    fname -- Name of the file to check.
    """
    args = ['git', '--no-pager', 'log', '-1', '--format=%h|%at', fname]
    try:
        b = subprocess.check_output(args, startupinfo=startupinfo)
        data = b.decode()[:-1]
        h, t = data.split('|')
        out = (fname[2:], h, time.gmtime(float(t)))
    except (subprocess.CalledProcessError, ValueError):
        return (fname[2:], '', time.gmtime(0.0))
    return out

def main():
    """Main program."""
    # Get a list of all files
    allfiles = []
    # Get a list of excluded files.
    exargs = ['git', 'ls-files', '-i', '-o', '--exclude-standard']
    exc = subprocess.check_output(exargs).split()
    if not '.git' in os.listdir('.'):
        print('This directory is not managed by git.')
        sys.exit(0)
    for root, dirs, files in os.walk('.'):
        if '.git' in dirs:
            dirs.remove('.git')
        tmp = [os.path.join(root, f) for f in files if f not in exc]
        allfiles += tmp
    # Gather the files' data using a Pool.
    p = Pool()
    filedata = []
    for res in p.imap_unordered(filecheck, allfiles):
        filedata.append(res)
    p.close()
    # Sort the data (latest modified first) and print it
    filedata.sort(key=lambda a: a[2], reverse=True)
    dfmt = '%Y-%m-%d %H:%M:%S %Z'
    for name, tag, date in filedata:
        print('{}|{}|{}'.format(name, tag, time.strftime(dfmt, date)))


if __name__ == '__main__':
    main()

【讨论】:

  • 感谢@RolandSmith。事实上,我目前正在使用与此类似的脚本。我希望有一种方法可以从 git 中获取这些信息(与其最后修改日期相关的文件名),而对 git log 的调用更少,或者最好是一次调用。
【解决方案3】:

我要做的是运行git ls-files 并将它们全部添加到一个数组中,然后运行git log $date_args --name-only,然后解析该输出并从数组中删除这些文件,同时将日期信息添加到字典中,然后停止数组为空时的处理。

【讨论】:

    【解决方案4】:

    给你:

    git ls-files -z | xargs -0 -n1 -I{} -- git log -1 --format='%at {}' {}
    

    这适用于bash,可能适用于sh

    【讨论】:

      猜你喜欢
      • 2010-11-29
      • 2014-09-08
      • 2012-10-27
      • 1970-01-01
      • 1970-01-01
      • 2015-07-03
      • 2011-08-06
      • 2012-09-14
      • 2022-06-29
      相关资源
      最近更新 更多