git-truck 0.8.2 → 0.8.4-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/bump-version.yml +1 -1
- package/build/index.js +3 -1
- package/package.json +1 -1
- package/src/analyzer/analyze.server.ts +322 -321
|
@@ -31,7 +31,7 @@ jobs:
|
|
|
31
31
|
env:
|
|
32
32
|
GITHUB_TOKEN: ${{ secrets.TOKEN }}
|
|
33
33
|
|
|
34
|
-
- run: npm install --package-lock-only
|
|
34
|
+
- run: npm install --package-lock-only --ignore-scripts
|
|
35
35
|
- run: git add package-lock.json
|
|
36
36
|
- run: git config user.email "version-bot@example.com"
|
|
37
37
|
- run: git config user.name "Version Bot"
|
package/build/index.js
CHANGED
|
@@ -5533,7 +5533,7 @@ async function latestVersion(packageName, options) {
|
|
|
5533
5533
|
|
|
5534
5534
|
// package.json
|
|
5535
5535
|
var name = "git-truck";
|
|
5536
|
-
var version = "0.8.
|
|
5536
|
+
var version = "0.8.3";
|
|
5537
5537
|
var private2 = false;
|
|
5538
5538
|
var description = "Visualizing a Git repository";
|
|
5539
5539
|
var license = "MIT";
|
|
@@ -5704,7 +5704,9 @@ async function findBranchHead(repo, branch) {
|
|
|
5704
5704
|
async function analyzeCommitLight(hash) {
|
|
5705
5705
|
const rawContent = await GitCaller.getInstance().catFileCached(hash);
|
|
5706
5706
|
const commitRegex = /tree (?<tree>.*)\s*(?:parent (?<parent>.*)\s*)?(?:parent (?<parent2>.*)\s*)?author (?<authorName>.*?) <(?<authorEmail>.*?)> (?<authorTimeStamp>\d*?) (?<authorTimeZone>.*?)\s*committer (?<committerName>.*?) <(?<committerEmail>.*?)> (?<committerTimeStamp>\d*?) (?<committerTimeZone>.*)\s*(?:gpgsig (?:.|\s)*?-----END PGP SIGNATURE-----)?\s*(?<message>.*)\s*(?<description>(?:.|\s)*)/gm;
|
|
5707
|
+
log.debug("before match");
|
|
5707
5708
|
const match = commitRegex.exec(rawContent);
|
|
5709
|
+
log.debug("after match: " + JSON.stringify(match));
|
|
5708
5710
|
const groups = (match == null ? void 0 : match.groups) ?? {};
|
|
5709
5711
|
const tree = groups["tree"];
|
|
5710
5712
|
const parent = groups["parent"] ?? emptyGitCommitHash;
|
package/package.json
CHANGED
|
@@ -1,321 +1,322 @@
|
|
|
1
|
-
import fsSync, { promises as fs } from "fs"
|
|
2
|
-
import {
|
|
3
|
-
GitBlobObject,
|
|
4
|
-
GitCommitObject,
|
|
5
|
-
GitCommitObjectLight,
|
|
6
|
-
GitTreeObject,
|
|
7
|
-
AnalyzerData,
|
|
8
|
-
AnalyzerDataInterfaceVersion,
|
|
9
|
-
TruckUserConfig,
|
|
10
|
-
} from "./model"
|
|
11
|
-
import { log, setLogLevel } from "./log.server"
|
|
12
|
-
import {
|
|
13
|
-
describeAsyncJob,
|
|
14
|
-
formatMs,
|
|
15
|
-
writeRepoToFile,
|
|
16
|
-
getCurrentBranch,
|
|
17
|
-
getRepoName,
|
|
18
|
-
getDefaultGitSettingValue,
|
|
19
|
-
resetGitSetting,
|
|
20
|
-
setGitSetting,
|
|
21
|
-
} from "./util"
|
|
22
|
-
import { GitCaller } from "./git-caller"
|
|
23
|
-
import { emptyGitCommitHash } from "./constants"
|
|
24
|
-
import { resolve, isAbsolute, join } from "path"
|
|
25
|
-
import { performance } from "perf_hooks"
|
|
26
|
-
import { getAuthorSet, hydrateData } from "./hydrate.server"
|
|
27
|
-
import {} from "@remix-run/node"
|
|
28
|
-
import { getArgs } from "./args.server"
|
|
29
|
-
import ignore from "ignore"
|
|
30
|
-
import { applyIgnore, applyMetrics, initMetrics, TreeCleanup } from "./postprocessing.server"
|
|
31
|
-
import latestVersion from "latest-version"
|
|
32
|
-
import pkg from "../../package.json"
|
|
33
|
-
import { getCoAuthors } from "./coauthors.server"
|
|
34
|
-
import { exec } from "child_process"
|
|
35
|
-
|
|
36
|
-
let repoDir = "."
|
|
37
|
-
|
|
38
|
-
export async function findBranchHead(repo: string, branch: string | null) {
|
|
39
|
-
if (branch === null) branch = await getCurrentBranch(repo)
|
|
40
|
-
|
|
41
|
-
const gitFolder = join(repo, ".git")
|
|
42
|
-
if (!fsSync.existsSync(gitFolder)) {
|
|
43
|
-
throw Error(`${repo} is not a git repository`)
|
|
44
|
-
}
|
|
45
|
-
// Find file containing the branch head
|
|
46
|
-
const branchPath = join(gitFolder, "refs/heads/" + branch)
|
|
47
|
-
const absolutePath = join(process.cwd(), branchPath)
|
|
48
|
-
log.debug("Looking for branch head at " + absolutePath)
|
|
49
|
-
|
|
50
|
-
const branchHead = (await fs.readFile(branchPath, "utf-8")).trim()
|
|
51
|
-
log.debug(`${branch} -> [commit]${branchHead}`)
|
|
52
|
-
if (!branchHead) throw Error("Branch head not found")
|
|
53
|
-
|
|
54
|
-
return [branchHead, branch]
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export async function analyzeCommitLight(hash: string): Promise<GitCommitObjectLight> {
|
|
58
|
-
const rawContent = await GitCaller.getInstance().catFileCached(hash)
|
|
59
|
-
const commitRegex =
|
|
60
|
-
/tree (?<tree>.*)\s*(?:parent (?<parent>.*)\s*)?(?:parent (?<parent2>.*)\s*)?author (?<authorName>.*?) <(?<authorEmail>.*?)> (?<authorTimeStamp>\d*?) (?<authorTimeZone>.*?)\s*committer (?<committerName>.*?) <(?<committerEmail>.*?)> (?<committerTimeStamp>\d*?) (?<committerTimeZone>.*)\s*(?:gpgsig (?:.|\s)*?-----END PGP SIGNATURE-----)?\s*(?<message>.*)\s*(?<description>(?:.|\s)*)/gm
|
|
61
|
-
|
|
62
|
-
const match = commitRegex.exec(rawContent)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
"
|
|
204
|
-
"
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
if
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
.
|
|
237
|
-
.
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
await
|
|
245
|
-
|
|
246
|
-
await
|
|
247
|
-
|
|
248
|
-
await
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
"
|
|
255
|
-
"
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
"
|
|
262
|
-
"
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
await resetGitSetting(repoDir, "
|
|
267
|
-
await resetGitSetting(repoDir, "diff.
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
`
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
}
|
|
1
|
+
import fsSync, { promises as fs } from "fs"
|
|
2
|
+
import {
|
|
3
|
+
GitBlobObject,
|
|
4
|
+
GitCommitObject,
|
|
5
|
+
GitCommitObjectLight,
|
|
6
|
+
GitTreeObject,
|
|
7
|
+
AnalyzerData,
|
|
8
|
+
AnalyzerDataInterfaceVersion,
|
|
9
|
+
TruckUserConfig,
|
|
10
|
+
} from "./model"
|
|
11
|
+
import { log, setLogLevel } from "./log.server"
|
|
12
|
+
import {
|
|
13
|
+
describeAsyncJob,
|
|
14
|
+
formatMs,
|
|
15
|
+
writeRepoToFile,
|
|
16
|
+
getCurrentBranch,
|
|
17
|
+
getRepoName,
|
|
18
|
+
getDefaultGitSettingValue,
|
|
19
|
+
resetGitSetting,
|
|
20
|
+
setGitSetting,
|
|
21
|
+
} from "./util"
|
|
22
|
+
import { GitCaller } from "./git-caller"
|
|
23
|
+
import { emptyGitCommitHash } from "./constants"
|
|
24
|
+
import { resolve, isAbsolute, join } from "path"
|
|
25
|
+
import { performance } from "perf_hooks"
|
|
26
|
+
import { getAuthorSet, hydrateData } from "./hydrate.server"
|
|
27
|
+
import {} from "@remix-run/node"
|
|
28
|
+
import { getArgs } from "./args.server"
|
|
29
|
+
import ignore from "ignore"
|
|
30
|
+
import { applyIgnore, applyMetrics, initMetrics, TreeCleanup } from "./postprocessing.server"
|
|
31
|
+
import latestVersion from "latest-version"
|
|
32
|
+
import pkg from "../../package.json"
|
|
33
|
+
import { getCoAuthors } from "./coauthors.server"
|
|
34
|
+
import { exec } from "child_process"
|
|
35
|
+
|
|
36
|
+
let repoDir = "."
|
|
37
|
+
|
|
38
|
+
export async function findBranchHead(repo: string, branch: string | null) {
|
|
39
|
+
if (branch === null) branch = await getCurrentBranch(repo)
|
|
40
|
+
|
|
41
|
+
const gitFolder = join(repo, ".git")
|
|
42
|
+
if (!fsSync.existsSync(gitFolder)) {
|
|
43
|
+
throw Error(`${repo} is not a git repository`)
|
|
44
|
+
}
|
|
45
|
+
// Find file containing the branch head
|
|
46
|
+
const branchPath = join(gitFolder, "refs/heads/" + branch)
|
|
47
|
+
const absolutePath = join(process.cwd(), branchPath)
|
|
48
|
+
log.debug("Looking for branch head at " + absolutePath)
|
|
49
|
+
|
|
50
|
+
const branchHead = (await fs.readFile(branchPath, "utf-8")).trim()
|
|
51
|
+
log.debug(`${branch} -> [commit]${branchHead}`)
|
|
52
|
+
if (!branchHead) throw Error("Branch head not found")
|
|
53
|
+
|
|
54
|
+
return [branchHead, branch]
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function analyzeCommitLight(hash: string): Promise<GitCommitObjectLight> {
|
|
58
|
+
const rawContent = await GitCaller.getInstance().catFileCached(hash)
|
|
59
|
+
const commitRegex =
|
|
60
|
+
/tree (?<tree>.*)\s*(?:parent (?<parent>.*)\s*)?(?:parent (?<parent2>.*)\s*)?author (?<authorName>.*?) <(?<authorEmail>.*?)> (?<authorTimeStamp>\d*?) (?<authorTimeZone>.*?)\s*committer (?<committerName>.*?) <(?<committerEmail>.*?)> (?<committerTimeStamp>\d*?) (?<committerTimeZone>.*)\s*(?:gpgsig (?:.|\s)*?-----END PGP SIGNATURE-----)?\s*(?<message>.*)\s*(?<description>(?:.|\s)*)/gm
|
|
61
|
+
log.debug("before match")
|
|
62
|
+
const match = commitRegex.exec(rawContent)
|
|
63
|
+
log.debug("after match: " + JSON.stringify(match))
|
|
64
|
+
const groups = match?.groups ?? {}
|
|
65
|
+
|
|
66
|
+
const tree = groups["tree"]
|
|
67
|
+
const parent = groups["parent"] ?? emptyGitCommitHash
|
|
68
|
+
const parent2 = groups["parent2"] ?? null
|
|
69
|
+
const author = {
|
|
70
|
+
name: groups["authorName"],
|
|
71
|
+
email: groups["authorEmail"],
|
|
72
|
+
timestamp: Number(groups["authorTimeStamp"]),
|
|
73
|
+
timezone: groups["authorTimeZone"],
|
|
74
|
+
}
|
|
75
|
+
const committer = {
|
|
76
|
+
name: groups["committerName"],
|
|
77
|
+
email: groups["committerEmail"],
|
|
78
|
+
timestamp: Number(groups["committerTimeStamp"]),
|
|
79
|
+
timezone: groups["committerTimeZone"],
|
|
80
|
+
}
|
|
81
|
+
const message = groups["message"]
|
|
82
|
+
const description = groups["description"]
|
|
83
|
+
const coauthors = description ? getCoAuthors(description) : []
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
type: "commit",
|
|
87
|
+
hash,
|
|
88
|
+
tree,
|
|
89
|
+
parent,
|
|
90
|
+
parent2,
|
|
91
|
+
author,
|
|
92
|
+
committer,
|
|
93
|
+
message,
|
|
94
|
+
description,
|
|
95
|
+
coauthors,
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export async function analyzeCommit(repoName: string, hash: string): Promise<GitCommitObject> {
|
|
100
|
+
if (hash === undefined) {
|
|
101
|
+
throw Error("Hash is required")
|
|
102
|
+
}
|
|
103
|
+
const { tree, ...commit } = await analyzeCommitLight(hash)
|
|
104
|
+
const commitObject = {
|
|
105
|
+
...commit,
|
|
106
|
+
tree: await analyzeTree(repoName, repoName, tree),
|
|
107
|
+
}
|
|
108
|
+
return commitObject
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function analyzeTree(path: string, name: string, hash: string): Promise<GitTreeObject> {
|
|
112
|
+
const rawContent = await GitCaller.getInstance().catFileCached(hash)
|
|
113
|
+
const entries = rawContent.split("\n").filter((x) => x.trim().length > 0)
|
|
114
|
+
|
|
115
|
+
const children: (GitTreeObject | GitBlobObject)[] = []
|
|
116
|
+
for await (const line of entries) {
|
|
117
|
+
const catFileRegex = /^.+?\s(?<type>\w+)\s(?<hash>.+?)\s+(?<name>.+?)\s*$/g
|
|
118
|
+
const groups = catFileRegex.exec(line)?.groups ?? {}
|
|
119
|
+
|
|
120
|
+
const type = groups["type"]
|
|
121
|
+
const hash = groups["hash"]
|
|
122
|
+
const name = groups["name"]
|
|
123
|
+
|
|
124
|
+
const newPath = [path, name].join("/")
|
|
125
|
+
log.debug(`Path: ${newPath}`)
|
|
126
|
+
|
|
127
|
+
switch (type) {
|
|
128
|
+
case "tree":
|
|
129
|
+
children.push(await analyzeTree(newPath, name, hash))
|
|
130
|
+
break
|
|
131
|
+
case "blob":
|
|
132
|
+
children.push({
|
|
133
|
+
type: "blob",
|
|
134
|
+
hash,
|
|
135
|
+
path: newPath,
|
|
136
|
+
name,
|
|
137
|
+
content: await GitCaller.getInstance().catFileCached(hash),
|
|
138
|
+
blameAuthors: await GitCaller.getInstance().parseBlame(newPath),
|
|
139
|
+
})
|
|
140
|
+
break
|
|
141
|
+
default:
|
|
142
|
+
throw new Error(` type ${type}`)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
type: "tree",
|
|
148
|
+
path,
|
|
149
|
+
name,
|
|
150
|
+
hash,
|
|
151
|
+
children,
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function getCommandLine() {
|
|
156
|
+
switch (process.platform) {
|
|
157
|
+
case "darwin":
|
|
158
|
+
return "open" // MacOS
|
|
159
|
+
case "win32":
|
|
160
|
+
return "start" // Windows
|
|
161
|
+
default:
|
|
162
|
+
return "xdg-open" // Linux
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function openFile(path: string) {
|
|
167
|
+
path = path.split("/").slice(1).join("/") ?? path.split("\\").slice(1).join("\\")
|
|
168
|
+
exec(`${getCommandLine()} ${resolve(repoDir, path)}`).stderr?.on("data", (e) => {
|
|
169
|
+
// TODO show error in UI
|
|
170
|
+
log.error(`Cannot open file ${resolve(repoDir, path)}: ${e}`)
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export async function updateTruckConfig(repoDir: string, updaterFn: (tc: TruckUserConfig) => TruckUserConfig) {
|
|
175
|
+
const truckConfigPath = resolve(repoDir, "truckconfig.json")
|
|
176
|
+
let currentConfig: TruckUserConfig = {}
|
|
177
|
+
try {
|
|
178
|
+
const configFileContents = await fs.readFile(truckConfigPath, "utf-8")
|
|
179
|
+
if (configFileContents) currentConfig = JSON.parse(configFileContents)
|
|
180
|
+
} catch (e) {}
|
|
181
|
+
const updatedConfig = updaterFn(currentConfig)
|
|
182
|
+
await fs.writeFile(truckConfigPath, JSON.stringify(updatedConfig, null, 2))
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export async function analyze(useCache = true) {
|
|
186
|
+
const args = await getArgs()
|
|
187
|
+
GitCaller.initInstance(args.path)
|
|
188
|
+
|
|
189
|
+
if (args?.log) {
|
|
190
|
+
setLogLevel(args.log as string)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
repoDir = args.path
|
|
194
|
+
if (!isAbsolute(repoDir)) repoDir = resolve(process.cwd(), repoDir)
|
|
195
|
+
|
|
196
|
+
const branch = args.branch
|
|
197
|
+
|
|
198
|
+
const hiddenFiles = args.hiddenFiles
|
|
199
|
+
|
|
200
|
+
const start = performance.now()
|
|
201
|
+
const [branchHead, branchName] = await describeAsyncJob(
|
|
202
|
+
() => findBranchHead(repoDir, branch),
|
|
203
|
+
"Finding branch head",
|
|
204
|
+
"Found branch head",
|
|
205
|
+
"Error finding branch head"
|
|
206
|
+
)
|
|
207
|
+
const repoName = getRepoName(repoDir)
|
|
208
|
+
|
|
209
|
+
let data: AnalyzerData | null = null
|
|
210
|
+
|
|
211
|
+
const dataPath = getOutPathFromRepoAndBranch(repoName, branchName)
|
|
212
|
+
if (fsSync.existsSync(dataPath)) {
|
|
213
|
+
const path = getOutPathFromRepoAndBranch(repoName, branchName)
|
|
214
|
+
const cachedData = JSON.parse(await fs.readFile(path, "utf8")) as AnalyzerData
|
|
215
|
+
|
|
216
|
+
// Check if the current branchHead matches the hash of the analyzed commit from the cache
|
|
217
|
+
const branchHeadMatches = branchHead === cachedData.commit.hash
|
|
218
|
+
|
|
219
|
+
// Check if the data uses the most recent analyzer data interface
|
|
220
|
+
const dataVersionMatches = cachedData.interfaceVersion === AnalyzerDataInterfaceVersion
|
|
221
|
+
|
|
222
|
+
const cacheConditions = {
|
|
223
|
+
branchHeadMatches,
|
|
224
|
+
dataVersionMatches,
|
|
225
|
+
refresh: !useCache,
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Only return cached data if every criteria is met
|
|
229
|
+
if (Object.values(cacheConditions).every(Boolean)) {
|
|
230
|
+
data = {
|
|
231
|
+
...cachedData,
|
|
232
|
+
hiddenFiles,
|
|
233
|
+
}
|
|
234
|
+
} else {
|
|
235
|
+
const reasons = Object.entries(cacheConditions)
|
|
236
|
+
.filter(([, value]) => !value)
|
|
237
|
+
.map(([key, value]) => `${key}: ${value}`)
|
|
238
|
+
.join(", ")
|
|
239
|
+
log.info(`Reanalyzing, since the following cache conditions were not met: ${reasons}`)
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (data === null) {
|
|
244
|
+
const quotePathDefaultValue = await getDefaultGitSettingValue(repoDir, "core.quotepath")
|
|
245
|
+
await setGitSetting(repoDir, "core.quotePath", "off")
|
|
246
|
+
const renamesDefaultValue = await getDefaultGitSettingValue(repoDir, "diff.renames")
|
|
247
|
+
await setGitSetting(repoDir, "diff.renames", "true")
|
|
248
|
+
const renameLimitDefaultValue = await getDefaultGitSettingValue(repoDir, "diff.renameLimit")
|
|
249
|
+
await setGitSetting(repoDir, "diff.renameLimit", "1000000")
|
|
250
|
+
|
|
251
|
+
const runDateEpoch = Date.now()
|
|
252
|
+
const repoTree = await describeAsyncJob(
|
|
253
|
+
() => analyzeCommit(repoName, branchHead),
|
|
254
|
+
"Analyzing commit tree",
|
|
255
|
+
"Commit tree analyzed",
|
|
256
|
+
"Error analyzing commit tree"
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
const hydratedRepoTree = await describeAsyncJob(
|
|
260
|
+
() => hydrateData(repoDir, repoTree),
|
|
261
|
+
"Hydrating commit tree",
|
|
262
|
+
"Commit tree hydrated",
|
|
263
|
+
"Error hydrating commit tree"
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
await resetGitSetting(repoDir, "core.quotepath", quotePathDefaultValue)
|
|
267
|
+
await resetGitSetting(repoDir, "diff.renames", renamesDefaultValue)
|
|
268
|
+
await resetGitSetting(repoDir, "diff.renameLimit", renameLimitDefaultValue)
|
|
269
|
+
|
|
270
|
+
const defaultOutPath = getOutPathFromRepoAndBranch(repoName, branchName)
|
|
271
|
+
let outPath = resolve((args.out as string) ?? defaultOutPath)
|
|
272
|
+
if (!isAbsolute(outPath)) outPath = resolve(process.cwd(), outPath)
|
|
273
|
+
|
|
274
|
+
let latestV: string | undefined
|
|
275
|
+
|
|
276
|
+
try {
|
|
277
|
+
latestV = await latestVersion(pkg.name)
|
|
278
|
+
} catch {}
|
|
279
|
+
|
|
280
|
+
const authorUnions = args.unionedAuthors as string[][]
|
|
281
|
+
data = {
|
|
282
|
+
cached: false,
|
|
283
|
+
hiddenFiles,
|
|
284
|
+
authors: getAuthorSet(),
|
|
285
|
+
repo: repoName,
|
|
286
|
+
branch: branchName,
|
|
287
|
+
commit: hydratedRepoTree,
|
|
288
|
+
authorUnions: authorUnions,
|
|
289
|
+
interfaceVersion: AnalyzerDataInterfaceVersion,
|
|
290
|
+
currentVersion: pkg.version,
|
|
291
|
+
latestVersion: latestV,
|
|
292
|
+
lastRunEpoch: runDateEpoch,
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
await describeAsyncJob(
|
|
296
|
+
() =>
|
|
297
|
+
writeRepoToFile(outPath, {
|
|
298
|
+
...data,
|
|
299
|
+
cached: true,
|
|
300
|
+
} as AnalyzerData),
|
|
301
|
+
"Writing data to file",
|
|
302
|
+
`Wrote data to ${resolve(outPath)}`,
|
|
303
|
+
`Error writing data to file ${outPath}`
|
|
304
|
+
)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const truckignore = ignore().add(hiddenFiles)
|
|
308
|
+
data.commit.tree = applyIgnore(data.commit.tree, truckignore)
|
|
309
|
+
TreeCleanup(data.commit.tree)
|
|
310
|
+
initMetrics(data)
|
|
311
|
+
data.commit.tree = applyMetrics(data, data.commit.tree)
|
|
312
|
+
|
|
313
|
+
const stop = performance.now()
|
|
314
|
+
|
|
315
|
+
log.raw(`\nDone in ${formatMs(stop - start)}`)
|
|
316
|
+
|
|
317
|
+
return data
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function getOutPathFromRepoAndBranch(repoName: string, branchName: string) {
|
|
321
|
+
return resolve(__dirname, "..", ".temp", repoName, `${branchName}.json`)
|
|
322
|
+
}
|