GitPython:如何在 GitPython 的提交中访问文件的内容

Posted

技术标签:

【中文标题】GitPython:如何在 GitPython 的提交中访问文件的内容【英文标题】:GitPython: How can I access the contents of a file in a commit in GitPython 【发布时间】:2016-07-25 13:52:46 【问题描述】:

我是 GitPython 的新手,我正在尝试在提交中获取文件的内容。我能够从特定的提交中获取每个文件,但是每次运行命令时都会出错。现在,我知道该文件存在于 GitPython 中,但每次运行程序时,都会出现以下错误:

 returned non-zero exit status 1

我正在使用 Python 2.7.6Ubuntu Linux 14.04。

我知道该文件存在,因为我也直接从命令行进入 Git,检查相应的提交,搜索该文件并找到它。我也在上面运行cat命令,文件内容就显示出来了。很多时候,当错误出现时,它表示有问题的文件不存在。我正在尝试使用 GitPython 完成每个提交,从每个单独的提交中获取每个 blob 或文件,并在该文件的内容上运行一个外部 Java 程序。 Java 程序旨在将字符串返回给 Python。为了捕获从我的 Java 代码返回的字符串,我还使用了subprocess.check_output。任何帮助将不胜感激。

我尝试将命令作为列表传递:

cmd = ['java', '-classpath', '/home/rahkeemg/workspace/CSCI499_Java/bin/:/usr/local/lib/*:', 'java_gram.mainJava','absolute/path/to/file']
subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=False)

我也尝试过将命令作为字符串传递:

subprocess.check_output('java -classpath /home/rahkeemg/workspace/CSCI499_Java/bin/:/usr/local/lib/*: java_gram.mainJava file'.format(file=entry.abspath.strip()), shell=True)

是否可以从 GitPython 访问文件的内容? 例如,假设有一个提交并且它有一个文件 foo.java 在该文件中是以下代码行:

foo.java

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

public class foo
    public static void main(String[] args) throws Exception

我想访问文件中的所有内容并在其上运行外部程序。 任何帮助将不胜感激。下面是我用来这样做的一段代码

 #! usr/bin/env python

 __author__ = 'rahkeemg'

 from git import *
 import git, json, subprocess, re

 
 git_dir = '/home/rahkeemg/Documents/GitRepositories/WhereHows'


 # make an instance of the repository from specified path
 repo = Repo(path=git_dir)

 heads = repo.heads  # obtain the different repositories
 master = heads.master  # get the master repository

 print master
 
 # get all of the commits on the master branch
 commits = list(repo.iter_commits(master))

 cmd = ['java', '-classpath', '/home/rahkeemg/workspace/CSCI499_Java/bin/:/usr/local/lib/*:', 'java_gram.mainJava']

 # start at the very 1st commit, or start at commit 0
 for i in range(len(commits) - 1, 0, -1):
     commit = commits[i]
     commit_num = len(commits) - 1 - i
     print commit_num, ": ", commit.hexsha, '\n', commit.message, '\n'

     for entry in commit.tree.traverse():
         if re.search(r'\.java', entry.path):
                             
            current_file = str(entry.abspath.strip())
            
            # add the current file or blob to the list for the command to run
            cmd.append(current_file) 
            print entry.abspath

            try:
             
                # This is the scenario where I pass arguments into command as a string
                print subprocess.check_output('java -classpath /home/rahkeemg/workspace/CSCI499_Java/bin/:/usr/local/lib/*: java_gram.mainJava file'.format(file=entry.abspath.strip()), shell=True)
            
  
                # scenario where I pass arguments into command as a list
                j_response = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=False)
            
            except subprocess.CalledProcessError as e:
                 print "Error on file: ", current_file
         
            # Use pop on list to remove the last string, which is the selected file at the moment, to make place for the next file.  
            cmd.pop()

【问题讨论】:

“我也直接从命令行进入 git,检查相应的提交,搜索文件,然后找到它” - 你的问题是,当你迭代GitPython不执行结帐 提示1:既然Python很神奇,你可以用for commit_num, commit in reversed(list(enumerate(commits))):代替ilencommits[i]等等... 提示 2:re.search(r'\.java', entry.path) 也将匹配像 'my.name.is.java.txt' 这样的字符串,它根本不必是 Java 源文件。为什么不干脆做entry.path.endswith('.java') 【参考方案1】:

首先,当你像这样遍历提交历史时,文件不会被检出。你得到的只是文件名,可能会导致文件,也可能不会,但肯定不会导致文件与当前签出的版本不同。

但是,有一个解决方案。请记住,原则上,您可以使用 git 命令执行的任何操作,都可以使用 GitPython。

要从特定修订中获取文件内容,您可以执行以下操作,I've taken from that page:

git show <treeish>:<file>

因此,在 GitPython 中:

file_contents = repo.git.show(':'.format(commit.hexsha, entry.path))

但是,这仍然不会使文件出现在磁盘上。如果需要文件的真实路径,可以使用tempfile:

f = tempfile.NamedTemporaryFile(delete=False)
f.write(file_contents)
f.close()

# at this point file with name f.name contains contents of
#   the file from path entry.path at revision commit.hexsha
# your program launch goes here, use f.name as filename to be read

os.unlink(f.name) # delete the temp file

【讨论】:

我使用的是 3.x 版。以下代码对我有用。 repo.git.execute(["git", "show", f"commit.hexsha:file"])

以上是关于GitPython:如何在 GitPython 的提交中访问文件的内容的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Python 和 PyGithub/GitPython 克隆我的 git 存储库?

如何使用GitPython克隆存储库

如何从GitPython中的repo获取目录git详细信息?

gitpython如何修改文件内容不影响格式

python gitpython失败

Python操作Git库 `GitPython`