从提交树中提取与代码更改相关的提交

时间:2015-02-07 20:37:59

标签: python github github-api pygit2

现在,我可以使用pygit2库遍历github存储库的提交树。我收到了存储库中每个文件更改的所有提交。这意味着我正在对存储库中扩展名为.rtf的文本文件进行更改。如何过滤掉仅与代码更改相关的提交?我不希望这些更改与文本文档有关。

感谢任何帮助或指示。感谢。

last = repo[repo.head.target]

t0=last

f = open(outputFile,'w')

print t0.hex


for commit in repo.walk(last.id):
     if t0.hex == commit.hex:
        continue

     print commit.hex
     out=repo.diff(t0,commit)
     f.write(out.patch)
     t0=commit;

作为输出的一部分,我得到了rtf文件以及以下内容的区别:

diff --git a/archived-output/NEW/action-core[best].rtf b/archived-output/NEW/action-core[best].rtf
deleted file mode 100644
index 56cdec6..0000000
--- a/archived-output/NEW/action-core[best].rtf
+++ /dev/null
@@ -1,8935 +0,0 @@
-{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff31507\deff0\stshfdbch31506\stshfloch31506\stshfhich31506\stshfbi31507\deflang1033\deflangfe1033\themelang1033\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f1\fbidi \fswiss\fcharset0\fprq2{\*\panose 020b0604020202020204}Arial;}
-{\f2\fbidi \fmodern\fcharset0\fprq1{\*\panose 02070309020205020404}Courier New;}{\f3\fbidi \froman\fcharset2\fprq2{\*\panose 05050102010706020507}Symbol;}

要么必须从树中过滤提交,要么我必须过滤输出。我正在考虑是否可以通过在遍历树时删除相应的提交来删除与rtf文件相关的更改。

2 个答案:

答案 0 :(得分:1)

  

如果可以的话,我们如何获得修改过的文件列表?

啊,现在你问的是正确的问题!当然,Git不会在每次提交中存储已修改文件的列表。相反,每个提交代表某个特定时间点的整个存储库的状态。为了找到修改过的文件,您需要将一次提交中包含的文件与之前的提交进行比较。

对于repo.walk()返回的每个提交,tree属性引用关联的Tree对象(它本身是表示包含在其中的文件和目录的TreeEntry个对象的列表。特别是Tree)。

Tree对象具有diff_to_tree()方法,可用于将其与另一个Tree对象进行比较。这将返回一个Diff对象,该对象充当Patch个对象列表的迭代器。每个Patch对象引用正在比较的两个Tree之间的单个文件中的更改。

Patch对象确实是所有这一切的关键,因为这就是方法 我们确定哪些文件已被修改。

以下代码演示了这一点。对于每个提交,它将打印 新文件,已修改文件或已删除文件的列表:

import stat
import pygit2


repo = pygit2.Repository('.')

prev = None
for cur in repo.walk(repo.head.target):

    if prev is not None:
        print prev.id
        diff = cur.tree.diff_to_tree(prev.tree)
        for patch in diff:
            print patch.status, ':', patch.new_file_path,
            if patch.new_file_path != patch.old_file_path:
                print '(was %s)' % patch.old_file_path,
            print

    if cur.parents:
        prev = cur
        cur = cur.parents[0]

如果我们针对a sample repository运行此操作,我们可以查看 前几次提交的输出:

c285a21e013892ee7601a53df16942cdcbd39fe6
D : fragments/configure-flannel.sh
A : fragments/flannel-config.service.yaml
A : fragments/write-flannel-config.sh
M : kubecluster.yaml
b06de8f2f366204aa1327491fff91574e68cd4ec
M : fragments/enable-services-master.sh
M : fragments/enable-services-minion.sh
c265ddedac7162c103672022633a574ea03edf6f
M : fragments/configure-flannel.sh
88a8bd0eefd45880451f4daffd47f0e592f5a62b
A : fragments/configure-docker-storage.sh
M : fragments/write-heat-params.yaml
M : kubenode.yaml

并将其与git log --oneline --name-status的输出进行比较:

c285a21 configure flannel via systemd unit
D       fragments/configure-flannel.sh
A       fragments/flannel-config.service.yaml
A       fragments/write-flannel-config.sh
M       kubecluster.yaml
b06de8f call daemon-reload before starting services
M       fragments/enable-services-master.sh
M       fragments/enable-services-minion.sh
c265dde fix json syntax problem
M       fragments/configure-flannel.sh
88a8bd0 configure cinder volume for docker storage
A       fragments/configure-docker-storage.sh
M       fragments/write-heat-params.yaml
M       kubenode.yaml

...... aaaand,看起来几乎相同。希望这已经足够了 你开始了。

答案 1 :(得分:0)

这主要是将larsks's excellent answer重写为

  • 当前的pygit2 API
  • Python3

它还修复了迭代逻辑中的一个缺陷:当遍历修订范围(a..b)时,原始代码可能会错过将最后一个修订与其父版本进行区分的功能。

以下近似命令

git log --name-status --pretty="format:Files changed in %h" origin/devel..master

在larsks提供的示例存储库中。

但是,我无法跟踪文件重命名。这被打印为删除和添加。永远不会到达打印重命名的代码行。

import pygit2

repo = pygit2.Repository('.')

# Show files changed between origin/devel and current HEAD
devel = repo.revparse_single('origin/devel')
walker = repo.walk(repo.head.target)
walker.hide(devel.id)

for cur in walker:
    if cur.parents:
        print ("Files changed in {}".format(cur.short_id))
        prev = cur.parents[0]

        diff = prev.tree.diff_to_tree(cur.tree)
        for patch in diff:
            print(patch.delta.status_char(), ':', patch.delta.new_file.path)
            if patch.delta.new_file.path != patch.delta.old_file.path:
                print('(was {})'.format(patch.delta.old_file.path))
        print()