wukong-gitlog-cli 0.0.9 → 0.0.11

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/CHANGELOG.md CHANGED
@@ -2,6 +2,21 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [0.0.11](https://github.com/tomatobybike/wukong-gitlog-cli/compare/v0.0.10...v0.0.11) (2025-11-28)
6
+
7
+
8
+ ### Features
9
+
10
+ * 🎸 each hour commit ([029ff72](https://github.com/tomatobybike/wukong-gitlog-cli/commit/029ff72636a47bcb5e77b7fb5acf1ca31d9cf9f3))
11
+
12
+ ### [0.0.10](https://github.com/tomatobybike/wukong-gitlog-cli/compare/v0.0.9...v0.0.10) (2025-11-28)
13
+
14
+
15
+ ### Features
16
+
17
+ * 🎸 each hour percent ([4f1a7e7](https://github.com/tomatobybike/wukong-gitlog-cli/commit/4f1a7e75a1c0d83c92aabf42e97932421c349b8d))
18
+ * 🎸 hourline ([9035ecb](https://github.com/tomatobybike/wukong-gitlog-cli/commit/9035ecb33295fa5f4ff64bbd9b9281c543247683))
19
+
5
20
  ### [0.0.9](https://github.com/tomatobybike/wukong-gitlog-cli/compare/v0.0.8...v0.0.9) (2025-11-28)
6
21
 
7
22
  ### [0.0.8](https://github.com/tomatobybike/wukong-gitlog-cli/compare/v0.0.7...v0.0.8) (2025-11-28)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wukong-gitlog-cli",
3
- "version": "0.0.9",
3
+ "version": "0.0.11",
4
4
  "description": "Advanced Git commit log exporter with Excel/JSON/TXT output, grouping, stats and CLI.",
5
5
  "keywords": [
6
6
  "git",
package/src/overtime.mjs CHANGED
@@ -64,6 +64,10 @@ export function analyzeOvertime(records, opts = {}) {
64
64
  hd.init('CN');
65
65
  }
66
66
 
67
+ // 新增:每小时分布统计(全部提交)
68
+ const hourlyCommits = Array(24).fill(0);
69
+ // 新增:每小时分布统计(仅加班提交)
70
+ const hourlyOvertimeCommits = Array(24).fill(0);
67
71
  records.forEach((r) => {
68
72
  const dt = parseCommitDate(r.date);
69
73
  if (!dt || !dt.isValid()) return; // skip
@@ -72,6 +76,15 @@ export function analyzeOvertime(records, opts = {}) {
72
76
  const isHoliday = !!hd.isHoliday(dt.toDate());
73
77
  const isNonWork = isWeekend(dt) || isHoliday;
74
78
 
79
+ // 每小时分布(全部提交)
80
+ const hour = dt.hour();
81
+ if (hour >= 0 && hour < 24) {
82
+ hourlyCommits[hour]++;
83
+ if (outside) {
84
+ hourlyOvertimeCommits[hour]++;
85
+ }
86
+ }
87
+
75
88
  if (outside) {
76
89
  outsideWorkCount++;
77
90
  // 记录最新的加班提交
@@ -134,6 +147,10 @@ export function analyzeOvertime(records, opts = {}) {
134
147
  // sort perAuthor by outsideWorkRate desc
135
148
  perAuthor.sort((a, b) => b.outsideWorkRate - a.outsideWorkRate || b.total - a.total);
136
149
 
150
+ // 新增:每小时分布百分比(全部提交)
151
+ const hourlyPercent = hourlyCommits.map(v => total ? +(v / total).toFixed(3) : 0);
152
+ // 新增:每小时分布百分比(仅加班提交)
153
+ const hourlyOvertimePercent = hourlyOvertimeCommits.map(v => total ? +(v / total).toFixed(3) : 0);
137
154
  return {
138
155
  total,
139
156
  outsideWorkCount,
@@ -148,16 +165,20 @@ export function analyzeOvertime(records, opts = {}) {
148
165
  latestOutsideCommit: latestOutsideCommit || null,
149
166
  startHour,
150
167
  endHour,
151
- lunchStart,
152
- lunchEnd,
153
- country,
154
- holidayCount,
155
- holidayRate: total ? +(holidayCount / total).toFixed(3) : 0,
168
+ lunchStart,
169
+ lunchEnd,
170
+ country,
171
+ holidayCount,
172
+ holidayRate: total ? +(holidayCount / total).toFixed(3) : 0,
173
+ hourlyCommits,
174
+ hourlyPercent,
175
+ hourlyOvertimeCommits,
176
+ hourlyOvertimePercent,
156
177
  };
157
178
  }
158
179
 
159
180
  export function renderOvertimeText(stats) {
160
- const { total, outsideWorkCount, nonWorkdayCount, holidayCount, outsideWorkRate, nonWorkdayRate, holidayRate, perAuthor, startHour, endHour, lunchStart, lunchEnd, country } = stats;
181
+ const { total, outsideWorkCount, nonWorkdayCount, holidayCount, outsideWorkRate, nonWorkdayRate, holidayRate, perAuthor, startHour, endHour, lunchStart, lunchEnd, country, hourlyOvertimeCommits = [], hourlyOvertimePercent = [] } = stats;
161
182
  const { startCommit, endCommit, latestCommit, latestOutsideCommit } = stats;
162
183
  const lines = [];
163
184
 
@@ -206,6 +227,18 @@ export function renderOvertimeText(stats) {
206
227
  lines.push(`下班时间(工作时间外)提交数:${outsideWorkCount},占比:${(outsideWorkRate * 100).toFixed(1)}%`);
207
228
  lines.push(`非工作日(周末)提交数:${nonWorkdayCount},占比:${(nonWorkdayRate * 100).toFixed(1)}%`);
208
229
  lines.push('');
230
+ lines.push('每小时加班分布(工作时间外提交数/占比):');
231
+ const hourLine = ' Hour | OvertimeCount | Percent';
232
+ lines.push(hourLine);
233
+ lines.push(' -----|--------------|--------');
234
+ for (let h = 0; h < 24; h++) {
235
+ const cnt = hourlyOvertimeCommits[h] || 0;
236
+ if (cnt > 0) {
237
+ const pct = hourlyOvertimePercent[h] ? (hourlyOvertimePercent[h] * 100).toFixed(1) : '0.0';
238
+ lines.push(` ${String(h).padStart(2, '0')} | ${String(cnt).padStart(12, ' ')} | ${pct.padStart(6, ' ')}%`);
239
+ }
240
+ }
241
+ lines.push('');
209
242
  lines.push('按人员统计:');
210
243
  // header
211
244
  const header = ` ${padDisplayEnd('Name', cols.name)} | ${padDisplayStart('总数', cols.total)} | ${padDisplayStart('下班外数', cols.outside)} | ${padDisplayStart('下班外占比', cols.outsideRate)} | ${padDisplayStart('非工作日数', cols.nonWork)} | ${padDisplayStart('非工作日占比', cols.nonWorkRate)} | ${padDisplayStart('假日数', cols.holiday)} | ${padDisplayStart('假日占比', cols.holidayRate)}`;
@@ -224,7 +257,7 @@ export function renderOvertimeText(stats) {
224
257
  }
225
258
 
226
259
  export function renderOvertimeTab(stats) {
227
- const { total, outsideWorkCount, nonWorkdayCount, holidayCount, outsideWorkRate, nonWorkdayRate, holidayRate, perAuthor, startHour, endHour, lunchStart, lunchEnd, country } = stats;
260
+ const { total, outsideWorkCount, nonWorkdayCount, holidayCount, outsideWorkRate, nonWorkdayRate, holidayRate, perAuthor, startHour, endHour, lunchStart, lunchEnd, country, hourlyOvertimeCommits = [], hourlyOvertimePercent = [] } = stats;
228
261
  const { startCommit, endCommit, latestCommit, latestOutsideCommit } = stats;
229
262
  const rows = [];
230
263
  rows.push(`总提交数:\t${total}`);
@@ -236,6 +269,16 @@ export function renderOvertimeTab(stats) {
236
269
  rows.push(`下班时间(工作时间外)提交数:\t${outsideWorkCount}\t占比:\t${(outsideWorkRate * 100).toFixed(1)}%`);
237
270
  rows.push(`非工作日(周末)提交数:\t${nonWorkdayCount}\t占比:\t${(nonWorkdayRate * 100).toFixed(1)}%`);
238
271
  rows.push('');
272
+ rows.push('每小时加班分布(工作时间外提交数/占比):');
273
+ rows.push(['Hour', 'OvertimeCount', 'Percent'].join('\t'));
274
+ for (let h = 0; h < 24; h++) {
275
+ const cnt = hourlyOvertimeCommits[h] || 0;
276
+ if (cnt > 0) {
277
+ const pct = hourlyOvertimePercent[h] ? (hourlyOvertimePercent[h] * 100).toFixed(1) : '0.0';
278
+ rows.push([String(h).padStart(2, '0'), cnt, `${pct}%`].join('\t'));
279
+ }
280
+ }
281
+ rows.push('');
239
282
  rows.push(['Name', '总数', '下班外数', '下班外占比', '非工作日数', '非工作日占比', '假日数', '假日占比'].join('\t'));
240
283
  perAuthor.forEach((p) => {
241
284
  rows.push([p.name || '-', p.total, p.outsideWorkCount, `${(p.outsideWorkRate * 100).toFixed(1)}%`, p.nonWorkdayCount, `${(p.nonWorkdayRate * 100).toFixed(1)}%`, p.holidayCount || 0, `${((p.holidayCount || 0) / p.total * 100).toFixed(1)}%`].join('\t'));
@@ -252,12 +295,22 @@ function escapeCsv(v) {
252
295
  }
253
296
 
254
297
  export function renderOvertimeCsv(stats) {
255
- const { perAuthor, latestOutsideCommit, country } = stats;
298
+ const { perAuthor, latestOutsideCommit, country, hourlyOvertimeCommits = [], hourlyOvertimePercent = [], total = 0 } = stats;
256
299
  const rows = [];
257
300
  if (latestOutsideCommit) {
258
301
  rows.push(`# 加班最晚的一次提交,Hash,Author,Date,Message`);
259
302
  rows.push(`# ,${escapeCsv(latestOutsideCommit.hash)},${escapeCsv(latestOutsideCommit.author)},${escapeCsv(formatDateForCountry(latestOutsideCommit.date, country))},${escapeCsv(latestOutsideCommit.message)}`);
260
303
  }
304
+ rows.push('');
305
+ rows.push('Hour,OvertimeCount,Percent');
306
+ for (let h = 0; h < 24; h++) {
307
+ const cnt = hourlyOvertimeCommits[h] || 0;
308
+ if (cnt > 0) {
309
+ const pct = hourlyOvertimePercent[h] ? (hourlyOvertimePercent[h] * 100).toFixed(1) : '0.0';
310
+ rows.push(`${h.toString().padStart(2, '0')},${cnt},${pct}%`);
311
+ }
312
+ }
313
+ rows.push('');
261
314
  rows.push('Name,Total,OutsideCount,OutsideRate,NonWorkdayCount,NonWorkdayRate,HolidayCount,HolidayRate');
262
315
  perAuthor.forEach((p) => {
263
316
  rows.push(`${escapeCsv(p.name)},${p.total},${p.outsideWorkCount},${(p.outsideWorkRate * 100).toFixed(1)}%,${p.nonWorkdayCount},${(p.nonWorkdayRate * 100).toFixed(1)}%,${p.holidayCount || 0},${((p.holidayCount || 0) / p.total * 100).toFixed(1)}%`);