python 如何使用GitHub API v3创建提交并推送到repo?

nkcskrwz  于 2023-05-05  发布在  Python
关注(0)|答案(7)|浏览(168)

我想创建一个存储库,并通过任何Python包向其提交一些文件。我该怎么做?
我不知道如何添加提交文件。

h7wcgrx3

h7wcgrx31#

使用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(以master分支为例)

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

最后,要了解更高级的设置,请阅读文档。

n53p2ov0

n53p2ov02#

您可以查看新更新GitHub CRUD API (May 2013)是否有帮助
repository contents API已经允许阅读文件一段时间了。现在,您可以轻松地将更改提交到单个文件,就像在Web UI中一样。
从今天开始,您可以使用这些方法:

  • 文件创建
  • 文件更新
  • 文件删除
clj7thdc

clj7thdc3#

下面是一个完整的片段:

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)
qoefvg9y

qoefvg9y4#

Github提供了一个Git database API,允许你读写原始对象,列出和更新你的引用(分支头和标签)。为了更好地理解这个主题,我强烈建议您阅读Pro Git书籍的Git Internals章节。
根据文档,将更改提交到存储库中的文件需要7个步骤:
1.获取当前提交对象
1.检索它所指向的树
1.检索该树具有的特定文件路径的blob对象的内容
1.以某种方式更改内容,并发布一个包含该新内容的新blob对象,以获取blob SHA
1.提交一个新的树对象,将文件路径指针替换为新的blob SHA,以获取树SHA
1.创建一个新的提交对象,将当前提交SHA作为父对象,并使用新的树SHA,返回提交SHA
1.更新分支的引用以指向新的提交SHA
This blog在使用perl解释这个过程方面做得很好。对于python实现,可以使用PyGithub库。

93ze6v8z

93ze6v8z5#

根据前面的答案,这里有一个完整的例子。请注意,如果你上传提交到一个新分支,你需要使用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)
xuo3flqw

xuo3flqw6#

我在 Google App Engine (GAE) 上,所以除了python,我可以创建一个新文件,更新它,甚至通过提交删除它,并在php,java和go中使用GitHub API v3推送到我在GitHub中的存储库。
检查和审查一些可用的第三方libraries来创建像在perl中呈现的example script,我建议使用以下方法:

如你所知,你可以为每个GitHub帐户和组织获得一个网站,以及无限的项目网站,这些网站直接从你的仓库托管,并默认由Jekyll提供支持。
在GAE上结合JekyllWebhooks和GitHub API Script,沿着适当的 GAE Setting,它将为您提供像calling external script一样的广泛可能性,并在GitHub上创建动态页面。
除了GAE之外,还有一个选项可以在Heroku上运行它。使用JekyllBot,它位于一个(免费)Heroku示例上,silently为每个帖子生成JSON文件,并将更改推回GitHub。

j13ufse2

j13ufse27#

我创建了一个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)

相关问题