pretty-git 0.1.2 → 0.1.3
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/README.md +114 -30
- data/README.ru.md +114 -30
- data/lib/pretty_git/analytics/churn.rb +75 -0
- data/lib/pretty_git/analytics/hotspots.rb +78 -0
- data/lib/pretty_git/analytics/ownership.rb +90 -0
- data/lib/pretty_git/app.rb +18 -16
- data/lib/pretty_git/cli.rb +1 -1
- data/lib/pretty_git/cli_helpers.rb +1 -1
- data/lib/pretty_git/render/console_renderer.rb +53 -16
- data/lib/pretty_git/render/csv_renderer.rb +19 -16
- data/lib/pretty_git/render/markdown_renderer.rb +35 -20
- data/lib/pretty_git/version.rb +1 -1
- metadata +4 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 84571ab2bdd16f92dc31c1883a70433a140253ee0d0c66f1079453fc30f34419
|
4
|
+
data.tar.gz: 532ee3a2e436163f12821073297dc013e874746c5ac269f89c455826566c9f27
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a0ba7918e1179ebfe3c4771c995b8b0eda978263be1fc6484f0f913e7b2129472272660e3c6dfb24a11f9d64300c26748d0b01fc14f0594e04e9b88cf244b96c
|
7
|
+
data.tar.gz: afaa201615eff920f1af0b86384964437fe7a119fc0d43d2a5ae10ee36211215b84c9607b92354646a20d691f0382f0b5d214385a214e8107596cdcf06eefcb2
|
data/CHANGELOG.md
CHANGED
@@ -7,6 +7,21 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
9
|
|
10
|
+
## [0.1.3] - 2025-08-14
|
11
|
+
### Added
|
12
|
+
- New analytics reports: `hotspots`, `churn`, `ownership` with sorting, scoring, and limits.
|
13
|
+
- Exporters: CSV and Markdown support for new reports with dynamic headers via mapping constants.
|
14
|
+
- Docs: Detailed sections for new reports in `README.md` and `README.ru.md` with usage and examples (CSV/JSON/YAML/XML).
|
15
|
+
|
16
|
+
### Changed
|
17
|
+
- Console: dispatching and rendering wired for new reports; consistent theming and width handling.
|
18
|
+
- CLI/App: unified analytics dispatch for all reports.
|
19
|
+
- Docs: public READMEs cleaned up from internal DR-* mentions; anchors and headings aligned (CSV).
|
20
|
+
|
21
|
+
### Fixed
|
22
|
+
- Minor documentation inaccuracies and anchor mismatches.
|
23
|
+
|
24
|
+
|
10
25
|
## [0.1.2] - 2025-08-13
|
11
26
|
### Added
|
12
27
|
- Languages report: support multiple metrics — `bytes`, `files`, `loc`; dynamic columns in Console/CSV/Markdown; color and percent fields in output.
|
data/README.md
CHANGED
@@ -14,7 +14,7 @@
|
|
14
14
|
<br>
|
15
15
|
</p>
|
16
16
|
|
17
|
-
Generator of rich reports for a local Git repository: summary, activity, authors, files, heatmap, languages. Output to Console and formats: JSON, CSV, Markdown, YAML, XML.
|
17
|
+
Generator of rich reports for a local Git repository: summary, activity, authors, files, heatmap, languages, hotspots, churn, ownership. Output to Console and formats: JSON, CSV, Markdown, YAML, XML.
|
18
18
|
|
19
19
|
— License: MIT.
|
20
20
|
|
@@ -35,10 +35,13 @@ Generator of rich reports for a local Git repository: summary, activity, authors
|
|
35
35
|
- [files — by files](#files--by-files)
|
36
36
|
- [heatmap — commit heatmap](#heatmap--commit-heatmap)
|
37
37
|
- [languages — languages](#languages--languages)
|
38
|
+
- [hotspots — hotspots (risky files)](#hotspots--hotspots-risky-files)
|
39
|
+
- [churn — code churn by file](#churn--code-churn-by-file)
|
40
|
+
- [ownership — code ownership](#ownership--code-ownership)
|
38
41
|
- [Exports](#exports)
|
39
42
|
- [Console](#console)
|
40
43
|
- [JSON](#json)
|
41
|
-
- [CSV
|
44
|
+
- [CSV](#csv)
|
42
45
|
- [Markdown](#markdown)
|
43
46
|
- [YAML](#yaml)
|
44
47
|
- [XML](#xml)
|
@@ -49,17 +52,17 @@ Generator of rich reports for a local Git repository: summary, activity, authors
|
|
49
52
|
- [Development](#development)
|
50
53
|
- [License](#license)
|
51
54
|
|
52
|
-
## Features
|
53
|
-
* **Reports**: `summary`, `activity`, `authors`, `files`, `heatmap`, `languages`.
|
55
|
+
## ✨ Features
|
56
|
+
* **Reports**: `summary`, `activity`, `authors`, `files`, `heatmap`, `languages`, `hotspots`, `churn`, `ownership`.
|
54
57
|
* **Filters**: branches, authors, paths, time period.
|
55
58
|
* **Exports**: `console`, `json`, `csv`, `md`, `yaml`, `xml`.
|
56
59
|
* **Output**: to stdout or file via `--out`.
|
57
60
|
|
58
|
-
## Requirements
|
61
|
+
## ⚙️ Requirements
|
59
62
|
* **Ruby**: >= 3.4 (recommended 3.4.x)
|
60
63
|
* **Git**: installed and available in `PATH`
|
61
64
|
|
62
|
-
## Installation
|
65
|
+
## 📦 Installation
|
63
66
|
|
64
67
|
### 🍺 Homebrew (recommended)
|
65
68
|
```bash
|
@@ -101,7 +104,7 @@ bundle install
|
|
101
104
|
bundle exec pretty-git --help
|
102
105
|
```
|
103
106
|
|
104
|
-
## Quick Start
|
107
|
+
## 🚀 Quick Start
|
105
108
|
```bash
|
106
109
|
# Repository summary to console
|
107
110
|
bundle exec bin/pretty-git summary .
|
@@ -114,21 +117,21 @@ bundle exec bin/pretty-git activity . --time-bucket week --since 2025-01-01 \
|
|
114
117
|
--paths app,lib --format csv --out activity.csv
|
115
118
|
```
|
116
119
|
|
117
|
-
## CLI and Options
|
120
|
+
## 🧰 CLI and Options
|
118
121
|
General form:
|
119
122
|
|
120
123
|
```bash
|
121
124
|
pretty-git <report> <repo_path> [options]
|
122
125
|
```
|
123
126
|
|
124
|
-
Available reports: `summary`, `activity`, `authors`, `files`, `heatmap`, `languages`.
|
127
|
+
Available reports: `summary`, `activity`, `authors`, `files`, `heatmap`, `languages`, `hotspots`, `churn`, `ownership`.
|
125
128
|
|
126
129
|
Key options:
|
127
130
|
* **--format, -f** `console|json|csv|md|yaml|xml` (default `console`)
|
128
131
|
* **--out, -o** Path to write output file
|
129
132
|
* **--limit, -l** Number of items shown; `all` or `0` — no limit
|
130
133
|
* **--time-bucket** `day|week|month` (for `activity`)
|
131
|
-
* **--since/--until** Date/time in ISO8601 or `YYYY-MM-DD`
|
134
|
+
* **--since/--until** Date/time in ISO8601 or `YYYY-MM-DD`
|
132
135
|
* **--branch** Multi-option, can be specified multiple times
|
133
136
|
* **--author/--exclude-author** Filter by authors
|
134
137
|
* **--path/--exclude-path** Filter by paths (comma-separated or repeated option)
|
@@ -165,15 +168,15 @@ pretty-git authors . --format csv --out authors.csv
|
|
165
168
|
* `1` — user error (unknown report/format, bad arguments)
|
166
169
|
* `2` — system error (git error etc.)
|
167
170
|
|
168
|
-
## Reports and Examples
|
171
|
+
## 📊 Reports and Examples
|
169
172
|
|
170
|
-
### summary — repository summary
|
173
|
+
### 🧭 summary — repository summary
|
171
174
|
```bash
|
172
175
|
pretty-git summary . --format json
|
173
176
|
```
|
174
177
|
Contains totals (commits, authors, additions, deletions) and top authors/files.
|
175
178
|
|
176
|
-
### activity — activity (day/week/month)
|
179
|
+
### 📆 activity — activity (day/week/month)
|
177
180
|
```bash
|
178
181
|
pretty-git activity . --time-bucket week --format csv
|
179
182
|
```
|
@@ -186,7 +189,7 @@ JSON example:
|
|
186
189
|
]
|
187
190
|
```
|
188
191
|
|
189
|
-
### authors — by authors
|
192
|
+
### 👤 authors — by authors
|
190
193
|
```bash
|
191
194
|
pretty-git authors . --format md --limit 10
|
192
195
|
```
|
@@ -199,7 +202,7 @@ Markdown example:
|
|
199
202
|
| Bob | b@example.com | 1 | 2 | 0 | 2.0 |
|
200
203
|
```
|
201
204
|
|
202
|
-
### files — by files
|
205
|
+
### 📁 files — by files
|
203
206
|
```bash
|
204
207
|
pretty-git files . --paths app,lib --format csv
|
205
208
|
```
|
@@ -219,7 +222,7 @@ XML example:
|
|
219
222
|
</files>
|
220
223
|
```
|
221
224
|
|
222
|
-
### heatmap — commit heatmap
|
225
|
+
### 🔥 heatmap — commit heatmap
|
223
226
|
```bash
|
224
227
|
pretty-git heatmap . --format json
|
225
228
|
```
|
@@ -231,7 +234,7 @@ dow,hour,commits
|
|
231
234
|
1,11,7
|
232
235
|
```
|
233
236
|
|
234
|
-
### languages — languages
|
237
|
+
### 🈺 languages — languages
|
235
238
|
```bash
|
236
239
|
pretty-git languages . --format md --limit 10
|
237
240
|
```
|
@@ -264,11 +267,92 @@ Export:
|
|
264
267
|
- CSV/MD: columns are dynamic — `language,<metric>,percent`. Markdown also includes a `color` column.
|
265
268
|
- JSON/YAML/XML: full report structure including per-language `color` and metadata (`report`, `generated_at`, `repo_path`).
|
266
269
|
|
267
|
-
|
270
|
+
### ⚠️ hotspots — hotspots (risky files)
|
271
|
+
```bash
|
272
|
+
pretty-git hotspots . --format csv --limit 20
|
273
|
+
```
|
274
|
+
Highlights riskier files combining change frequency and magnitude.
|
275
|
+
|
276
|
+
CSV columns: `path,score,commits,additions,deletions,changes`.
|
277
|
+
|
278
|
+
Example:
|
279
|
+
```csv
|
280
|
+
path,score,commits,additions,deletions,changes
|
281
|
+
lib/a.rb,9.5,12,300,220,520
|
282
|
+
app/b.rb,7.1,8,140,60,200
|
283
|
+
```
|
284
|
+
|
285
|
+
JSON example:
|
286
|
+
```json
|
287
|
+
{
|
288
|
+
"report": "hotspots",
|
289
|
+
"generated_at": "2025-01-31T00:00:00Z",
|
290
|
+
"repo_path": ".",
|
291
|
+
"items": [
|
292
|
+
{"path": "lib/a.rb", "score": 9.5, "commits": 12, "additions": 300, "deletions": 220, "changes": 520}
|
293
|
+
]
|
294
|
+
}
|
295
|
+
```
|
296
|
+
|
297
|
+
### 🔄 churn — code churn by file
|
298
|
+
```bash
|
299
|
+
pretty-git churn . --format md --limit 20
|
300
|
+
```
|
301
|
+
Measures code churn (amount of code changing frequently).
|
302
|
+
|
303
|
+
CSV columns: `path,churn,commits,additions,deletions`.
|
304
|
+
|
305
|
+
Markdown example:
|
306
|
+
```markdown
|
307
|
+
| path | churn | commits | additions | deletions |
|
308
|
+
|---|---:|---:|---:|---:|
|
309
|
+
| lib/a.rb | 520 | 12 | 300 | 220 |
|
310
|
+
```
|
311
|
+
|
312
|
+
YAML example:
|
313
|
+
```yaml
|
314
|
+
report: churn
|
315
|
+
generated_at: '2025-01-31T00:00:00Z'
|
316
|
+
repo_path: .
|
317
|
+
items:
|
318
|
+
- path: lib/a.rb
|
319
|
+
churn: 520
|
320
|
+
commits: 12
|
321
|
+
additions: 300
|
322
|
+
deletions: 220
|
323
|
+
```
|
324
|
+
|
325
|
+
### 🏷️ ownership — code ownership
|
326
|
+
```bash
|
327
|
+
pretty-git ownership . --format csv --limit 50
|
328
|
+
```
|
329
|
+
Shows file ownership concentration per primary owner.
|
330
|
+
|
331
|
+
CSV columns: `path,owner,owner_share,authors`.
|
332
|
+
|
333
|
+
Notes:
|
334
|
+
- `owner`: author identifier (name/email) with the largest share of edits.
|
335
|
+
- `owner_share`: percent of edits by the owner (0..100).
|
336
|
+
- `authors`: total unique authors who edited the file.
|
337
|
+
|
338
|
+
XML example:
|
339
|
+
```xml
|
340
|
+
<ownership>
|
341
|
+
<report>ownership</report>
|
342
|
+
<generated_at>2025-01-31T00:00:00Z</generated_at>
|
343
|
+
<repo_path>.</repo_path>
|
344
|
+
<items>
|
345
|
+
<item path="lib/a.rb" owner="Alice <a@example.com>" owner_share="82.5" authors="2"/>
|
346
|
+
</items>
|
347
|
+
|
348
|
+
</ownership>
|
349
|
+
```
|
350
|
+
|
351
|
+
## 📤 Exports
|
268
352
|
|
269
353
|
Below are exact serialization rules for each format to ensure compatibility with common tools (Excel, BI, CI, etc.).
|
270
354
|
|
271
|
-
### Console
|
355
|
+
### 🖥️ Console
|
272
356
|

|
273
357
|
_Example terminal output (theme: basic)._
|
274
358
|
* **Colors**: headers and table heads highlighted; totals: `commits` — yellow, `+additions` — green, `-deletions` — red. `--no-color` fully disables coloring.
|
@@ -279,7 +363,7 @@ _Example terminal output (theme: basic)._
|
|
279
363
|
* **Purpose**: human-readable terminal output.
|
280
364
|
* **Layout**: boxed tables, auto-truncation of long values.
|
281
365
|
|
282
|
-
### JSON
|
366
|
+
### 🧾 JSON
|
283
367
|
* **Keys**: `snake_case`.
|
284
368
|
* **Numbers**: integers/floats without localization (dot decimal separator).
|
285
369
|
* **Boolean**: `true/false`; **null**: `null`.
|
@@ -311,7 +395,7 @@ _Example terminal output (theme: basic)._
|
|
311
395
|
Bob,b@example.com,1,2,0,2.0
|
312
396
|
```
|
313
397
|
|
314
|
-
### Markdown
|
398
|
+
### 📝 Markdown
|
315
399
|
* **Tables**: GitHub Flavored Markdown.
|
316
400
|
* **Alignment**: numeric columns are right-aligned (`---:`).
|
317
401
|
* **Encoding/line endings**: UTF‑8, LF.
|
@@ -324,7 +408,7 @@ _Example terminal output (theme: basic)._
|
|
324
408
|
| app/models/user.rb | 42 | 2100 | 1400 |
|
325
409
|
```
|
326
410
|
|
327
|
-
### YAML
|
411
|
+
### 📄 YAML
|
328
412
|
* **Structure**: full result hierarchy.
|
329
413
|
* **Keys**: serialized as strings.
|
330
414
|
* **Numbers/boolean/null**: standard YAML (`123`, `true/false`, `null`).
|
@@ -344,7 +428,7 @@ _Example terminal output (theme: basic)._
|
|
344
428
|
commits: 1
|
345
429
|
```
|
346
430
|
|
347
|
-
### XML
|
431
|
+
### 🗂️ XML
|
348
432
|
* **Structure**: elements correspond to keys; arrays — repeated `<item>` or specialized tags.
|
349
433
|
* **Attributes**: for compact rows (e.g., files report) main fields may be attributes.
|
350
434
|
* **Text nodes**: used for scalar values when needed.
|
@@ -362,7 +446,7 @@ _Example terminal output (theme: basic)._
|
|
362
446
|
</authors>
|
363
447
|
```
|
364
448
|
|
365
|
-
## Ignored directories and files
|
449
|
+
## 🚫 Ignored directories and files
|
366
450
|
|
367
451
|
To keep language statistics meaningful, certain directories and file types are skipped by default.
|
368
452
|
|
@@ -387,16 +471,16 @@ vendor, node_modules, .git, .bundle, dist, build, out, target, coverage,
|
|
387
471
|
|
388
472
|
These lists mirror the implementation in `lib/pretty_git/analytics/languages.rb` and may evolve.
|
389
473
|
|
390
|
-
## Determinism and Sorting
|
474
|
+
## 🔁 Determinism and Sorting
|
391
475
|
Output is deterministic given the same input. Sorting for files/authors: by changes (desc), then by commits (desc), then by path/name (asc). Limits are applied after sorting; `all` or `0` means no limit.
|
392
476
|
|
393
|
-
## Windows Notes
|
477
|
+
## 🪟 Windows Notes
|
394
478
|
Primary targets — macOS/Linux. Windows is supported best‑effort:
|
395
479
|
* Running via Git Bash/WSL is OK
|
396
480
|
* Colors can be disabled by `--no-color`
|
397
481
|
* Carefully quote arguments when working with paths
|
398
482
|
|
399
|
-
## Diagnostics and Errors
|
483
|
+
## 🩺 Diagnostics and Errors
|
400
484
|
Typical issues and solutions:
|
401
485
|
|
402
486
|
* **Unknown report/format** — check the first argument and `--format`.
|
@@ -405,12 +489,12 @@ Typical issues and solutions:
|
|
405
489
|
* **Empty result** — check your filters (`--since/--until`, `--branch`, `--path`); your selection might be too narrow.
|
406
490
|
* **CSV encoding issues** — files are saved as UTF‑8; when opening in Excel, pick UTF‑8.
|
407
491
|
|
408
|
-
## FAQ
|
492
|
+
## ❓ FAQ
|
409
493
|
* **Why Ruby 3.4+?** The project uses dependencies aligned with Ruby 3.4+ and targets the current ecosystem.
|
410
494
|
* **New formats?** Yes, add a renderer under `lib/pretty_git/render/` and wire it in the app.
|
411
495
|
* **Where does data come from?** From system `git` via CLI calls.
|
412
496
|
|
413
|
-
## Development
|
497
|
+
## 🛠️ Development
|
414
498
|
```bash
|
415
499
|
# Install deps
|
416
500
|
bin/setup
|
@@ -422,5 +506,5 @@ bundle exec rubocop
|
|
422
506
|
|
423
507
|
Style — RuboCop clean. Tests cover aggregators, renderers, CLI, and integration scenarios (determinism, format correctness).
|
424
508
|
|
425
|
-
## License
|
509
|
+
## 📄 License
|
426
510
|
MIT © Contributors
|
data/README.ru.md
CHANGED
@@ -14,7 +14,7 @@
|
|
14
14
|
<br>
|
15
15
|
</p>
|
16
16
|
|
17
|
-
Генератор отчётов по локальному Git-репозиторию: сводка, активность, авторы, файлы,
|
17
|
+
Генератор отчётов по локальному Git-репозиторию: сводка, активность, авторы, файлы, теплокарта, языки, hotspots, churn, ownership. Вывод в консоль и форматы: JSON, CSV, Markdown, YAML, XML.
|
18
18
|
|
19
19
|
— Лицензия: MIT.
|
20
20
|
|
@@ -35,10 +35,13 @@
|
|
35
35
|
- [files — по файлам](#files--по-файлам)
|
36
36
|
- [heatmap — тепловая карта](#heatmap--тепловая-карта)
|
37
37
|
- [languages — языки](#languages--языки)
|
38
|
+
- [hotspots — рискованные файлы](#hotspots--рискованные-файлы)
|
39
|
+
- [churn — churn по файлам](#churn--churn-по-файлам)
|
40
|
+
- [ownership — владение кодом](#ownership--владение-кодом)
|
38
41
|
- [Экспорт в форматы](#экспорт-в-форматы)
|
39
42
|
- [Console](#console)
|
40
43
|
- [JSON](#json)
|
41
|
-
- [CSV
|
44
|
+
- [CSV](#csv)
|
42
45
|
- [Markdown](#markdown)
|
43
46
|
- [YAML](#yaml)
|
44
47
|
- [XML](#xml)
|
@@ -49,17 +52,17 @@
|
|
49
52
|
- [Разработка](#разработка)
|
50
53
|
- [Лицензия](#лицензия)
|
51
54
|
|
52
|
-
## Возможности
|
53
|
-
* __Отчёты__: `summary`, `activity`, `authors`, `files`, `heatmap`, `languages`.
|
55
|
+
## ✨ Возможности
|
56
|
+
* __Отчёты__: `summary`, `activity`, `authors`, `files`, `heatmap`, `languages`, `hotspots`, `churn`, `ownership`.
|
54
57
|
* __Фильтры__: ветки, авторы, пути, период времени.
|
55
58
|
* __Экспорт__: `console`, `json`, `csv`, `md`, `yaml`, `xml`.
|
56
59
|
* __Вывод__: в stdout или файл через `--out`.
|
57
60
|
|
58
|
-
## Требования
|
61
|
+
## ⚙️ Требования
|
59
62
|
* __Ruby__: >= 3.4 (рекомендуется 3.4.x)
|
60
63
|
* __Git__: установлен и доступен в `PATH`
|
61
64
|
|
62
|
-
## Установка
|
65
|
+
## 📦 Установка
|
63
66
|
|
64
67
|
### 🍺 Homebrew (рекомендуется)
|
65
68
|
```bash
|
@@ -101,7 +104,7 @@ bundle install
|
|
101
104
|
bundle exec pretty-git --help
|
102
105
|
```
|
103
106
|
|
104
|
-
## Быстрый старт
|
107
|
+
## 🚀 Быстрый старт
|
105
108
|
```bash
|
106
109
|
# Сводка репозитория в консоль
|
107
110
|
bundle exec bin/pretty-git summary .
|
@@ -114,21 +117,21 @@ bundle exec bin/pretty-git activity . --time-bucket week --since 2025-01-01 \
|
|
114
117
|
--paths app,lib --format csv --out activity.csv
|
115
118
|
```
|
116
119
|
|
117
|
-
## CLI и параметры
|
120
|
+
## 🧰 CLI и параметры
|
118
121
|
Общий вид:
|
119
122
|
|
120
123
|
```bash
|
121
124
|
pretty-git <report> <repo_path> [options]
|
122
125
|
```
|
123
126
|
|
124
|
-
Доступные отчёты: `summary`, `activity`, `authors`, `files`, `heatmap`, `languages`.
|
127
|
+
Доступные отчёты: `summary`, `activity`, `authors`, `files`, `heatmap`, `languages`, `hotspots`, `churn`, `ownership`.
|
125
128
|
|
126
129
|
Ключевые опции:
|
127
130
|
* __--format, -f__ `console|json|csv|md|yaml|xml` (по умолчанию `console`)
|
128
131
|
* __--out, -o__ Путь для записи в файл
|
129
132
|
* __--limit, -l__ Число элементов в топах/выводе; `all` или `0` — без ограничения
|
130
133
|
* __--time-bucket__ `day|week|month` (для `activity`)
|
131
|
-
* __--since/--until__ Дата/время в ISO8601 или `YYYY-MM-DD`
|
134
|
+
* __--since/--until__ Дата/время в ISO8601 или `YYYY-MM-DD`
|
132
135
|
* __--branch__ Мульти-опция, можно указывать несколько веток
|
133
136
|
* __--author/--exclude-author__ Фильтрация по авторам
|
134
137
|
* __--path/--exclude-path__ Фильтрация по путям (через запятую или повтор опции)
|
@@ -165,15 +168,15 @@ pretty-git authors . --format csv --out authors.csv
|
|
165
168
|
* `1` — ошибка пользователя (неизвестный отчёт/формат, неверные аргументы)
|
166
169
|
* `2` — системная ошибка (ошибка Git и пр.)
|
167
170
|
|
168
|
-
## Отчёты и примеры
|
171
|
+
## 📊 Отчёты и примеры
|
169
172
|
|
170
|
-
### summary — сводка
|
173
|
+
### 🧭 summary — сводка
|
171
174
|
```bash
|
172
175
|
pretty-git summary . --format json
|
173
176
|
```
|
174
177
|
Содержит totals (commits, authors, additions, deletions) и топы по авторам/файлам.
|
175
178
|
|
176
|
-
### activity — активность (day/week/month)
|
179
|
+
### 📆 activity — активность (day/week/month)
|
177
180
|
```bash
|
178
181
|
pretty-git activity . --time-bucket week --format csv
|
179
182
|
```
|
@@ -186,7 +189,7 @@ CSV-колонки: `bucket,timestamp,commits,additions,deletions`.
|
|
186
189
|
]
|
187
190
|
```
|
188
191
|
|
189
|
-
### authors — по авторам
|
192
|
+
### 👤 authors — по авторам
|
190
193
|
```bash
|
191
194
|
pretty-git authors . --format md --limit 10
|
192
195
|
```
|
@@ -199,7 +202,7 @@ CSV-колонки: `author,author_email,commits,additions,deletions,avg_commit_
|
|
199
202
|
| Bob | b@example.com | 1 | 2 | 0 | 2.0 |
|
200
203
|
```
|
201
204
|
|
202
|
-
### files — по файлам
|
205
|
+
### 📁 files — по файлам
|
203
206
|
```bash
|
204
207
|
pretty-git files . --paths app,lib --format csv
|
205
208
|
```
|
@@ -219,7 +222,7 @@ CSV-колонки: `path,commits,additions,deletions,changes`.
|
|
219
222
|
</files>
|
220
223
|
```
|
221
224
|
|
222
|
-
### heatmap — тепловая карта
|
225
|
+
### 🔥 heatmap — тепловая карта
|
223
226
|
```bash
|
224
227
|
pretty-git heatmap . --format json
|
225
228
|
```
|
@@ -231,7 +234,7 @@ dow,hour,commits
|
|
231
234
|
1,11,7
|
232
235
|
```
|
233
236
|
|
234
|
-
### languages — языки
|
237
|
+
### 🈺 languages — языки
|
235
238
|
```bash
|
236
239
|
pretty-git languages . --format md --limit 10
|
237
240
|
```
|
@@ -262,7 +265,88 @@ Markdown 1200 1.7
|
|
262
265
|
- CSV/MD: колонки динамические — `language,<metric>,percent`. В Markdown дополнительно присутствует колонка `color`.
|
263
266
|
- JSON/YAML/XML: полная структура отчёта, включая `color` на язык и метаданные (`report`, `generated_at`, `repo_path`).
|
264
267
|
|
265
|
-
|
268
|
+
### ⚠️ hotspots — рискованные файлы
|
269
|
+
```bash
|
270
|
+
pretty-git hotspots . --format csv --limit 20
|
271
|
+
```
|
272
|
+
Подсвечивает потенциально более рискованные файлы по сочетанию частоты и масштаба изменений.
|
273
|
+
|
274
|
+
CSV‑колонки: `path,score,commits,additions,deletions,changes`.
|
275
|
+
|
276
|
+
Пример:
|
277
|
+
```csv
|
278
|
+
path,score,commits,additions,deletions,changes
|
279
|
+
lib/a.rb,9.5,12,300,220,520
|
280
|
+
app/b.rb,7.1,8,140,60,200
|
281
|
+
```
|
282
|
+
|
283
|
+
Пример JSON:
|
284
|
+
```json
|
285
|
+
{
|
286
|
+
"report": "hotspots",
|
287
|
+
"generated_at": "2025-01-31T00:00:00Z",
|
288
|
+
"repo_path": ".",
|
289
|
+
"items": [
|
290
|
+
{"path": "lib/a.rb", "score": 9.5, "commits": 12, "additions": 300, "deletions": 220, "changes": 520}
|
291
|
+
]
|
292
|
+
}
|
293
|
+
```
|
294
|
+
|
295
|
+
### 🔄 churn — churn по файлам
|
296
|
+
```bash
|
297
|
+
pretty-git churn . --format md --limit 20
|
298
|
+
```
|
299
|
+
Оценивает churn (объём часто меняющегося кода) по файлам.
|
300
|
+
|
301
|
+
CSV‑колонки: `path,churn,commits,additions,deletions`.
|
302
|
+
|
303
|
+
Пример Markdown:
|
304
|
+
```markdown
|
305
|
+
| path | churn | commits | additions | deletions |
|
306
|
+
|---|---:|---:|---:|---:|
|
307
|
+
| lib/a.rb | 520 | 12 | 300 | 220 |
|
308
|
+
```
|
309
|
+
|
310
|
+
Пример YAML:
|
311
|
+
```yaml
|
312
|
+
report: churn
|
313
|
+
generated_at: '2025-01-31T00:00:00Z'
|
314
|
+
repo_path: .
|
315
|
+
items:
|
316
|
+
- path: lib/a.rb
|
317
|
+
churn: 520
|
318
|
+
commits: 12
|
319
|
+
additions: 300
|
320
|
+
deletions: 220
|
321
|
+
```
|
322
|
+
|
323
|
+
### 🏷️ ownership — владение кодом
|
324
|
+
```bash
|
325
|
+
pretty-git ownership . --format csv --limit 50
|
326
|
+
```
|
327
|
+
Показывает концентрацию владения файлами по основному владельцу.
|
328
|
+
|
329
|
+
CSV‑колонки: `path,owner,owner_share,authors`.
|
330
|
+
|
331
|
+
Примечания:
|
332
|
+
- `owner`: идентификатор автора (имя/email) с наибольшей долей правок.
|
333
|
+
- `owner_share`: доля правок владельца в процентах (0..100).
|
334
|
+
- `authors`: всего уникальных авторов, редактировавших файл.
|
335
|
+
|
336
|
+
Пример XML:
|
337
|
+
```xml
|
338
|
+
<ownership>
|
339
|
+
<report>ownership</report>
|
340
|
+
<generated_at>2025-01-31T00:00:00Z</generated_at>
|
341
|
+
<repo_path>.</repo_path>
|
342
|
+
<items>
|
343
|
+
<item path="lib/a.rb" owner="Alice <a@example.com>" owner_share="82.5" authors="2"/>
|
344
|
+
</items>
|
345
|
+
|
346
|
+
</ownership>
|
347
|
+
```
|
348
|
+
|
349
|
+
## 🚫 Игнорируемые директории и файлы
|
266
350
|
|
267
351
|
Чтобы статистика по языкам оставалась релевантной, некоторые директории и типы файлов пропускаются по умолчанию.
|
268
352
|
|
@@ -287,11 +371,11 @@ vendor, node_modules, .git, .bundle, dist, build, out, target, coverage,
|
|
287
371
|
|
288
372
|
Списки соответствуют реализации в `lib/pretty_git/analytics/languages.rb` и могут изменяться.
|
289
373
|
|
290
|
-
## Экспорт в форматы
|
374
|
+
## 📤 Экспорт в форматы
|
291
375
|
|
292
376
|
Ниже — точные правила сериализации для каждого формата, чтобы обеспечивалась совместимость с популярными инструментами (Excel, BI, CI и т.п.).
|
293
377
|
|
294
|
-
### Console
|
378
|
+
### 🖥️ Console
|
295
379
|

|
296
380
|
_Пример вывода в терминале (тема: basic)._
|
297
381
|
* __Цвета__: заголовки и шапки таблиц подсвечены; суммы: `commits` — жёлтым, `+additions` — зелёным, `-deletions` — красным. `--no-color` полностью отключает раскраску.
|
@@ -305,7 +389,7 @@ _Пример вывода в терминале (тема: basic)._
|
|
305
389
|
* __Пустые наборы__: печатается `No data`.
|
306
390
|
* __Кодировка/переводы строк__: UTF‑8, LF (`\n`).
|
307
391
|
|
308
|
-
### JSON
|
392
|
+
### 🧾 JSON
|
309
393
|
* __Ключи__: `snake_case`.
|
310
394
|
* __Числа__: целые/вещественные без локализации (точка как разделитель).
|
311
395
|
* __Булевы__: `true/false`; __null__: `null`.
|
@@ -337,7 +421,7 @@ _Пример вывода в терминале (тема: basic)._
|
|
337
421
|
Bob,b@example.com,1,2,0,2.0
|
338
422
|
```
|
339
423
|
|
340
|
-
### Markdown
|
424
|
+
### 📝 Markdown
|
341
425
|
* __Таблицы__: стандартный синтаксис GitHub Flavored Markdown.
|
342
426
|
* __Выравнивание__: числовые колонки выравниваются по правому краю (`---:`).
|
343
427
|
* __Кодировка/переводы строк__: UTF‑8, LF.
|
@@ -350,7 +434,7 @@ _Пример вывода в терминале (тема: basic)._
|
|
350
434
|
| app/models/user.rb | 42 | 2100 | 1400 |
|
351
435
|
```
|
352
436
|
|
353
|
-
### YAML
|
437
|
+
### 📄 YAML
|
354
438
|
* __Структура__: полная иерархия результата.
|
355
439
|
* __Ключи__: сериализуются строками.
|
356
440
|
* __Числа/булевы/null__: стандарт YAML (`123`, `true/false`, `null`).
|
@@ -370,7 +454,7 @@ _Пример вывода в терминале (тема: basic)._
|
|
370
454
|
commits: 1
|
371
455
|
```
|
372
456
|
|
373
|
-
### XML
|
457
|
+
### 🗂️ XML
|
374
458
|
* __Структура__: элементы соответствуют ключам; массивы — повторяющиеся `<item>` или специализированные теги.
|
375
459
|
* __Атрибуты__: для компактных записей (например, строк файлового отчёта) основные поля могут быть атрибутами элемента.
|
376
460
|
* __Текстовые узлы__: применяются для скалярных значений при необходимости.
|
@@ -388,16 +472,16 @@ _Пример вывода в терминале (тема: basic)._
|
|
388
472
|
</authors>
|
389
473
|
```
|
390
474
|
|
391
|
-
## Детерминизм и сортировка
|
475
|
+
## 🔁 Детерминизм и сортировка
|
392
476
|
Вывод детерминирован при одинаковых входных данных. Сортировка для файлов/авторов: по количеству изменений (desc), затем по числу коммитов (desc), затем по пути/имени (asc). Лимиты применяются поверх отсортированного списка; значение `all` или `0` означает отсутствие ограничения.
|
393
477
|
|
394
|
-
## Советы по Windows
|
478
|
+
## 🪟 Советы по Windows
|
395
479
|
Целевая платформа — macOS/Linux. Windows поддерживается в режиме best‑effort:
|
396
480
|
* Запуск через Git Bash/WSL допустим
|
397
481
|
* Цвета можно отключить `--no-color`
|
398
482
|
* Аккуратное квотирование аргументов при работе с путями
|
399
483
|
|
400
|
-
## Диагностика и ошибки
|
484
|
+
## 🩺 Диагностика и ошибки
|
401
485
|
Типичные ошибки и решения:
|
402
486
|
|
403
487
|
* __Неизвестный отчёт/формат__ — проверьте значение первого аргумента и `--format`.
|
@@ -406,12 +490,12 @@ _Пример вывода в терминале (тема: basic)._
|
|
406
490
|
* __Пустой результат__ — проверьте фильтры (`--since/--until`, `--branch`, `--path`), возможно, выборка слишком узкая.
|
407
491
|
* __Проблемы с кодировкой CSV__ — файлы сохраняются в UTF‑8, при открытии в Excel выбирайте UTF‑8.
|
408
492
|
|
409
|
-
## FAQ
|
493
|
+
## ❓ FAQ
|
410
494
|
* __Почему Ruby 3.4+?__ Проект использует зависимости, согласованные с версией 3.4+, и ориентируется на актуальную экосистему.
|
411
495
|
* __Можно ли добавить новые форматы?__ Да, добавьте рендерер в `lib/pretty_git/render/` и зарегистрируйте его в приложении.
|
412
496
|
* __Откуда берутся данные?__ Из системного `git` через вызовы CLI.
|
413
497
|
|
414
|
-
## Разработка
|
498
|
+
## 🛠️ Разработка
|
415
499
|
```bash
|
416
500
|
# Установка зависимостей
|
417
501
|
bin/setup
|
@@ -423,5 +507,5 @@ bundle exec rubocop
|
|
423
507
|
|
424
508
|
Стиль — RuboCop без ошибок. Тесты покрывают агрегаторы, рендереры, CLI и интеграционные сценарии (детерминизм, корректность форматов).
|
425
509
|
|
426
|
-
## Лицензия
|
510
|
+
## 📄 Лицензия
|
427
511
|
MIT © Contributors
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
module PrettyGit
|
6
|
+
module Analytics
|
7
|
+
# Churn: per-file volatility over the selected period.
|
8
|
+
# churn = additions + deletions (total changed lines)
|
9
|
+
class Churn
|
10
|
+
class << self
|
11
|
+
def call(enum, filters)
|
12
|
+
per_file = aggregate(enum)
|
13
|
+
items = build_items(per_file)
|
14
|
+
items = sort_and_limit(items, filters.limit)
|
15
|
+
build_result(filters, items)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def aggregate(enum)
|
21
|
+
acc = Hash.new { |h, k| h[k] = { commits: 0, additions: 0, deletions: 0 } }
|
22
|
+
enum.each do |commit|
|
23
|
+
seen = {}
|
24
|
+
commit.files&.each { |f| process_file_entry(acc, seen, f) }
|
25
|
+
end
|
26
|
+
acc
|
27
|
+
end
|
28
|
+
|
29
|
+
def process_file_entry(acc, seen, file_stat)
|
30
|
+
path = file_stat.path
|
31
|
+
unless seen[path]
|
32
|
+
acc[path][:commits] += 1
|
33
|
+
seen[path] = true
|
34
|
+
end
|
35
|
+
acc[path][:additions] += file_stat.additions.to_i
|
36
|
+
acc[path][:deletions] += file_stat.deletions.to_i
|
37
|
+
end
|
38
|
+
|
39
|
+
def build_items(per_file)
|
40
|
+
per_file.map do |path, v|
|
41
|
+
churn = v[:additions] + v[:deletions]
|
42
|
+
{
|
43
|
+
path: path,
|
44
|
+
churn: churn,
|
45
|
+
commits: v[:commits]
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def sort_and_limit(items, raw_limit)
|
51
|
+
limit = normalize_limit(raw_limit)
|
52
|
+
sorted = items.sort_by { |h| [-h[:churn], -h[:commits], h[:path].to_s] }
|
53
|
+
limit ? sorted.first(limit) : sorted
|
54
|
+
end
|
55
|
+
|
56
|
+
def build_result(filters, items)
|
57
|
+
{
|
58
|
+
report: 'churn',
|
59
|
+
repo_path: File.expand_path(filters.repo_path),
|
60
|
+
period: { since: filters.since_iso8601, until: filters.until_iso8601 },
|
61
|
+
items: items,
|
62
|
+
generated_at: Time.now.utc.iso8601
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
def normalize_limit(raw)
|
67
|
+
return nil if raw.nil? || raw == 'all'
|
68
|
+
|
69
|
+
n = raw.to_i
|
70
|
+
n <= 0 ? nil : n
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
module PrettyGit
|
6
|
+
module Analytics
|
7
|
+
# Hotspots: files with the highest change activity over the selected period.
|
8
|
+
# score = commits * (additions + deletions)
|
9
|
+
class Hotspots
|
10
|
+
class << self
|
11
|
+
def call(enum, filters)
|
12
|
+
per_file = aggregate(enum)
|
13
|
+
items = build_items(per_file)
|
14
|
+
items = sort_and_limit(items, filters.limit)
|
15
|
+
build_result(filters, items)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def aggregate(enum)
|
21
|
+
acc = Hash.new { |h, k| h[k] = { commits: 0, additions: 0, deletions: 0 } }
|
22
|
+
enum.each do |commit|
|
23
|
+
seen = {}
|
24
|
+
commit.files&.each { |f| process_file_entry(acc, seen, f) }
|
25
|
+
end
|
26
|
+
acc
|
27
|
+
end
|
28
|
+
|
29
|
+
def process_file_entry(acc, seen, file_stat)
|
30
|
+
path = file_stat.path
|
31
|
+
unless seen[path]
|
32
|
+
acc[path][:commits] += 1
|
33
|
+
seen[path] = true
|
34
|
+
end
|
35
|
+
acc[path][:additions] += file_stat.additions.to_i
|
36
|
+
acc[path][:deletions] += file_stat.deletions.to_i
|
37
|
+
end
|
38
|
+
|
39
|
+
def build_items(per_file)
|
40
|
+
per_file.map do |path, v|
|
41
|
+
changes = v[:additions] + v[:deletions]
|
42
|
+
score = v[:commits] * changes
|
43
|
+
{
|
44
|
+
path: path,
|
45
|
+
score: score,
|
46
|
+
commits: v[:commits],
|
47
|
+
additions: v[:additions],
|
48
|
+
deletions: v[:deletions]
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def sort_and_limit(items, raw_limit)
|
54
|
+
limit = normalize_limit(raw_limit)
|
55
|
+
sorted = items.sort_by { |h| [-h[:score], -h[:commits], -h[:additions] - h[:deletions], h[:path].to_s] }
|
56
|
+
limit ? sorted.first(limit) : sorted
|
57
|
+
end
|
58
|
+
|
59
|
+
def build_result(filters, items)
|
60
|
+
{
|
61
|
+
report: 'hotspots',
|
62
|
+
repo_path: File.expand_path(filters.repo_path),
|
63
|
+
period: { since: filters.since_iso8601, until: filters.until_iso8601 },
|
64
|
+
items: items,
|
65
|
+
generated_at: Time.now.utc.iso8601
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
def normalize_limit(raw)
|
70
|
+
return nil if raw.nil? || raw == 'all'
|
71
|
+
|
72
|
+
n = raw.to_i
|
73
|
+
n <= 0 ? nil : n
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
module PrettyGit
|
6
|
+
module Analytics
|
7
|
+
# Ownership: per-file code ownership based on change activity (churn).
|
8
|
+
# For each file, the owner is the author with the largest share of churn (adds+dels).
|
9
|
+
class Ownership
|
10
|
+
class << self
|
11
|
+
def call(enum, filters)
|
12
|
+
per_file = aggregate(enum)
|
13
|
+
items = build_items(per_file)
|
14
|
+
items = sort_and_limit(items, filters.limit)
|
15
|
+
build_result(filters, items)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# Builds a map: path => { total_churn: N, authors: {"name <email>" => churn} }
|
21
|
+
def aggregate(enum)
|
22
|
+
acc = Hash.new { |h, k| h[k] = { total: 0, authors: Hash.new(0) } }
|
23
|
+
enum.each do |commit|
|
24
|
+
author_key = author_identity(commit)
|
25
|
+
commit.files&.each { |f| process_file_entry(acc, author_key, f) }
|
26
|
+
end
|
27
|
+
acc
|
28
|
+
end
|
29
|
+
|
30
|
+
def process_file_entry(acc, author_key, file_stat)
|
31
|
+
churn = file_stat.additions.to_i + file_stat.deletions.to_i
|
32
|
+
return if churn <= 0
|
33
|
+
|
34
|
+
path = file_stat.path
|
35
|
+
acc[path][:total] += churn
|
36
|
+
acc[path][:authors][author_key] += churn
|
37
|
+
end
|
38
|
+
|
39
|
+
def author_identity(commit)
|
40
|
+
name = commit.author_name.to_s.strip
|
41
|
+
email = commit.author_email.to_s.strip
|
42
|
+
email.empty? ? name : "#{name} <#{email}>"
|
43
|
+
end
|
44
|
+
|
45
|
+
def build_items(per_file)
|
46
|
+
per_file.map do |path, v|
|
47
|
+
owner, share, authors_count = compute_owner(v[:authors], v[:total])
|
48
|
+
{
|
49
|
+
path: path,
|
50
|
+
owner: owner,
|
51
|
+
owner_share: share.round(2),
|
52
|
+
authors: authors_count
|
53
|
+
}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def compute_owner(authors_map, total)
|
58
|
+
return [nil, 0.0, 0] if total.to_i <= 0 || authors_map.nil? || authors_map.empty?
|
59
|
+
|
60
|
+
author, owner_churn = authors_map.max_by { |a, c| [c, a] }
|
61
|
+
share = (owner_churn.to_f * 100.0) / total.to_f
|
62
|
+
[author, share, authors_map.size]
|
63
|
+
end
|
64
|
+
|
65
|
+
def sort_and_limit(items, raw_limit)
|
66
|
+
limit = normalize_limit(raw_limit)
|
67
|
+
sorted = items.sort_by { |h| [-h[:owner_share].to_f, h[:authors].to_i, h[:path].to_s] }
|
68
|
+
limit ? sorted.first(limit) : sorted
|
69
|
+
end
|
70
|
+
|
71
|
+
def build_result(filters, items)
|
72
|
+
{
|
73
|
+
report: 'ownership',
|
74
|
+
repo_path: File.expand_path(filters.repo_path),
|
75
|
+
period: { since: filters.since_iso8601, until: filters.until_iso8601 },
|
76
|
+
items: items,
|
77
|
+
generated_at: Time.now.utc.iso8601
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
def normalize_limit(raw)
|
82
|
+
return nil if raw.nil? || raw == 'all'
|
83
|
+
|
84
|
+
n = raw.to_i
|
85
|
+
n <= 0 ? nil : n
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/pretty_git/app.rb
CHANGED
@@ -7,6 +7,9 @@ require_relative 'analytics/files'
|
|
7
7
|
require_relative 'analytics/authors'
|
8
8
|
require_relative 'analytics/heatmap'
|
9
9
|
require_relative 'analytics/languages'
|
10
|
+
require_relative 'analytics/hotspots'
|
11
|
+
require_relative 'analytics/churn'
|
12
|
+
require_relative 'analytics/ownership'
|
10
13
|
require_relative 'render/json_renderer'
|
11
14
|
require_relative 'render/console_renderer'
|
12
15
|
require_relative 'render/csv_renderer'
|
@@ -62,22 +65,21 @@ module PrettyGit
|
|
62
65
|
end
|
63
66
|
|
64
67
|
def analytics_for(report, enum, filters)
|
65
|
-
|
66
|
-
|
67
|
-
Analytics::
|
68
|
-
|
69
|
-
Analytics::
|
70
|
-
|
71
|
-
Analytics::
|
72
|
-
|
73
|
-
Analytics::
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
end
|
68
|
+
dispatch = {
|
69
|
+
'summary' => Analytics::Summary,
|
70
|
+
'activity' => Analytics::Activity,
|
71
|
+
'authors' => Analytics::Authors,
|
72
|
+
'files' => Analytics::Files,
|
73
|
+
'heatmap' => Analytics::Heatmap,
|
74
|
+
'languages' => Analytics::Languages,
|
75
|
+
'hotspots' => Analytics::Hotspots,
|
76
|
+
'churn' => Analytics::Churn,
|
77
|
+
'ownership' => Analytics::Ownership
|
78
|
+
}
|
79
|
+
klass = dispatch[report]
|
80
|
+
raise ArgumentError, "Unknown report: #{report}" unless klass
|
81
|
+
|
82
|
+
klass.call(enum, filters)
|
81
83
|
end
|
82
84
|
end
|
83
85
|
end
|
data/lib/pretty_git/cli.rb
CHANGED
@@ -9,7 +9,7 @@ require_relative 'cli_helpers'
|
|
9
9
|
module PrettyGit
|
10
10
|
# Command-line interface entry point.
|
11
11
|
class CLI
|
12
|
-
SUPPORTED_REPORTS = %w[summary activity authors files heatmap languages].freeze
|
12
|
+
SUPPORTED_REPORTS = %w[summary activity authors files heatmap languages hotspots churn ownership].freeze
|
13
13
|
SUPPORTED_FORMATS = %w[console json csv md yaml xml].freeze
|
14
14
|
|
15
15
|
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
@@ -9,7 +9,7 @@ module PrettyGit
|
|
9
9
|
# and RuboCop-compliant. Provides parser configuration and execution utilities.
|
10
10
|
# rubocop:disable Metrics/ModuleLength
|
11
11
|
module CLIHelpers
|
12
|
-
REPORTS = %w[summary activity authors files heatmap languages].freeze
|
12
|
+
REPORTS = %w[summary activity authors files heatmap languages hotspots churn ownership].freeze
|
13
13
|
FORMATS = %w[console json csv md yaml xml].freeze
|
14
14
|
METRICS = %w[bytes files loc].freeze
|
15
15
|
|
@@ -153,6 +153,7 @@ module PrettyGit
|
|
153
153
|
end
|
154
154
|
|
155
155
|
# Renders human-friendly console output with optional colors.
|
156
|
+
# rubocop:disable Metrics/ClassLength
|
156
157
|
class ConsoleRenderer
|
157
158
|
def initialize(io: $stdout, color: true, theme: 'basic')
|
158
159
|
@io = io
|
@@ -162,22 +163,21 @@ module PrettyGit
|
|
162
163
|
end
|
163
164
|
|
164
165
|
def call(report, result, _filters)
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
end
|
166
|
+
handlers = {
|
167
|
+
'summary' => method(:render_summary),
|
168
|
+
'activity' => method(:render_activity),
|
169
|
+
'authors' => method(:render_authors),
|
170
|
+
'files' => method(:render_files),
|
171
|
+
'heatmap' => method(:render_heatmap),
|
172
|
+
'languages' => ->(data) { LanguagesSection.render(@io, @table, data, color: @color) },
|
173
|
+
'hotspots' => method(:render_hotspots),
|
174
|
+
'churn' => method(:render_churn),
|
175
|
+
'ownership' => method(:render_ownership)
|
176
|
+
}
|
177
|
+
handler = handlers[report]
|
178
|
+
return @io.puts(result.inspect) unless handler
|
179
|
+
|
180
|
+
handler.call(result)
|
181
181
|
end
|
182
182
|
|
183
183
|
private
|
@@ -266,6 +266,42 @@ module PrettyGit
|
|
266
266
|
|
267
267
|
# Languages rendering moved to PrettyGit::Render::LanguagesSection
|
268
268
|
|
269
|
+
def render_hotspots(data)
|
270
|
+
title "Hotspots for #{data[:repo_path]}"
|
271
|
+
line "Period: #{data.dig(:period, :since)} .. #{data.dig(:period, :until)}"
|
272
|
+
|
273
|
+
@io.puts
|
274
|
+
title 'Hotspots'
|
275
|
+
@table.print(%w[path score commits additions deletions], data[:items])
|
276
|
+
|
277
|
+
@io.puts
|
278
|
+
line "Generated at: #{data[:generated_at]}"
|
279
|
+
end
|
280
|
+
|
281
|
+
def render_churn(data)
|
282
|
+
title "Churn for #{data[:repo_path]}"
|
283
|
+
line "Period: #{data.dig(:period, :since)} .. #{data.dig(:period, :until)}"
|
284
|
+
|
285
|
+
@io.puts
|
286
|
+
title 'Churn'
|
287
|
+
@table.print(%w[path churn commits], data[:items])
|
288
|
+
|
289
|
+
@io.puts
|
290
|
+
line "Generated at: #{data[:generated_at]}"
|
291
|
+
end
|
292
|
+
|
293
|
+
def render_ownership(data)
|
294
|
+
title "Ownership for #{data[:repo_path]}"
|
295
|
+
line "Period: #{data.dig(:period, :since)} .. #{data.dig(:period, :until)}"
|
296
|
+
|
297
|
+
@io.puts
|
298
|
+
title 'Ownership'
|
299
|
+
@table.print(%w[path owner owner_share authors], data[:items])
|
300
|
+
|
301
|
+
@io.puts
|
302
|
+
line "Generated at: #{data[:generated_at]}"
|
303
|
+
end
|
304
|
+
|
269
305
|
def title(text)
|
270
306
|
@io.puts Colors.title(text, @color, @theme)
|
271
307
|
end
|
@@ -276,5 +312,6 @@ module PrettyGit
|
|
276
312
|
|
277
313
|
# table is handled by @table
|
278
314
|
end
|
315
|
+
# rubocop:enable Metrics/ClassLength
|
279
316
|
end
|
280
317
|
end
|
@@ -6,31 +6,34 @@ module PrettyGit
|
|
6
6
|
module Render
|
7
7
|
# Renders CSV according to specs/output_formats.md and DR-001
|
8
8
|
class CsvRenderer
|
9
|
+
HEADERS = {
|
10
|
+
'activity' => %w[bucket timestamp commits additions deletions],
|
11
|
+
'authors' => %w[author author_email commits additions deletions avg_commit_size],
|
12
|
+
'files' => %w[path commits additions deletions changes],
|
13
|
+
'heatmap' => %w[dow hour commits],
|
14
|
+
'hotspots' => %w[path score commits additions deletions changes],
|
15
|
+
'churn' => %w[path churn commits additions deletions],
|
16
|
+
'ownership' => %w[path owner owner_share authors]
|
17
|
+
}.freeze
|
9
18
|
def initialize(io: $stdout)
|
10
19
|
@io = io
|
11
20
|
end
|
12
21
|
|
13
22
|
def call(report, result, _filters)
|
14
|
-
|
15
|
-
|
16
|
-
write_csv(%w[bucket timestamp commits additions deletions], result[:items])
|
17
|
-
when 'authors'
|
18
|
-
write_csv(%w[author author_email commits additions deletions avg_commit_size], result[:items])
|
19
|
-
when 'files'
|
20
|
-
write_csv(%w[path commits additions deletions changes], result[:items])
|
21
|
-
when 'heatmap'
|
22
|
-
write_csv(%w[dow hour commits], result[:items])
|
23
|
-
when 'languages'
|
24
|
-
metric = (result[:metric] || 'bytes').to_s
|
25
|
-
headers = ['language', metric, 'percent', 'color']
|
26
|
-
write_csv(headers, result[:items])
|
27
|
-
else
|
28
|
-
raise ArgumentError, "CSV output for report '#{report}' is not supported yet"
|
29
|
-
end
|
23
|
+
headers = headers_for(report, result)
|
24
|
+
write_csv(headers, result[:items])
|
30
25
|
end
|
31
26
|
|
32
27
|
private
|
33
28
|
|
29
|
+
def headers_for(report, result)
|
30
|
+
return ['language', (result[:metric] || 'bytes').to_s, 'percent', 'color'] if report == 'languages'
|
31
|
+
|
32
|
+
HEADERS.fetch(report) do
|
33
|
+
raise ArgumentError, "CSV output for report '#{report}' is not supported yet"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
34
37
|
def write_csv(headers, rows)
|
35
38
|
csv = CSV.generate(force_quotes: false) do |out|
|
36
39
|
out << headers
|
@@ -4,35 +4,50 @@ module PrettyGit
|
|
4
4
|
module Render
|
5
5
|
# Renders Markdown tables and sections per specs/output_formats.md
|
6
6
|
class MarkdownRenderer
|
7
|
+
TITLES = {
|
8
|
+
'activity' => 'Activity',
|
9
|
+
'authors' => 'Authors',
|
10
|
+
'files' => 'Top Files',
|
11
|
+
'heatmap' => 'Heatmap',
|
12
|
+
'languages' => 'Languages',
|
13
|
+
'hotspots' => 'Hotspots',
|
14
|
+
'churn' => 'Churn',
|
15
|
+
'ownership' => 'Ownership'
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
HEADERS = {
|
19
|
+
'activity' => %w[bucket timestamp commits additions deletions],
|
20
|
+
'authors' => %w[author author_email commits additions deletions avg_commit_size],
|
21
|
+
'files' => %w[path commits additions deletions changes],
|
22
|
+
'heatmap' => %w[dow hour commits],
|
23
|
+
'hotspots' => %w[path score commits additions deletions changes],
|
24
|
+
'churn' => %w[path churn commits additions deletions],
|
25
|
+
'ownership' => %w[path owner owner_share authors]
|
26
|
+
}.freeze
|
7
27
|
def initialize(io: $stdout)
|
8
28
|
@io = io
|
9
29
|
end
|
10
30
|
|
11
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
12
31
|
def call(report, result, _filters)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
when 'authors'
|
19
|
-
render_table('Authors', %w[author author_email commits additions deletions avg_commit_size], result[:items])
|
20
|
-
when 'files'
|
21
|
-
render_table('Top Files', %w[path commits additions deletions changes], result[:items])
|
22
|
-
when 'heatmap'
|
23
|
-
render_table('Heatmap', %w[dow hour commits], result[:items])
|
24
|
-
when 'languages'
|
25
|
-
metric = (result[:metric] || 'bytes').to_s
|
26
|
-
headers = ['language', metric, 'percent', 'color']
|
27
|
-
render_table('Languages', headers, result[:items])
|
28
|
-
else
|
29
|
-
@io.puts result.inspect
|
30
|
-
end
|
32
|
+
return render_summary(result) if report == 'summary'
|
33
|
+
|
34
|
+
headers = headers_for(report, result)
|
35
|
+
title = title_for(report)
|
36
|
+
render_table(title, headers, result[:items])
|
31
37
|
end
|
32
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
33
38
|
|
34
39
|
private
|
35
40
|
|
41
|
+
def headers_for(report, result)
|
42
|
+
return ['language', (result[:metric] || 'bytes').to_s, 'percent', 'color'] if report == 'languages'
|
43
|
+
|
44
|
+
HEADERS.fetch(report, [])
|
45
|
+
end
|
46
|
+
|
47
|
+
def title_for(report)
|
48
|
+
TITLES.fetch(report, report.to_s.capitalize)
|
49
|
+
end
|
50
|
+
|
36
51
|
def render_summary(data)
|
37
52
|
header_summary(data)
|
38
53
|
print_totals(data[:totals])
|
data/lib/pretty_git/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pretty-git
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pretty Git Authors
|
@@ -66,9 +66,12 @@ files:
|
|
66
66
|
- lib/pretty_git.rb
|
67
67
|
- lib/pretty_git/analytics/activity.rb
|
68
68
|
- lib/pretty_git/analytics/authors.rb
|
69
|
+
- lib/pretty_git/analytics/churn.rb
|
69
70
|
- lib/pretty_git/analytics/files.rb
|
70
71
|
- lib/pretty_git/analytics/heatmap.rb
|
72
|
+
- lib/pretty_git/analytics/hotspots.rb
|
71
73
|
- lib/pretty_git/analytics/languages.rb
|
74
|
+
- lib/pretty_git/analytics/ownership.rb
|
72
75
|
- lib/pretty_git/analytics/summary.rb
|
73
76
|
- lib/pretty_git/app.rb
|
74
77
|
- lib/pretty_git/cli.rb
|