34

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

我不明白如何添加文件以进行提交。

4

7 回答 7

20

使用请求库的解决方案:

注意:我使用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
# }

最后,对于更高级的设置,请阅读文档

于 2020-08-18T03:10:57.813 回答
17

您可以查看新的更新GitHub CRUD API(2013 年 5 月)是否可以提供帮助

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

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

于 2013-05-07T06:25:04.040 回答
16

这是一个完整的片段:

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)
于 2017-10-15T21:24:59.450 回答
10

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

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

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

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

于 2013-02-03T13:07:36.920 回答
2

根据先前的答案,这是一个完整的示例。请注意,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)

于 2020-10-10T20:40:01.530 回答
1

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

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

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

结合GAE 上的JekyllWebhooks和 GitHub API 脚本,以及适当的GAE 设置,它将为您提供广泛的可能性,例如调用外部脚本并在 GitHub 上创建动态页面。

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

于 2016-05-11T14:26:07.953 回答
-1

我创建了一个使用 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)
于 2021-11-19T09:48:30.277 回答