【问题标题】:How to create a commit and push into repo with GitHub API v3?如何使用 GitHub API v3 创建提交并推送到 repo?
【发布时间】:2012-08-01 20:55:21
【问题描述】:

我想创建一个存储库并通过任何 Python 包向它提交一些文件。我该怎么办?

我不明白如何为提交添加文件。

【问题讨论】:

    标签: python github github-api


    【解决方案1】:

    使用 requests 库的解决方案:

    注意:我使用 requests 库来调用 GitHub REST API v3。

    1.获取特定分支的最后一次提交 SHA

    # GET /repos/:owner/:repo/branches/:branch_name
    last_commit_sha = response.json()['commit']['sha']
    

    2。使用文件内容创建 blob(编码 base64 或 utf-8)

    # POST /repos/:owner/:repo/git/blobs
    # {
    #  "content": "aGVsbG8gd29ybGQK",
    #  "encoding": "base64"
    #}
    base64_blob_sha = response.json()['sha']
    
    # POST /repos/:owner/:repo/git/blobs
    # {
    #  "content": "hello world",
    #  "encoding": "utf-8"
    #}
    utf8_blob_sha = response.json()['sha']
    

    3.创建定义文件夹结构的树

    # POST repos/:owner/:repo/git/trees/
    # {
    #   "base_tree": last_commit_sha,
    #   "tree": [
    #     {
    #       "path": "myfolder/base64file.txt",
    #       "mode": "100644",
    #       "type": "blob",
    #       "sha": base64_blob_sha
    #     },
    #     {
    #       "path": "file-utf8.txt",
    #       "mode": "100644",
    #       "type": "blob",
    #       "sha": utf8_blob_sha
    #     }
    #   ]
    # }
    tree_sha = response.json()['sha']
    

    4.创建提交

    # POST /repos/:owner/:repo/git/commits
    # {
    #   "message": "Add new files at once programatically",
    #   "author": {
    #     "name": "Jan-Michael Vincent",
    #     "email": "JanQuadrantVincent16@rickandmorty.com"
    #   },
    #   "parents": [
    #     last_commit_sha
    #   ],
    #   "tree": tree_sha
    # }
    new_commit_sha = response.json()['sha']
    

    5.更新您的分支的引用以指向新的提交 SHA(在主分支示例上)

    # POST /repos/:owner/:repo/git/refs/heads/master
    # {
    #     "ref": "refs/heads/master",
    #     "sha": new_commit_sha
    # }
    

    最后,要了解更高级的设置,请阅读docs

    【讨论】:

    • 谢谢。很难仅对如何执行此操作进行描述,并且代码可以正常工作并使其清晰。
    • 这些只是 HTTP 请求/响应包。它并没有真正解释如何做到这一点或它们的含义。
    • 感谢您的回答,确实为我节省了数小时的研究和尝试等
    【解决方案2】:

    你可以看看新的更新GitHub CRUD API (May 2013)能不能帮到你

    repository contents API 已允许读取文件一段时间。现在您可以轻松地将更改提交到单个文件,就像在 Web UI 中一样。

    从今天开始,您可以使用以下方法:

    【讨论】:

    【解决方案3】:

    这是一个完整的sn-p:

    def push_to_github(filename, repo, branch, token):
        url="https://api.github.com/repos/"+repo+"/contents/"+filename
    
        base64content=base64.b64encode(open(filename,"rb").read())
    
        data = requests.get(url+'?ref='+branch, headers = {"Authorization": "token "+token}).json()
        sha = data['sha']
    
        if base64content.decode('utf-8')+"\n" != data['content']:
            message = json.dumps({"message":"update",
                                "branch": branch,
                                "content": base64content.decode("utf-8") ,
                                "sha": sha
                                })
    
            resp=requests.put(url, data = message, headers = {"Content-Type": "application/json", "Authorization": "token "+token})
    
            print(resp)
        else:
            print("nothing to update")
    
    token = "lskdlfszezeirzoherkzjehrkzjrzerzer"
    filename="foo.txt"
    repo = "you/test"
    branch="master"
    
    push_to_github(filename, repo, branch, token)
    

    【讨论】:

    • 当存储库中不存在文件时,此代码将失败。如果您要更新现有文件,这将起作用。对于新文件,它会抛出 Not Found 错误。
    • 使用此 API 端点创建新文件的正确方法是什么? docs.github.com/en/rest/reference/… 端点也用于创建,但像这个答案一样使用它只允许更新现有文件。
    【解决方案4】:

    Github 提供了一个Git database API,让您可以访问读取和写入原始对象以及列出和更新您的引用(分支头和标签)。为了更好地理解该主题,我强烈建议您阅读 Pro Git 书籍的Git Internals 章节。

    根据文档,将更改提交到存储库中的文件需要 7 个步骤:

    1. 获取当前提交对象
    2. 检索它指向的树
    3. 检索树中针对该特定文件路径的 blob 对象的内容
    4. 以某种方式更改内容并使用该新内容发布新的 blob 对象,从而获得 blob SHA 回复
    5. 发布一个新的树对象,将文件路径指针替换为新的 blob SHA 获取树 SHA 回复
    6. 以当前提交 SHA 作为父级和新树 SHA 创建一个新提交对象,获取提交 SHA 回
    7. 更新分支的引用以指向新的提交 SHA

    This blog 在使用 perl 解释这个过程方面做得很好。对于 python 实现,您可以使用 PyGithub 库。

    【讨论】:

      【解决方案5】:

      根据之前的回答,这里有一个完整的例子。请注意,如果您将提交上传到新分支,则需要使用 POST,或使用 PATCH 上传到现有分支。

      
          import whatsneeded
          
          GITHUB_TOKEN = "WHATEVERWILLBEWILLBE"
          
          def github_request(method, url, headers=None, data=None, params=None):
              """Execute a request to the GitHUB API, handling redirect"""
              if not headers:
                  headers = {}
              headers.update({
                  "User-Agent": "Agent 007",
                  "Authorization": "Bearer " + GITHUB_TOKEN,
              })
          
              url_parsed = urllib.parse.urlparse(url)
              url_path = url_parsed.path
              if params:
                  url_path += "?" + urllib.parse.urlencode(params)
          
              data = data and json.dumps(data)
              conn = http.client.HTTPSConnection(url_parsed.hostname)
              conn.request(method, url_path, body=data, headers=headers)
              response = conn.getresponse()
              if response.status == 302:
                  return github_request(method, response.headers["Location"])
          
              if response.status >= 400:
                  headers.pop('Authorization', None)
                  raise Exception(
                      f"Error: {response.status} - {json.loads(response.read())} - {method} - {url} - {data} - {headers}"
                  )
          
              return (response, json.loads(response.read().decode()))
            
          def upload_to_github(repository, src, dst, author_name, author_email, git_message, branch="heads/master"):
              # Get last commit SHA of a branch
              resp, jeez = github_request("GET", f"/repos/{repository}/git/ref/{branch}")
              last_commit_sha = jeez["object"]["sha"]
              print("Last commit SHA: " + last_commit_sha)
          
              base64content = base64.b64encode(open(src, "rb").read())
              resp, jeez = github_request(
                  "POST",
                  f"/repos/{repository}/git/blobs",
                  data={
                      "content": base64content.decode(),
                      "encoding": "base64"
                  },
              )
              blob_content_sha = jeez["sha"]
          
              resp, jeez = github_request(
                  "POST",
                  f"/repos/{repository}/git/trees",
                  data={
                      "base_tree":
                      last_commit_sha,
                      "tree": [{
                          "path": dst,
                          "mode": "100644",
                          "type": "blob",
                          "sha": blob_content_sha,
                      }],
                  },
              )
              tree_sha = jeez["sha"]
          
              resp, jeez = github_request(
                  "POST",
                  f"/repos/{repository}/git/commits",
                  data={
                      "message": git_message,
                      "author": {
                          "name": author_name,
                          "email": author_email,
                      },
                      "parents": [last_commit_sha],
                      "tree": tree_sha,
                  },
              )
              new_commit_sha = jeez["sha"]
          
              resp, jeez = github_request(
                  "PATCH",
                  f"/repos/{repository}/git/refs/{branch}",
                  data={"sha": new_commit_sha},
              )
              return (resp, jeez)
      
      

      【讨论】:

        【解决方案6】:

        我在 Google App Engine (GAE) 上,所以除了 python,我可以 create 一个新文件,update 它,甚至 delete it 通过提交并推送到我在 GitHub 中的存储库在 php、java 和 go 中使用 GitHub API v3

        检查和审查一些可用的第三方 libraries 以创建类似于 perl 中提供的 example script,我建议使用以下内容:

        如您所知,您可以为每个 GitHub 帐户和组织获取一个站点,以及无限的项目站点,这些站点直接从您的存储库托管并默认由 Jekyll 提供支持。

        结合 JekyllWebhooks 和 GAE 上的 GitHub API 脚本,以及适当的 GAE Setting,它将为您提供像 calling external script 这样的广泛可能性并创建动态页面在 GitHub 上。

        除了 GAE,还有一个选项可以在 Heroku 上运行它。使用位于(免费)Heroku 实例上的 JekyllBotsilently 为每个帖子生成 JSON 文件并将更改推送回 GitHub。

        【讨论】:

          【解决方案7】:

          我为committing with multiple files using Python 创建了一个示例:

          import datetime
          import os
          import github
             
          # If you run this example using your personal token the commit is not going to be verified.
          # It only works for commits made using a token generated for a bot/app 
          # during the workflow job execution.
          
          def main(repo_token, branch):
          
              gh = github.Github(repo_token)
          
              repository = "josecelano/pygithub"
          
              remote_repo = gh.get_repo(repository)
          
              # Update files:
              #   data/example-04/latest_datetime_01.txt
              #   data/example-04/latest_datetime_02.txt
              # with the current date.
          
              file_to_update_01 = "data/example-04/latest_datetime_01.txt"
              file_to_update_02 = "data/example-04/latest_datetime_02.txt"
          
              now = datetime.datetime.now()
          
              file_to_update_01_content = str(now)
              file_to_update_02_content = str(now)
          
              blob1 = remote_repo.create_git_blob(file_to_update_01_content, "utf-8")
              element1 = github.InputGitTreeElement(
                  path=file_to_update_01, mode='100644', type='blob', sha=blob1.sha)
          
              blob2 = remote_repo.create_git_blob(file_to_update_02_content, "utf-8")
              element2 = github.InputGitTreeElement(
                  path=file_to_update_02, mode='100644', type='blob', sha=blob2.sha)
          
              commit_message = f'Example 04: update datetime to {now}'
          
              branch_sha = remote_repo.get_branch(branch).commit.sha
             
              base_tree = remote_repo.get_git_tree(sha=branch_sha)
           
              tree = remote_repo.create_git_tree([element1, element2], base_tree)
          
              parent = remote_repo.get_git_commit(sha=branch_sha)
          
              commit = remote_repo.create_git_commit(commit_message, tree, [parent])
          
              branch_refs = remote_repo.get_git_ref(f'heads/{branch}')
          
              branch_refs.edit(sha=commit.sha)
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-12-13
            • 1970-01-01
            • 2021-10-08
            • 1970-01-01
            • 1970-01-01
            • 2020-01-04
            • 1970-01-01
            相关资源
            最近更新 更多