python - 如何使用Python中的GitPython包在每次提交时制作GitHub存储库的副本

标签 python git

我一直在尝试使用Python中的GitPython软件包在其历史记录中的每次提交时复制GitHub存储库,并且当它在我的代码中途出现时会遇到此错误。

git.exc.GitCommandError: Cmd('git') failed due to: exit code(128)
cmdline: git reset --mixed HEAD~1 --
stderr: 'fatal: Failed to resolve 'HEAD~1' as a valid revision.'


这是我一直在运行的代码:

from git import *
import os, shutil

repo = Repo(repo_path)
commits = list(repo.iter_commits('master'))
for c in commits:
    # reset to previous commit
    repo.head.reset('HEAD~1', index = True, working_tree = True)
    # unique SHA key
    sha = c.name_rev.split()[0] 
    shutil.copytree(repo_path, destination_path)


可能由于合并导致此错误吗?如果是这样,如何解决它,以便可以在回购的master分支中获得所有提交?

最佳答案

在开始回答之前,我会说:我不清楚您为什么要这么做。例如,您可以使用git archive创建任何给定提交的tar或zip文件。例如:

git archive -o foo.tar v2.3.1


从标记为foo.tar的修订版本中制作一个v2.3.1文件。要从master可以访问的所有修订中制作许多tar或zip文件,您可以编写:

git rev-list master | while read hash; do
    git archive -o /path/to/$hash.zip $hash
done


并完成它。




  可能由于合并导致此错误吗?


是的,可能会。


  如果是这样,如何解决它,以便可以在回购的master分支中获得所有提交?


当心:master中的提交可能包括许多其他分支中的提交。

执行此操作时:


commits = list(repo.iter_commits('master'))



您可以从名称master中获得所有提交的完整列表,从最新的列表开始。例如,假设master指向要提交的图形。代替每个实际的提交哈希ID,我将使用一个大写字母表示提交:

A--B--C------G   <-- master
    \       /
     D--E--F   <--- develop


该存储库有七次提交(计数!)。所有七个提交均处于启用状态,即可以从分支master进行访问。七个提交中的六个在分支develop上。名称master标识提交G,它是一个合并提交。名称develop标识提交F,不是。

执行此操作时:


    repo.head.reset('HEAD~1', index = True, working_tree = True)



您让Python告诉Git将当前提交(这是其中的七个)解析到它的第一个父提交,然后将存储库的“当前提交”的概念更改为刚刚找到的提交。假设您从HEAD(当前提交)开始为提交G。然后HEAD~1是提交C

在这里,事情变得有些复杂。 repo.head对象代表Git自己的HEAD,它始终是两个不同项之一。但是,在这种情况下,它很明显是一个符号引用,指向master。我尚未对此进行测试,但是几乎可以肯定的是,GitPython在这里忠实地重现了Git自己的行为,并且根据您的参数和您的参数,使用git reset--soft--mixed之一等效于--hard--hard的代码(奇怪的是,此处显示失败的命令使用--mixed;您的代码与您的发布不匹配,或者更有可能的是,GitPython使用了额外的步骤)。因此,这最终使名称master指向新选择的提交C

A--B--C   <-- master
 \
  D--E--F   <-- develop


提交G到哪里去了?好吧,虽然没有真正的地方,但是现在已经“丢失”了:很难找到它,并且在到期后,它实际上会被完全删除。因此,提交G实际上已经消失了。 (如果我们知道它的哈希,可以将其复活:我们可以强制master用另一个git reset或同等名称再次指向它。您在变量commits中的提交列表仍然列出了其哈希,因此这是众多哈希之一我们可以找到并恢复它的方式。)

现在,您可以使用commit C来编写主循环主体代码:


sha = c.name_rev.split()[0] 
shutil.copytree(repo_path, destination_path)



您已经遍历了列表中的七个提交之一,在认为它是提交C的同时复制了提交G(在repo.iter_commits('master')中的第一个提交是提交G,因为这是一个master指着)。

现在您可以循环使用第二个了。但是,存储库现在只有六个提交,并且master指向提交C。现在,您再执行一次git reset --hard,从图片中删除提交C,剩下的就是:

A--B   <-- master
    \
     D--E--F   <-- develop


现在,您可以使用commit B进行一些操作(而c中的for c in commits在七次提交中的第二次提交中,以一定顺序列出)–目前尚不清楚repo.iter_commits使用的顺序,但它可能会运行git rev-list并因此获得默认顺序;如果是这样,请参见git rev-list文档)。

现在,您执行另一个git reset --hard。这次,不会忘记提交B:提交D会记住它。但是master最后指向提交A

A   <-- master
 \
  B--D--E--F   <-- develop


您可以使用提交A来完成您的任务,而for c in commits则是执行七个的第三次提交。

现在,您要求Git查找A的第一个父提交...但是A没有第一个父提交,也没有任何父提交。提交A是有史以来的第一次提交;这是一个根提交。此时,git reset完全失败。您已经通过仅跟随第一个父级链接来遍历从master可以到达的四个提交。从master可以访问的其他三个提交在某一时刻要求紧随第二个父提交。您还删除了您访问的四个提交中的两个;剩下两个只是因为它们可以通过另一个名称访问。

请注意,您可能具有相同的图形,但不再有名称develop

A--B--C------G   <-- master
    \       /
     D--E--F


在这种情况下,第一个擦除git resetG也会擦除对D-E-F链的访问,因为G是该访问的关键:现在是G^2,它是commit的第二个父级G,即找到F。是F找到E,而E找到D;因此,失去G会失去所有这些,而最终剩下的只是:

A--B--C   <-- master


可见。 (和以前一样,所有“已擦除”的提交都会在宽限期内停留,只要您可以再次找到它们,就可以将其复活。)


  ...我该如何解决


使用完全不同的算法,和/或明智地选择提交。仅仅因为从某个分支名称可以到达七个(或其他数目)提交,并不意味着所有七个(或任何其他数目)都作为第一父级链接在一起。

请注意,即使在完全线性的设置中,例如:

A--B   <-- master


您将具有两个提交的列表(以B然后A的顺序),但是您只能运行一次git reset HEAD~1,才能从B退回到A。一旦进入A,就无法再后退。在这种情况下,与执行提交相比,您必须退回的步数要少一些。无论做什么,您都应该首先执行提交。

对于我来说,GitPython如何处理“分离的HEAD”并不是立即显而易见的,尽管如果您想直接从Python代码访问文件,那么使用分离的HEAD并没有多大意义。但是,如果您要运行shutils.copytree,则最好只用shell脚本编写整个过程,这要简单得多:Git充满了shell脚本,并且旨在与它们很好地协同工作,并且需要shell解释器为了使Git完全起作用而存在,因此,如果您具有Git,则具有外壳解释器。

关于python - 如何使用Python中的GitPython包在每次提交时制作GitHub存储库的副本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45472397/

相关文章:

python - 无法制作自定义 Django View 装饰器(带参数)

python - 如何计算 pandas 列表列中值的总出现次数?

python - 将 Pandas 数据帧转换为 csv 字符串

python - 为 *nix 创建 Python 可执行文件

git commit - 如何查看提交时所做的更改

python - 在 seaborn 中订购箱线图 x 轴

git - 在 git 中自动 merge 没有冲突(使用逐字比较而不是逐行)

git - 从包含符号链接(symbolic link)的 GitHub 存储库 check out Subversion

git - 将 SVN 转换为 Git 使用 lfs 保存大文件

git - libgit2sharp 向 GitHub API merge pull 请求提供的正确 sha 是什么?