如何使用GitHub.js从浏览器直接将整个目录提交到GitHub

by Illia Kolodiazhnyi

通过伊利亚·科洛迪亚涅尼(Illia Kolodiazhnyi)

如何使用GitHub.js从浏览器直接将整个目录提交到GitHub (How to commit entire directories to GitHub directly from your browser using GitHub.js)

Did you know you can parse a movie database website, then store its data in your own GitHub repository — without ever leaving your browser?

您是否知道可以解析电影数据库网站,然后将其数据存储在自己的GitHub存储库中-甚至无需离开浏览器?

You can also do things like change a webpage by using your browser’s developer tools, then push the updated code as a commit — together with all its images and other resources.

您还可以执行以下操作,例如使用浏览器的开发人员工具来更改网页,然后将更新的代码连同所有图像和其他资源一起作为提交推送。

GitHub’s HTTP API lets you to use pretty much all of GitHub infrastructure. In most cases, it’s transparent and easy to grasp. But there’s one thing that isn’t so easy to do at first glance — making nice commits with lots of files at the same time, like running git push from your terminal does.

GitHub的HTTP API使您可以使用几乎所有的GitHub基础架构。 在大多数情况下,它是透明的并且易于掌握。 但是乍一看,有一件事情做起来不那么容易-可以同时对很多文件进行不错的提交,就像从终端运行git push一样。

But don’t worry. By the time you finish reading this article, you’ll be able to use a set of low-level calls to achieve this.

但是不用担心。 在阅读完本文时,您将可以使用一组低级调用来实现此目的。

设定 (Getting set up)

You’re going to implement a function that will take the data from files and push them with a commit to GitHub, like this:

您将实现一个功能,该功能将从文件中获取数据并通过提交到GitHub来推送它们,如下所示:

pushFiles(    'Making a commit with my adorable files',    [        {content: 'You are a Wizard, Harry', path: 'harry.txt'},        {content: 'May the Force be with you', path: 'jedi.txt'}    ]);

There are a few important things to note, though:

但是,需要注意一些重要事项:

  • I’m going to use the Github-JS library to simplify things. It’s a convenient wrapper around the calls to the API.

    我将使用Github-JS库简化事情。 这是对API调用的便捷包装。

  • Although there will only be one function to do the job, it will make many requests under the hood. This is due to the way the GitHub API is built — it has to make at least one request per file submitted, then several extra requests.

    尽管只有一个功能可以完成这项工作,但它会在后台发出许多请求。 这是由于GitHub API的构建方式-每个提交的文件必须至少发出一个请求,然后再发出几个额外的请求。
  • Committing binary files (like images) will require a bit more set up. I have a special section below that covers this.

    提交二进制文件(如图像)将需要更多设置。 我在下面有一个特别的部分介绍了这一点。

成功的算法 (An algorithm for success)

Take a look at the internal structure of the GitHub repository:

看一下GitHub存储库的内部结构:

Here is a brief explanation of how this works: the top pointer of every branch points to a particular commit, which points to a tree, which points to a version of a file. Those are basically the type of objects you should care about: Commit, Tree and Blob (content of a file).

这是它的工作原理的简要说明:每个分支的顶部指针指向一个特定的提交,该提交指向一棵树,该指向一个文件的版本。 这些基本上就是您应该关注的对象类型: CommitTreeBlob (文件的内容)。

Each contains a hash string called SHA — it’s actually a checksum hash of the object. So objects point to each other using those SHA values.

每个对象都包含一个称为SHA的哈希字符串-实际上是对象的校验和哈希。 因此,对象使用这些SHA值指向彼此。

On the Git Data page of the API, you can find the description of the algorithm to achieve exactly your goal. But here’s how this works in detail:

在API的Git Data页面上,您可以找到算法说明以准确实现您的目标。 但这是它的详细工作原理:

  1. Retrieves the current freshest Commit and remembers its SHA. It will be needed later to place a new Commit on top of the old one.

    检索当前最新的Commit并记住其SHA。 稍后将需要在旧的顶部上放置一个新的Commit

  2. Retrieves the Tree of the current Commit and remembers its SHA, too. It will be needed for creating the new Tree to base it on the old one.

    检索当前提交并记住其SHA。 创建新以在旧的基础上将需要它。

  3. Creates new Blobs for each of your files, then saves their SHAs.

    为每个文件创建新的Blob ,然后保存其SHA。

  4. Creates a new Tree and passes information about the Blobs it created in step 3 and the SHA of the old Tree retrieved in step 2. This will create a relation between the old Commit and the new one.

    创建一个新树,并传递有关在步骤3中创建的Blob和在步骤2中检索到的旧的SHA的信息。这将在旧提交和新提交之间创建一个关系。

  5. Creates a new Commit using: the SHA of the old Commit retrieved on step 1, the SHA of the Tree created on step 4, and the commit message for the new Commit.

    使用以下命令创建一个新的Commit :在步骤1中检索的旧Commit的SHA,在步骤4中创建的Tree的SHA和新Commit的提交消息。

  6. Finally, updates the pointer of the branch to point to the newly created Commit.

    最后,更新分支的指针以指向新创建的Commit

Apart from that, note that there’s also an authentication step, and a step where GitHub sets up the repository and branch you would like to push to.

除此之外,请注意,还有一个身份验证步骤,以及GitHub设置要推送到的存储库和分支的步骤。

Now that you have a conceptual understanding of how this works, let’s dive into the fun part — getting things done with code!

现在,您已经对如何工作有了概念上的了解,让我们深入研究有趣的部分-使用代码完成工作!

圣典! (Holy Code!)

Let’s keep things simple and use a wrapper function to store the functionality. This exposes a reference to an instance of the Github API wrapper library, and along with it several functions for getting the job done:

让我们保持简单,并使用包装函数存储该功能。 这公开了对Github API包装库实例的引用,并提供了一些用于完成工作的函数:

function GithubAPI(auth) {    let repo;    let filesToCommit = [];    let currentBranch = {};    let newCommit = {};
this.gh = new GitHub(auth);
this.setRepo = function() {}    this.setBranch = function() {}    this.pushFiles = function() {}
function getCurrentCommitSHA() {}    function getCurrentTreeSHA() {}    function createFiles() {}    function createFile() {}    function createTree() {}    function createCommit() {}    function updateHead() {}};

The setRepo() just passes arguments to the underlying library and saves the Repository object:

setRepo()只是将参数传递给基础库并保存Repository对象:

this.setRepo = function(userName, repoName) {    repo = this.gh.getRepo(userName, repoName);}

The setBranch() is a bit more complicated in logic:

setBranch()逻辑有点复杂:

this.setBranch = function(branchName) {    return repo.listBranches()        .then((branches) => {            let branchExists = branches.data                .find( branch => branch.name === branchName );            if (!branchExists) {                return repo.createBranch('master', branchName)                    .then(() => {                        currentBranch.name = branchName;                    });            } else {                currentBranch.name = branchName;            }        });}

Here you get all branches of the Repository and try to find the one you want to commit to. If it’s not found, the new branch is created based on the master.

在这里,您可以获得存储库的所有分支,并尝试找到要提交的分支。 如果找不到,则根据master创建新分支。

When you use the pushFiles() function, it goes through all the steps we discussed above:

当您使用pushFiles()函数时,它会完成我们上面讨论的所有步骤:

this.pushFiles = function(message, files) {    return getCurrentCommitSHA()        .then(getCurrentTreeSHA)        .then( () => createFiles(files) )        .then(createTree)        .then( () => createCommit(message) )        .then(updateHead)        .catch((e) => {            console.error(e);        });}

It uses a chain of Promises, as every step will make an actual request to the GitHub API.

它使用了Promises链,因为每个步骤都会向GitHub API发出实际请求。

Step 1 and 2 of the Algorithm aren’t very interesting. They just call API methods and save the SHAs of the current Commit and Tree:

算法的第1步和第2步不是很有趣。 他们只调用API方法并保存当前CommitTree的SHA:

function getCurrentCommitSHA() {    return repo.getRef('heads/' + currentBranch.name)        .then((ref) => {            currentBranch.commitSHA = ref.data.object.sha;        });}
function getCurrentTreeSHA() {    return repo.getCommit(currentBranch.commitSHA)        .then((commit) => {            currentBranch.treeSHA = commit.data.tree.sha;        });}

Now on Step 3, you need to create Blob objects for each file:

现在,在第3步中,您需要为每个文件创建Blob对象:

function createFiles(files) {    let promises = [];    let length = filesInfo.length;
for (let i = 0; i < length; i++) {        promises.push(createFile(files[i]));    }
return Promise.all(promises);}
function createFile(file) {    return repo.createBlob(file.content)        .then((blob) => {            filesToCommit.push({                sha: blob.data.sha,                path: fileInfo.path,                mode: '100644',                type: 'blob'            });        });}

Two points to note here:

这里要注意两点:

  1. you need to wait for all Blobs to be created — hence the Promise.all expression

    您需要等待所有Blob都创建Promise.all -因此Promise.all表达式

  2. the file mode will must be set to 100644 to designate a simple file. GitHub allows other types, but you don’t really need them here.

    文件模式必须设置为100644才能指定一个简单文件。 GitHub允许其他类型 ,但是您实际上并不需要它们。

Step 4 and 5 are about creating a new Tree with files (Blobs) and a Commit with that Tree:

第4步和第5步是关于使用文件( Blob )创建新的Tree以及使用该Tree进行Commit的操作

function createTree() {    return repo.createTree(filesToCommit, currentBranch.treeSHA)        .then((tree) => {            newCommit.treeSHA = tree.data.sha;        });}
function createCommit(message) {    return repo.commit(currentBranch.commitSHA, newCommit.treeSHA, message)        .then((commit) => {            newCommit.sha = commit.data.sha;        });}

And the only thing left is Step 6 — update the branch to point to the new Commit:

剩下的唯一一件事就是步骤6-更新分支以指向新的Commit

function updateHead() {    return repo.updateHead(        'heads/' + currentBranch.name,        newCommit.sha    );}

That’s it! Now you can use this beauty to push your files:

而已! 现在,您可以使用此功能来推送文件:

let api = new GithubAPI({token: 'API_TOKEN'});api.setRepo('GITHUB_USER', 'REPOSITORY');api.setBranch('AWESOME_BRANCH')    .then( () => api.pushFiles(        'Making a commit with my adorable files',        [            {content: 'You are a Wizard, Harry', path: 'harry.txt'},            {content: 'May the Force be with you', path: 'jedi.txt'}        ])    )    .then(function() {        console.log('Files committed!');    });

You can find the ready-to-use resulting implementation in this Gist.

您可以在此Gist中找到可立即使用的实现。

二进制文件呢? (What About Binary Files?)

Unfortunately, at the moment of writing this article (January 2017) the library used here internally fails to send binary data to GitHub.

不幸的是,在撰写本文时(2017年1月),此处使用的库内部无法将二进制数据发送到GitHub。

I’ve created an issue with them to try and resolve the problem. But until it’s settled, we will have to find a workaround for this.

我已经与他们创建了一个问题 ,以尝试解决该问题。 但是在解决之前,我们将不得不为此找到解决方法。

The predicament lies in the createBlob() function, which should send the content in Base64 format with proper request structure. But instead, the library handles it like a plain string.

困境在于createBlob()函数,该函数应使用适当的请求结构以Base64格式发送内容。 但是,库将其像普通字符串一样处理。

So the temporary workaround I came up with includes forking the library and changing this line to the following:

因此,我想出的临时解决方法包括派生库并将此行更改为以下内容:

if (typeof content === 'object') {    postBody = content;} else {    postBody = this._getContentObject(content);}

Basically, you would want the library to allow you to specify the proper object yourself.

基本上,您希望该库允许您自己指定适当的对象。

Using this tweaked version of the library, you can now push binary files with:

使用此库的调整版本,您现在可以使用以下命令推送二进制文件:

createBlob({content: base64Content, encoding: 'base64'})

where the base64Content is generated like this:

base64Content的生成方式如下:

let fileReader = new FileReader();fileReader.onload = function(e) {    let content = e.target.result;    //remove the header and leave only the Base64 content itself    base64Content = content.replace(/^(.+,)/, '');}fileReader.readAsDataURL(file);

I admit that this is hacky, but it’s probably the easiest way to achieve the necessary behavior.

我承认这很棘手,但这可能是实现必要行为的最简单方法。

现在继续并提交代码 (Now go forth and commit code)

GitHub gives you the ability to work with their service smoothly, and from pretty much any environment. I hope this article helped clarify some crucial concepts in relation to using the GitHub API in browser using JavaScript.

GitHub使您能够在几乎任何环境中顺利使用其服务。 我希望本文有助于阐明与使用JavaScript在浏览器中使用GitHub API有关的一些关键概念。

Good luck to you all! And let me know what you think about this in the comments.

祝大家好运! 并在评论中告诉我您对此的看法。

翻译自: https://www.freecodecamp.org/news/pushing-a-list-of-files-to-the-github-with-javascript-b724c8c09b66/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值