Skip to content

Instantly share code, notes, and snippets.

@StephanHoyer
Last active February 13, 2024 14:19
Show Gist options
  • Save StephanHoyer/91d8175507fcae8fb31a to your computer and use it in GitHub Desktop.
Save StephanHoyer/91d8175507fcae8fb31a to your computer and use it in GitHub Desktop.
Commiting multiple files to github over API
'use strict';
var Octokat = require('octokat');
var extend = require('lodash/object/assign');
var defaults = {
branchName: 'master',
token: '',
username: '',
reponame: ''
};
function init(options) {
options = extend({}, defaults, options);
var head;
var octo = new Octokat({
token: options.token
});
var repo = octo.repos(options.username, options.reponame);
function fetchHead() {
return repo.git.refs.heads(options.branchName).fetch();
}
function fetchTree() {
return fetchHead().then(function(commit) {
head = commit;
return repo.git.trees(commit.object.sha).fetch();
});
}
function commit(files, message) {
return Promise.all(files.map(function(file) {
return repo.git.blobs.create({
content: file.content,
encoding: 'utf-8'
});
})).then(function(blobs) {
return fetchTree().then(function(tree) {
return repo.git.trees.create({
tree: files.map(function(file, index) {
return {
path: file.path,
mode: '100644',
type: 'blob',
sha: blobs[index].sha
};
}),
basetree: tree.sha
});
});
}).then(function(tree) {
return repo.git.commits.create({
message: message,
tree: tree.sha,
parents: [
head.object.sha
]
});
}).then(function(commit) {
return repo.git.refs.heads(options.branchName).update({
sha: commit.sha
});
});
}
return {
commit: commit
};
}
module.exports = init;
'use strict';
var github = require('./github');
var api = github({
username: 'YOUR_USERNAME',
token: 'API_TOKEN', // created here https://github.com/settings/applications#personal-access-tokens
reponame: 'BRANCH_NAME'
});
api.commit([{
path: 'test/file1.md',
content: '# File1'
}, {
path: 'test/file2.md',
content: '# File2'
}], 'test commit via api'); // returns a promise
@lopezjurip
Copy link

Line 50 in github.js should be:

base_tree: tree.sha // with underscore

@mwilc0x
Copy link

mwilc0x commented Apr 10, 2017

This is awesome, for some reason this overwrites contents in sub-folders when doing multiple commits to that sub-folder using this approach.

EDIT: @mrpatiwi comment totally fixes it. Thanks!

@sudcha2
Copy link

sudcha2 commented Nov 30, 2020

Update: Works well as of November 2020.

@StephanHoyer thank you for sharing!

@freegroup
Copy link

Update: Works well as of April 2021 :-)

the same is possible to delete multiple files at once

delete: function(files, message) {
    return fetchTree()
      .then(function(tree) {
        return repo.git.trees.create({
          tree: files.map(function(file) {
            return {
              path: file.path,
              mode: '100644',
              type: 'blob',
              sha: null
            };
          }),
          base_tree: tree.sha
        });
      })
      .then((tree) => {
        return repo.git.commits.create({
          message: message,
          tree: tree.sha,
          parents: [ head.object.sha ]
        });
      })
      .then((commit) => {
        return repo.git.refs.heads(GITHUB_BRANCH).update({
          sha: commit.sha
        });
      })
  }

@freegroup
Copy link

freegroup commented Apr 2, 2022

just for the sake of completeness...renameDir. It is not an easy task. You must delete all files in the directory which you want to rename and create a new tree with the SHA of the deleted tree

  renameDirectory: async function(fromDir, toDir, message) {
    let parentDir = path.dirname(fromDir)
    let parentSha = null
    let dirSha = null
    return fetchTree().then( (tree) => {
      parentSha = tree.sha
      return repo.contents(parentDir).fetch()
    }).then(function(infos) {
      let item = infos.items.find( item => item.path===fromDir )
      dirSha = item.sha
      return repo.git.trees(dirSha).fetch({recursive:true});
    }).then(function({tree}) {
      // create the "tree" with "sha:null" to delete all files below the old directory
      //
      const newTree = tree.filter(({ type }) => type === TYPE.BLOB)
                              .map(({ path, mode, type }) => ( { path: `${fromDir}/${path}`, sha: null, mode, type }));
      // create a tree with the sha of the delete directory....inherits the content.
      //
      newTree.push( {
        path: toDir,
        mode: '040000',
        type: 'tree',
        sha: dirSha
      })
      // create the new folder and delte the files in the old folder.
      //
      return repo.git.trees.create({
        tree: newTree,
        base_tree: parentSha
      });
    }).then(function(tree) {
      return repo.git.commits.create({
        message: message,
        tree: tree.sha,
        parents: [ head.object.sha ]
      });
    }).then(function(commit) {
      return repo.git.refs.heads(GITHUB_BRANCH).update({
        sha: commit.sha
      });
    });
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment