-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathgit.ts
More file actions
143 lines (136 loc) · 3.85 KB
/
git.ts
File metadata and controls
143 lines (136 loc) · 3.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import { promises as fs } from "fs";
import git from "isomorphic-git";
import { commitFilesFromBuffers } from "./node";
import {
CommitChangesFromRepoArgs,
CommitFilesFromBuffersArgs,
CommitFilesResult,
} from "./interface";
import { relative, resolve } from "path";
/**
* @see https://isomorphic-git.org/docs/en/walk#walkerentry-mode
*/
const FILE_MODES = {
directory: 0o40000,
file: 0o100644,
executableFile: 0o100755,
symlink: 0o120000,
} as const;
export const commitChangesFromRepo = async ({
base,
cwd: workingDirectory,
recursivelyFindRoot = true,
filterFiles,
log,
...otherArgs
}: CommitChangesFromRepoArgs): Promise<CommitFilesResult> => {
const ref = base?.commit ?? "HEAD";
const cwd = resolve(workingDirectory);
const repoRoot = recursivelyFindRoot
? await git.findRoot({ fs, filepath: cwd })
: cwd;
const gitLog = await git.log({
fs,
dir: repoRoot,
ref,
depth: 1,
});
const oid = gitLog[0]?.oid;
if (!oid) {
throw new Error(`Could not determine oid for ${ref}`);
}
/**
* The directory to add files from. This is relative to the repository
* root, and is used to filter files.
*/
const relativeStartDirectory =
cwd === repoRoot ? null : relative(repoRoot, cwd) + "/";
// Determine changed files
const trees = [git.TREE({ ref: oid }), git.WORKDIR()];
const additions: CommitFilesFromBuffersArgs["fileChanges"]["additions"] = [];
const deletions: CommitFilesFromBuffersArgs["fileChanges"]["deletions"] = [];
const fileChanges = {
additions,
deletions,
};
await git.walk({
fs,
dir: repoRoot,
trees,
map: async (filepath, [commit, workdir]) => {
// Don't include ignored files
if (
await git.isIgnored({
fs,
dir: repoRoot,
filepath,
})
) {
return null;
}
const prevOid = await commit?.oid();
const currentOid = await workdir?.oid();
// Don't include files that haven't changed, and exist in both trees
if (prevOid === currentOid && !commit === !workdir) {
return null;
}
if (
(await commit?.mode()) === FILE_MODES.symlink ||
(await workdir?.mode()) === FILE_MODES.symlink
) {
throw new Error(
`Unexpected symlink at ${filepath}, GitHub API only supports files and directories. You may need to add this file to .gitignore`,
);
}
if ((await workdir?.mode()) === FILE_MODES.executableFile) {
throw new Error(
`Unexpected executable file at ${filepath}, GitHub API only supports non-executable files and directories. You may need to add this file to .gitignore`,
);
}
// Iterate through anything that may be a directory in either the
// current commit or the working directory
if (
(await commit?.type()) === "tree" ||
(await workdir?.type()) === "tree"
) {
// Iterate through these directories
return true;
}
if (
relativeStartDirectory &&
!filepath.startsWith(relativeStartDirectory)
) {
// Ignore files that are not in the specified directory
return null;
}
if (filterFiles && !filterFiles(filepath)) {
// Ignore out files that don't match any specified filter
return null;
}
if (!workdir) {
// File was deleted
deletions.push(filepath);
return null;
} else {
// File was added / updated
const arr = await workdir.content();
if (!arr) {
throw new Error(`Could not determine content of file ${filepath}`);
}
additions.push({
path: filepath,
contents: Buffer.from(arr),
});
}
return true;
},
});
return commitFilesFromBuffers({
...otherArgs,
fileChanges,
log,
base: {
commit: oid,
},
});
};