【问题标题】:Get time of last commit for Git repository files via Python?通过 Python 获取 Git 存储库文件的最后提交时间?
【发布时间】:2012-10-17 18:08:52
【问题描述】:

我有一个包含数千个文件的 Git 存储库,并且想要获取每个单独文件的最后一次提交的日期和时间。这可以使用 Python 来完成吗(例如,使用 os.path.getmtime(path) 之类的东西)?

【问题讨论】:

    标签: python git


    【解决方案1】:

    这对我有用

    http://gitpython.readthedocs.io/en/stable/tutorial.html#the-tree-object

    根据文档,由于树只允许直接访问其中间子条目,请使用 traverse 方法获取迭代器以递归方式检索条目

    它创建了一个生成器对象来完成工作

    print tree.traverse()
    <generator object traverse at 0x0000000004129DC8>
    
    for blob in tree.traverse():
        commit=repo.iter_commits(paths=blob.path).next()
            print(blob.path,commit.committed_date)
    

    【讨论】:

    • 在 Python 3 中你需要使用next(repo.iter_commits(paths=blob.path))
    【解决方案2】:

    有了GitPython,就可以了:

    import git
    repo = git.Repo("./repo")
    tree = repo.tree()
    for blob in tree:
        commit = repo.iter_commits(paths=blob.path, max_count=1).next()
        print(blob.path, commit.committed_date)
    

    请注意,commit.committed_date 采用“自纪元以来的秒数”格式。

    【讨论】:

      【解决方案3】:

      您可以使用GitPython 库。

      【讨论】:

      • 谢谢。我已经找到了那个库,但是从它的文档中我无法理解(我还没有真正研究过 Git 的内部工作原理)。
      【解决方案4】:

      一个有趣的问题。下面是一个快速而肮脏的实现。 我使用multiprocessing.Pool.imap() 来启动子进程,因为它很方便。

      #!/usr/bin/env python
      # vim:fileencoding=utf-8:ft=python
      #
      # Author: R.F. Smith <rsmith@xs4all.nl>
      # Last modified: 2015-05-24 12:28:45 +0200
      #
      # 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."""
      
      from __future__ import print_function
      from multiprocessing import Pool
      import os
      import subprocess
      import sys
      import time
      
      # Suppres annoying command prompts on ms-windows.
      startupinfo = None
      if os.name == 'nt':
          startupinfo = subprocess.STARTUPINFO()
          startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
      
      
      def main():
          """
          Entry point for gitdates.
          """
          checkfor(['git', '--version'])
          # Get a list of all files
          allfiles = []
          # Get a list of excluded files.
          if '.git' not in os.listdir('.'):
              print('This directory is not managed by git.')
              sys.exit(0)
          exargs = ['git', 'ls-files', '-i', '-o', '--exclude-standard']
          exc = subprocess.check_output(exargs, startupinfo=startupinfo).split()
          for root, dirs, files in os.walk('.'):
              for d in ['.git', '__pycache__']:
                  try:
                      dirs.remove(d)
                  except ValueError:
                      pass
              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 = [res for res in p.imap_unordered(filecheck, allfiles)
                      if res is not None]
          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)))
      
      
      def checkfor(args, rv=0):
          """
          Make sure that a program necessary for using this script is available.
          Calls sys.exit when this is not the case.
      
          Arguments:
              args: String or list of strings of commands. A single string may
                  not contain spaces.
              rv: Expected return value from evoking the command.
          """
          if isinstance(args, str):
              if ' ' in args:
                  raise ValueError('no spaces in single command allowed')
              args = [args]
          try:
              with open(os.devnull, 'w') as bb:
                  rc = subprocess.call(args, stdout=bb, stderr=bb,
                                       startupinfo=startupinfo)
              if rc != rv:
                  raise OSError
          except OSError as oops:
              outs = "Required program '{}' not found: {}."
              print(outs.format(args[0], oops.strerror))
              sys.exit(1)
      
      
      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.
      
          Returns:
              A 3-tuple containing the file name, latest short hash and latest
              commit date.
          """
          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 None
          return out
      
      
      if __name__ == '__main__':
          main()
      

      示例输出:

      serve-git|8d92934|2012-08-31 21:21:38 +0200
      setres|8d92934|2012-08-31 21:21:38 +0200
      mydec|e711e27|2008-04-09 21:26:05 +0200
      sync-iaudio|8d92934|2012-08-31 21:21:38 +0200
      tarenc|8d92934|2012-08-31 21:21:38 +0200
      keypress.sh|a5c0fb5|2009-09-29 00:00:51 +0200
      tolower|8d92934|2012-08-31 21:21:38 +0200
      

      编辑:更新为使用os.devnull(也适用于ms-windows)而不是/dev/null

      Edit2:使用startupinfo 禁止在 ms-windows 上弹出命令提示符。

      Edit3:使用 __future__ 使其与 Python 2 和 3 兼容。已在 2.7.9 和 3.4.3 中测试。现在还有available on github

      【讨论】:

      • 谢谢。不过,我可能应该提到我正在尝试在 Windwos 下执行此操作,因此 /dev/null 位将不起作用。
      • @RintzeZelle 答案更新为使用更便携的os.devnull
      • 谢谢!我只需要将 git 可执行文件的路径添加到 Windows PATH 系统变量中。虽然可以为每个文件打开(和关闭)命令提示符,但可能会抑制它:stackoverflow.com/questions/1016384/…
      猜你喜欢
      • 1970-01-01
      • 2015-09-20
      • 2011-06-05
      • 2013-05-13
      • 2021-07-20
      • 1970-01-01
      • 1970-01-01
      • 2011-10-30
      相关资源
      最近更新 更多