Skip to content

Commit 2d82b17

Browse files
authored
v3.0.0: Allow multiple commits in a single request
Allow multiple commits
2 parents 2e114e5 + 13b2ccf commit 2d82b17

File tree

4 files changed

+305
-87
lines changed

4 files changed

+305
-87
lines changed

README.md

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ npm install octokit-commit-multiple-files --save
1010

1111
## Usage
1212

13-
This plugin accepts `owner`, `repo`, `path`, `branch` and `message` like `.createOrUpdateFile` ([Octokit Docs](https://octokit.github.io/rest.js/#octokit-routes-repos-create-or-update-file)).
13+
This plugin accepts `owner`, `repo`, `path` and `branch` like `.createOrUpdateFile` ([Octokit Docs](https://octokit.github.io/rest.js/#octokit-routes-repos-create-or-update-file)).
1414

1515
If the `branch` provided does not exist, the plugin will error. To automatically create it, set `createBranch` to true. You may provide a `base` branch if you choose to do this, or the plugin will use the repo's default branch as the base.
1616

17-
In addition, it accepts `changes` which is an array of objects containing a `path` and the file `contents`.
17+
In addition, it accepts `changes` which is an array of objects containing a `message` and a `files` object
1818

1919
```javascript
2020
const Octokit = require("@octokit/rest").plugin(
@@ -27,17 +27,39 @@ const branchName = await octokit.repos.createOrUpdateFiles({
2727
repo,
2828
branch,
2929
createBranch,
30-
message,
3130
changes: [
3231
{
33-
path: "test.md",
34-
contents: "One"
32+
message: "Your commit message",
33+
files: {
34+
"test.md": `# This is a test
35+
36+
I hope it works`,
37+
"test2.md": {
38+
contents: `Something else`
39+
}
40+
}
3541
},
3642
{
37-
path: "test2.md",
38-
contents: "Two"
43+
"message": "This is a separate commit",
44+
"files": {
45+
"second.md": "Where should we go today?"
46+
}
3947
}
4048
]
4149
})
4250
```
4351

52+
In addition, you can set the `mode` of a file change. For example, if you wanted to update a submodule pointer:
53+
54+
```javascript
55+
{
56+
"message": "This is a submodule commit",
57+
"files": {
58+
"my_submodule": {
59+
"contents": "your-commit-sha",
60+
"mode": "160000",
61+
"type": "commit"
62+
}
63+
}
64+
}
65+
```

create-or-update-files.js

Lines changed: 60 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ module.exports = function(octokit, opts) {
22
return new Promise(async (resolve, reject) => {
33
// Up front validation
44
try {
5-
for (let req of ["owner", "repo", "branch", "message"]) {
5+
for (let req of ["owner", "repo", "branch"]) {
66
if (!opts[req]) {
77
return reject(`'${req}' is a required parameter`);
88
}
@@ -19,8 +19,7 @@ module.exports = function(octokit, opts) {
1919
base,
2020
branch: branchName,
2121
createBranch,
22-
changes,
23-
message
22+
changes
2423
} = opts;
2524

2625
let branchAlreadyExists = true;
@@ -57,60 +56,74 @@ module.exports = function(octokit, opts) {
5756
}
5857

5958
// Create blobs
60-
const treeItems = [];
6159
for (let change of changes) {
62-
if (!change.contents) {
63-
return reject(
64-
`No file contents provided for ${change.path || "Un-named file"}`
65-
);
60+
let message = change.message;
61+
if (!message) {
62+
return reject(`changes[].message is a required parameter`);
63+
}
64+
if (!change.files || Object.keys(change.files).length === 0) {
65+
return reject(`changes[].files is a required parameter`);
6666
}
6767

68-
if (!change.path) {
69-
return reject(
70-
`No file path provided for the following contents: ${change.contents.substr(
71-
0,
72-
30
73-
)}...`
74-
);
68+
const treeItems = [];
69+
for (let fileName in change.files) {
70+
let properties = change.files[fileName] || "";
71+
72+
let contents = properties.contents || properties;
73+
let mode = properties.mode || "100644";
74+
let type = properties.type || "blob";
75+
76+
if (!contents) {
77+
return reject(`No file contents provided for ${fileName}`);
78+
}
79+
80+
let fileSha;
81+
if (type == "commit") {
82+
fileSha = contents;
83+
} else {
84+
let file = (
85+
await octokit.git.createBlob({
86+
owner,
87+
repo,
88+
content: Buffer.from(contents).toString("base64"),
89+
encoding: "base64"
90+
})
91+
).data;
92+
fileSha = file.sha;
93+
}
94+
95+
treeItems.push({
96+
path: fileName,
97+
sha: fileSha,
98+
mode: mode,
99+
type: type
100+
});
75101
}
76102

77-
let file = (
78-
await octokit.git.createBlob({
103+
// Add those blobs to a tree
104+
let tree = (
105+
await octokit.git.createTree({
79106
owner,
80107
repo,
81-
content: Buffer.from(change.contents).toString("base64"),
82-
encoding: "base64"
108+
tree: treeItems,
109+
base_tree: baseTree
83110
})
84111
).data;
85112

86-
treeItems.push({
87-
path: change.path,
88-
sha: file.sha,
89-
mode: "100644",
90-
type: "blob"
91-
});
92-
}
93-
94-
// Add those blobs to a tree
95-
let tree = (
96-
await octokit.git.createTree({
97-
owner,
98-
repo,
99-
tree: treeItems,
100-
base_tree: baseTree
101-
})
102-
).data;
113+
// Create a commit that points to that tree
114+
let commit = (
115+
await octokit.git.createCommit({
116+
owner,
117+
repo,
118+
message,
119+
tree: tree.sha,
120+
parents: [baseTree]
121+
})
122+
).data;
103123

104-
// Create a commit that points to that tree
105-
let commit = (
106-
await octokit.git.createCommit({
107-
owner,
108-
repo,
109-
message,
110-
tree: tree.sha,
111-
parents: [baseTree]
112-
})
113-
).data;
124+
// Update the base tree if we have another commit to make
125+
baseTree = commit.sha;
126+
}
114127

115128
// Create a ref that points to that tree
116129
let action = "createRef";
@@ -128,7 +141,7 @@ module.exports = function(octokit, opts) {
128141
repo,
129142
force: true,
130143
ref: `${updateRefBase}heads/${branchName}`,
131-
sha: commit.sha
144+
sha: baseTree
132145
})
133146
).data;
134147

0 commit comments

Comments
 (0)