【问题标题】:Python Git Module experiences? [closed]Python Git 模块体验? [关闭]
【发布时间】:2009-09-21 19:10:10
【问题描述】:

人们对 Python 的任何 Git 模块有什么体验? (我知道 GitPython、PyGit 和 Dulwich - 如果您知道,请随时提及其他人。)

我正在编写一个程序,它必须与 Git 存储库进行交互(添加、删除、提交),但没有使用 Git 的经验,所以我正在寻找的一件事是易于使用/理解到 Git。

我主要感兴趣的其他方面是库的成熟度和完整性、错误的合理缺乏、持续开发以及文档和开发人员的帮助。

如果您想到我可能想/需要知道的其他事情,请随时提及。

【问题讨论】:

  • 我们可以把这个问题变成一个社区维基吗?我觉得最好的答案会随着时间而改变。
  • @relet:只要关闭就不能做wiki。

标签: python git


【解决方案1】:

虽然这个问题是不久前被问到的,当时我不知道库的状态,但对于搜索者来说,值得一提的是,GitPython 在抽象命令行工具方面做得很好,这样你就不会需要使用子进程。您可以使用一些有用的内置抽象,但对于其他所有内容,您可以执行以下操作:

import git
repo = git.Repo( '/home/me/repodir' )
print repo.git.status()
# checkout and track a remote branch
print repo.git.checkout( 'origin/somebranch', b='somebranch' )
# add a file
print repo.git.add( 'somefile' )
# commit
print repo.git.commit( m='my commit message' )
# now we are one commit ahead
print repo.git.status()

GitPython 中的所有其他功能只是让导航变得更容易。我对这个库非常满意,并感谢它是底层 git 工具的包装器。

更新:我已经切换到使用 sh 模块,不仅是 git,还有我在 python 中需要的大多数命令行实用程序。要复制上述内容,我会这样做:

import sh
git = sh.git.bake(_cwd='/home/me/repodir')
print git.status()
# checkout and track a remote branch
print git.checkout('-b', 'somebranch')
# add a file
print git.add('somefile')
# commit
print git.commit(m='my commit message')
# now we are one commit ahead
print git.status()

【讨论】:

  • 基于这个答案,我刚刚用 git-python 试试运气。我发现 API 处理起来很奇怪。大多数情况下,您必须回退到 repo.git.* 通用界面,即使这样有时也无法正常工作(例如,repo.git.branch(b=somebranch) 有效,但 repo.git.branch(D=somebranch) 无效,因为缺少空格)。我想我会自己实现一个基于子流程的通用功能。我很伤心,我寄予厚望。 :-/
  • 我现在已经切换到使用 sh 模块和git = sh.git.bake(_cwd=repopath)。效果很好。
  • 链接到 sh:amoffat.github.io/sh 真的应该是 python stdlib 的一部分。
  • 最新的 python sh 版本不能在 Windows 上运行。彻底失败。
  • 来自 GitPython 自述文件:“GitPython 不适合长时间运行的进程(如守护进程),因为它容易泄漏系统资源。”
【解决方案2】:

也许它有帮助,但 Bazaar 和 Mercurial 都在使用德威来实现 Git 互操作性。

Dulwich 可能与另一个不同,因为它是 git 在 python 中的重新实现。另一个可能只是 Git 命令的包装器(因此从高级的角度来看它可能更容易使用:commit/add/delete),这可能意味着它们的 API 非常接近 git 的命令行,所以你需要获得使用 Git 的经验。

【讨论】:

  • 非常有用的回答,不知道Mercurial用德威,谢谢!
【解决方案3】:

我推荐pygit2 - 它使用出色的libgit2 绑定

【讨论】:

  • 它也提供了对 git 管道的最佳访问。
  • pygit2 是一个非常有用的库,我期待它在未来的扩展!
  • 就像现在一样,必须手动下载和编译/设置libgitpygit2 的半稳定版本,源代码来自 GitHub。问题是,头部分支的测试失败,最新的“稳定”安装失败......如果可靠性很重要并且您需要在各种环境中部署......这不是一个合适的解决方案...... :(
  • 如果您计划使用 cygwin 的客户,请远离这种组合。 pygit2 是 libgit2 的包装器,而 libgit2 已放弃所有 cygwin 支持。我从其中一位开发者那里得到的评论是,“你可以尝试,但如果它构建了它就是一个奇迹”漂亮的 API,是的,但是我的一半客户是 cygwin,因此我不能使用它。可能会去 GitPython。
  • 请注意,它们不支持 cygwin,因为 their focus is on native Windows support instead。因此,虽然 cygwin 不支持 libgit2 是正确的,但 并不 意味着 Windows 用户会被冷落。
【解决方案4】:

为了完整起见,http://github.com/alex/pyvcs/ 是所有 dvcs 的抽象层。它使用 dulwich,但提供与其他 dvcs 的互操作。

【讨论】:

    【解决方案5】:

    这是一个相当古老的问题,在寻找 Git 库时,我发现了一个今年(2013 年)制作的名为 Gittle

    它对我很有效(我尝试过的其他方法都很不稳定),并且似乎涵盖了大多数常见操作。

    自述文件中的一些示例:

    from gittle import Gittle
    
    # Clone a repository
    repo_path = '/tmp/gittle_bare'
    repo_url = 'git://github.com/FriendCode/gittle.git'
    repo = Gittle.clone(repo_url, repo_path)
    
    # Stage multiple files
    repo.stage(['other1.txt', 'other2.txt'])
    
    # Do the commit
    repo.commit(name="Samy Pesse", email="samy@friendco.de", message="This is a commit")
    
    # Authentication with RSA private key
    key_file = open('/Users/Me/keys/rsa/private_rsa')
    repo.auth(pkey=key_file)
    
    # Do push
    repo.push()
    

    【讨论】:

    • 我不喜欢你“暂存”文件而不是将它们“添加”到索引中。更改常见/重要操作的名称似乎会令人困惑。
    • @underrun 添加正在向舞台添加文件。暂存文件不是一样吗?
    • 添加文件是暂存要提交的文件(它将它们添加到索引中)。操作是一样的,但在命令行中你会输入git add other1.txt other2.txt,所以它不符合预期。
    • 同意这个包的优越性。在安装了 StaSh 之后,我什至可以在 Pythonista 应用程序中使用它,它是打包的。此外,值得注意的是,您的答案是此问题答案中最新更新的。
    • 实际上,它似乎在 Pythonista 上为我工作。让它对我的 Mac 上的私有 bitbucket 存储库的克隆进行密码验证是我终于放弃的噩梦。
    【解决方案6】:

    反映时代变化的更新答案:

    GitPython 目前是最容易使用的。它支持包装许多 git 管道命令,并具有可插入的对象数据库(dulwich 就是其中之一),如果未实现命令,则提供一个简单的 api 用于向命令行输出。例如:

    repo = Repo('.')
    repo.checkout(b='new_branch')
    

    这调用:

    bash$ git checkout -b new_branch
    

    德威也不错,但水平要低得多。使用它有点痛苦,因为它需要在管道级别对 git 对象进行操作,并且没有您通常想要做的漂亮瓷器。但是,如果您打算修改 git 的任何部分,或者使用 git-receive-pack 和 git-upload-pack,则需要使用 dulwich。

    【讨论】:

      【解决方案7】:

      我想我会回答我自己的问题,因为我选择的路径与答案中建议的不同。尽管如此,感谢那些回答的人。

      首先,简要介绍一下我使用 GitPython、PyGit 和 Dulwich 的经历:

      • GitPython:下载后,我得到了这个导入并初始化了相应的对象。但是,尝试执行本教程中的建议会导致错误。由于缺乏更多文档,我转向别处。
      • PyGit:这甚至不会导入,我找不到任何文档。
      • Dulwich:似乎是最有前途的(至少对于我想要和看到的)。我在它上面取得了一些进展,比 GitPython 还要多,因为它的 egg 带有 Python 源代码。但是,过了一段时间,我决定尝试我所做的可能会更容易。

      另外,StGit 看起来很有趣,但我需要将功能提取到一个单独的模块中,并且不想现在等待它发生。

      在尝试使上述三个模块正常工作所花费的时间(少得多)中,我设法通过子进程模块使 git 命令正常工作,例如

      def gitAdd(fileName, repoDir):
          cmd = ['git', 'add', fileName]
          p = subprocess.Popen(cmd, cwd=repoDir)
          p.wait()
      
      gitAdd('exampleFile.txt', '/usr/local/example_git_repo_dir')
      

      这还没有完全整合到我的程序中,但我预计不会有问题,除了速度可能(因为我有时会处理数百甚至数千个文件)。

      也许我只是没有耐心让 Dulwich 或 GitPython 顺利进行。也就是说,我希望这些模块很快会得到更多的开发并变得更加有用。

      【讨论】:

      • 这个答案已经过时了。
      • 是的,我有兴趣更新。
      • GitPython 运行良好并且有大量文档。
      • @Arthur 我不同意,因为我至少花了 3 个小时学习 StackOverflow 和 GitPython 文档,只是为了了解 git pull、add、commit 和使用它推送到远程仓库的基础知识。该文档确实有一些高级用例,但缺乏非常基本的用例。我基本上放弃并使用子流程。
      【解决方案8】:

      这是“git status”的一个非常快速的实现:

      import os
      import string
      from subprocess import *
      
      repoDir = '/Users/foo/project'
      
      def command(x):
          return str(Popen(x.split(' '), stdout=PIPE).communicate()[0])
      
      def rm_empty(L): return [l for l in L if (l and l!="")]
      
      def getUntracked():
          os.chdir(repoDir)
          status = command("git status")
          if "# Untracked files:" in status:
              untf = status.split("# Untracked files:")[1][1:].split("\n")
              return rm_empty([x[2:] for x in untf if string.strip(x) != "#" and x.startswith("#\t")])
          else:
              return []
      
      def getNew():
          os.chdir(repoDir)
          status = command("git status").split("\n")
          return [x[14:] for x in status if x.startswith("#\tnew file:   ")]
      
      def getModified():
          os.chdir(repoDir)
          status = command("git status").split("\n")
          return [x[14:] for x in status if x.startswith("#\tmodified:   ")]
      
      print("Untracked:")
      print( getUntracked() )
      print("New:")
      print( getNew() )
      print("Modified:")
      print( getModified() )
      

      【讨论】:

      • 我不建议解析git status
      • 解析git status --short 会更容易,我认为--short 的输出不太可能改变。
      • 对这个--porcelain: Give the output in a stable, easy-to-parse format for scripts...使用git status --porcelain
      • 或者更好的是,使用--z 而不是--porcelain。与--porcelain 不同,--z 不会转义文件名。
      【解决方案9】:

      PTBNL 的答案对我来说非常完美。 我为 Windows 用户做了更多。

      import time
      import subprocess
      def gitAdd(fileName, repoDir):
          cmd = 'git add ' + fileName
          pipe = subprocess.Popen(cmd, shell=True, cwd=repoDir,stdout = subprocess.PIPE,stderr = subprocess.PIPE )
          (out, error) = pipe.communicate()
          print out,error
          pipe.wait()
          return 
      
      def gitCommit(commitMessage, repoDir):
          cmd = 'git commit -am "%s"'%commitMessage
          pipe = subprocess.Popen(cmd, shell=True, cwd=repoDir,stdout = subprocess.PIPE,stderr = subprocess.PIPE )
          (out, error) = pipe.communicate()
          print out,error
          pipe.wait()
          return 
      def gitPush(repoDir):
          cmd = 'git push '
          pipe = subprocess.Popen(cmd, shell=True, cwd=repoDir,stdout = subprocess.PIPE,stderr = subprocess.PIPE )
          (out, error) = pipe.communicate()
          pipe.wait()
          return 
      
      temp=time.localtime(time.time())
      uploaddate= str(temp[0])+'_'+str(temp[1])+'_'+str(temp[2])+'_'+str(temp[3])+'_'+str(temp[4])
      
      repoDir='d:\\c_Billy\\vfat\\Programming\\Projector\\billyccm' # your git repository , windows your need to use double backslash for right directory.
      gitAdd('.',repoDir )
      gitCommit(uploaddate, repoDir)
      gitPush(repoDir)
      

      【讨论】:

      • 我看到很多代码重复...:p
      • 抱歉代码表达不佳,但在懒惰的计算机上提供文本加载。 :)
      【解决方案10】:

      StGit 的 git 交互库部分其实还不错。但是,它并没有作为单独的包进行拆分,但如果有足够的兴趣,我相信可以修复。

      它有非常好的抽象来表示提交、树等,以及创建新的提交和树。

      【讨论】:

        【解决方案11】:

        作为记录,上述 Git Python 库似乎都没有包含“git status”等价物,这真的是我唯一想要的,因为通过子进程处理其余的 git 命令非常容易。

        【讨论】:

        • 使用 GitPython:git.Repo( repoDir ).git.status()
        猜你喜欢
        • 2022-01-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-09
        • 2019-05-30
        • 1970-01-01
        • 2014-07-01
        • 2021-11-23
        相关资源
        最近更新 更多