diff --git a/packages/analysis-engine/src/index.ts b/packages/analysis-engine/src/index.ts index 91bf1899..a35367c2 100644 --- a/packages/analysis-engine/src/index.ts +++ b/packages/analysis-engine/src/index.ts @@ -53,7 +53,9 @@ export class AnalysisEngine { if (this.isDebugMode) console.log("baseBranchName: ", this.baseBranchName); const commitRaws = getCommitRaws(this.gitLog); - if (this.isDebugMode) console.log("commitRaws: ", commitRaws); + if (this.isDebugMode){ + console.log("commitRaws: ", commitRaws); + } const commitDict = buildCommitDict(commitRaws); if (this.isDebugMode) console.log("commitDict: ", commitDict); diff --git a/packages/vscode/.eslintrc.json b/packages/vscode/.eslintrc.json index 093a13f7..25d2a036 100644 --- a/packages/vscode/.eslintrc.json +++ b/packages/vscode/.eslintrc.json @@ -12,7 +12,8 @@ } ], "simple-import-sort/exports": "error", - "no-duplicate-imports": "error" + "no-duplicate-imports": "error", + "import/no-commonjs": "off" }, "overrides": [ { diff --git a/packages/vscode/src/extension.ts b/packages/vscode/src/extension.ts index 1513e06e..34f5fc41 100644 --- a/packages/vscode/src/extension.ts +++ b/packages/vscode/src/extension.ts @@ -7,6 +7,7 @@ import { GithubTokenUndefinedError, WorkspacePathUndefinedError } from "./errors import { deleteGithubToken, getGithubToken, setGithubToken } from "./setting-repository"; import { mapClusterNodesFrom } from "./utils/csm.mapper"; import { + fetchGitLogInParallel, findGit, getBranches, getCurrentBranchName, @@ -77,6 +78,7 @@ export async function activate(context: vscode.ExtensionContext) { }; const initialBaseBranchName = await fetchCurrentBranch(); + const fetchClusterNodes = async (baseBranchName = initialBaseBranchName) => { const gitLog = await getGitLog(gitPath, currentWorkspacePath); const { owner, repo: initialRepo } = getRepo(gitConfig); diff --git a/packages/vscode/src/utils/git.util.ts b/packages/vscode/src/utils/git.util.ts index feb0ecb4..e77bd9c0 100644 --- a/packages/vscode/src/utils/git.util.ts +++ b/packages/vscode/src/utils/git.util.ts @@ -1,6 +1,8 @@ import * as cp from "child_process"; import * as fs from "fs"; +import os from "os"; import * as path from "path"; +import { Worker } from "worker_threads"; export interface GitExecutable { readonly path: string; @@ -197,6 +199,70 @@ export async function getGitLog(gitPath: string, currentWorkspacePath: string): }); } +export async function getLogCount(gitPath: string, currentWorkspacePath: string): Promise { + const BASE_10 = 10; + return new Promise((resolve, reject) => { + const args = ["rev-list", "--count", "--all"]; + + resolveSpawnOutput( + cp.spawn(gitPath, args, { + cwd: currentWorkspacePath, + env: Object.assign({}, process.env), + }) + ).then(([status, stdout, stderr]) => { + const { code, error } = status; + + if (code === 0 && !error) { + const commitCount = parseInt(stdout.toString().trim(), BASE_10); + resolve(commitCount); + } else { + reject(stderr); + } + }); + }); +} + +export async function fetchGitLogInParallel(gitPath: string, currentWorkspacePath: string): Promise { + const numCores = os.cpus().length; + + const totalCnt = await getLogCount(gitPath, currentWorkspacePath); + let numberOfThreads = 1; + + const taskThreshold = 1000; + const coreCountThreshold = 4; + + if (totalCnt > taskThreshold) { + if (numCores < coreCountThreshold) numberOfThreads = 2; + else numberOfThreads = 3; + } + + const chunkSize = Math.ceil(totalCnt / numberOfThreads); + const promises: Promise[] = []; + + for (let i = 0; i < numberOfThreads; i++) { + const skipCount = i * chunkSize; + const limitCount = chunkSize; + + const worker = new Worker(path.resolve(__dirname, "./worker.js"), { + workerData: { + gitPath, + currentWorkspacePath, + skipCount, + limitCount, + }, + }); + + promises.push( + new Promise((resolve, reject) => { + worker.on("message", resolve); + worker.on("error", reject); + }) + ); + } + + return Promise.all(promises).then((logs) => logs.join("\n")); +} + export async function getGitConfig( gitPath: string, currentWorkspacePath: string, diff --git a/packages/vscode/src/utils/git.worker.ts b/packages/vscode/src/utils/git.worker.ts new file mode 100644 index 00000000..f5c9da26 --- /dev/null +++ b/packages/vscode/src/utils/git.worker.ts @@ -0,0 +1,59 @@ +import * as cp from "child_process"; +import { parentPort, workerData } from "worker_threads"; + +import { resolveSpawnOutput } from "./git.util"; + +const { gitPath, currentWorkspacePath, skipCount, limitCount } = workerData; + +async function getPartialGitLog() { + const gitLogFormat = + "%n%n" + + [ + "%H", // commit hash (id) + "%P", // parent hashes + "%D", // ref names (branches, tags) + "%an", // author name + "%ae", // author email + "%ad", // author date + "%cn", // committer name + "%ce", // committer email + "%cd", // committer date + "%w(0,0,4)%s", // commit message subject + "%b", // commit message body + ].join("%n"); + + const args = [ + "--no-pager", + "log", + "--all", + "--parents", + "--numstat", + "--date-order", + `--pretty=format:${gitLogFormat}`, + "--decorate", + "-c", + `--skip=${skipCount}`, + `-n ${limitCount}`, + ]; + + resolveSpawnOutput( + cp.spawn(gitPath, args, { + cwd: currentWorkspacePath, + env: Object.assign({}, process.env), + }) + ) + .then(([status, stdout, stderr]) => { + const { code, error } = status; + + if (code === 0 && !error && parentPort !== null) { + parentPort.postMessage(stdout.toString()); + } else { + if (parentPort !== null) parentPort.postMessage(stderr); + } + }) + .catch((error) => { + console.error("Spawn Error:", error); + }); +} + +getPartialGitLog(); diff --git a/packages/vscode/src/webview-loader.ts b/packages/vscode/src/webview-loader.ts index ce15056a..73c9ecf6 100644 --- a/packages/vscode/src/webview-loader.ts +++ b/packages/vscode/src/webview-loader.ts @@ -17,7 +17,6 @@ export default class WebviewLoader implements vscode.Disposable { const { fetchClusterNodes, fetchBranches, fetchCurrentBranch, fetchGithubInfo } = fetcher; const viewColumn = vscode.ViewColumn.One; - //캐시 초기화 console.log("Initialize cache data"); context.workspaceState.keys().forEach((key) => { context.workspaceState.update(key, undefined); @@ -37,13 +36,12 @@ export default class WebviewLoader implements vscode.Disposable { const { command, payload } = message; if (command === "fetchAnalyzedData" || command === "refresh") { - const baseBranchName = (payload && JSON.parse(payload)) ?? (await fetchCurrentBranch()); try { const baseBranchName = (payload && JSON.parse(payload)) ?? (await fetchCurrentBranch()); const storedAnalyzedData = context.workspaceState.get( `${ANALYZE_DATA_KEY}_${baseBranchName}` ); - let analyzedData = storedAnalyzedData; + analyzedData = storedAnalyzedData; if (!storedAnalyzedData) { console.log("No cache Data"); console.log("baseBranchName : ", baseBranchName); @@ -51,7 +49,6 @@ export default class WebviewLoader implements vscode.Disposable { context.workspaceState.update(`${ANALYZE_DATA_KEY}_${baseBranchName}`, analyzedData); } else console.log("Cache data exists"); - // 현재 캐싱된 Branch console.log("Current Stored data"); context.workspaceState.keys().forEach((key) => { console.log(key); @@ -76,10 +73,10 @@ export default class WebviewLoader implements vscode.Disposable { payload: branches, }); } - + if (command === "fetchGithubInfo") { const githubInfo = await fetchGithubInfo(); - await this.respondToMessage({ + await this.respondToMessage({ ...message, payload: githubInfo, }); diff --git a/packages/vscode/webpack.config.js b/packages/vscode/webpack.config.js index 80a33728..1941adb0 100644 --- a/packages/vscode/webpack.config.js +++ b/packages/vscode/webpack.config.js @@ -12,11 +12,14 @@ const extensionConfig = { target: "node", // vscode extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/ mode: "none", // this leaves the source code as close as possible to the original (when packaging we set this to 'production') - entry: "./src/extension.ts", // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ + entry: { + extension: "./src/extension.ts", // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ + worker: "./src/utils/git.worker.ts" + }, output: { // the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/ path: path.resolve(__dirname, "dist"), - filename: "extension.js", + filename: "[name].js", libraryTarget: "commonjs2", }, externals: {