|
@@ -2,6 +2,7 @@ const API = require("../../lib/API")
|
|
|
const AccessControl = require("../../lib/AccessControl")
|
|
|
const { BaseStdResponse } = require("../../BaseStdResponse")
|
|
|
const db = require("../../plugin/DataBase/db")
|
|
|
+const redis = require('../../plugin/DataBase/Redis')
|
|
|
const simpleGit = require('simple-git')
|
|
|
|
|
|
class GitContributors extends API {
|
|
@@ -14,24 +15,35 @@ class GitContributors extends API {
|
|
|
|
|
|
async analyzeGitContributors(repoPath) {
|
|
|
try {
|
|
|
- const git = simpleGit()
|
|
|
- await git.cwd(repoPath)
|
|
|
-
|
|
|
- // 获取详细日志,带上插入删除统计并排除 merge 提交
|
|
|
- const logs = await git.raw([
|
|
|
- "log",
|
|
|
- "--no-merges",
|
|
|
- "--pretty=format:%H|%an|%ae",
|
|
|
- "--shortstat"
|
|
|
- ])
|
|
|
-
|
|
|
+ // 检查 Redis 中是否已存在缓存日志
|
|
|
+ const redisKey = `gitLogs:${repoPath}`
|
|
|
+ const cachedLogs = await redis.get(redisKey)
|
|
|
+
|
|
|
+ let logs
|
|
|
+ if (cachedLogs)
|
|
|
+ logs = JSON.parse(cachedLogs)
|
|
|
+ else {
|
|
|
+ const git = simpleGit()
|
|
|
+ await git.cwd(repoPath)
|
|
|
+
|
|
|
+ // 获取详细日志,带上插入删除统计并排除 merge 提交
|
|
|
+ logs = await git.raw([
|
|
|
+ "log",
|
|
|
+ "--no-merges",
|
|
|
+ "--pretty=format:%H|%an|%ae",
|
|
|
+ "--shortstat"
|
|
|
+ ])
|
|
|
+
|
|
|
+ redis.set(redisKey, 86400, JSON.stringify(logs))
|
|
|
+ }
|
|
|
+
|
|
|
const lines = logs.split("\n")
|
|
|
const contributors = {}
|
|
|
let currentAuthor = null
|
|
|
-
|
|
|
+
|
|
|
for (let i = 0; i < lines.length; i++) {
|
|
|
const line = lines[i].trim()
|
|
|
-
|
|
|
+
|
|
|
if (line.includes("|")) {
|
|
|
const [commitHash, name, email] = line.split("|")
|
|
|
currentAuthor = email // 用邮箱作为唯一 key 更稳妥
|
|
@@ -47,7 +59,7 @@ class GitContributors extends API {
|
|
|
}
|
|
|
}
|
|
|
contributors[currentAuthor].commits += 1
|
|
|
-
|
|
|
+
|
|
|
// 接下来的 shortstat 统计信息在下一行
|
|
|
const statLine = lines[i + 1]?.trim() || ""
|
|
|
const statMatch = {
|
|
@@ -55,31 +67,31 @@ class GitContributors extends API {
|
|
|
insertions: statLine.match(/(\d+) insertion[s]?\(\+\)/),
|
|
|
deletions: statLine.match(/(\d+) deletion[s]?\(-\)/),
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
const files = statMatch.filesChanged ? parseInt(statMatch.filesChanged[1], 10) : 0
|
|
|
const insertions = statMatch.insertions ? parseInt(statMatch.insertions[1], 10) : 0
|
|
|
const deletions = statMatch.deletions ? parseInt(statMatch.deletions[1], 10) : 0
|
|
|
-
|
|
|
+
|
|
|
contributors[currentAuthor].filesChanged += files
|
|
|
contributors[currentAuthor].linesAdded += insertions
|
|
|
contributors[currentAuthor].linesDeleted += deletions
|
|
|
contributors[currentAuthor].linesChanged += insertions + deletions
|
|
|
-
|
|
|
+
|
|
|
if (statLine) i++ // 跳过 shortstat 行
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
const sorted = Object.values(contributors).sort(
|
|
|
(a, b) => b.linesChanged - a.linesChanged || b.commits - a.commits
|
|
|
)
|
|
|
-
|
|
|
+
|
|
|
return sorted
|
|
|
} catch (err) {
|
|
|
console.error("分析 Git 贡献者失败:", err)
|
|
|
throw new Error("分析 Git 贡献者失败")
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
|
|
|
async onRequest(req, res) {
|
|
|
let { uuid, session, id } = req.query
|