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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2200709c51eb32592b4fe79a3acea35178fab498dbd781edcc4237ceabfa00c7
4
- data.tar.gz: c83bdd9cb01c5b345dfa52f0f1d35a03ed167a78307a6a085819453d96c88293
3
+ metadata.gz: 84571ab2bdd16f92dc31c1883a70433a140253ee0d0c66f1079453fc30f34419
4
+ data.tar.gz: 532ee3a2e436163f12821073297dc013e874746c5ac269f89c455826566c9f27
5
5
  SHA512:
6
- metadata.gz: 1d23439b48405024896bec5e173a8573ce0b8fb1b8d1872b98d0201d4cceb85bd07f246f0d2e0a406d1c9f25d40f903280536b32e8dd030dfb04f801bc617d32
7
- data.tar.gz: 3137c6d5b19ecbc496f14c9db9395a598d7e7bb8f33f5863db6eb8f3bf9141db141bd2429b8346101a53d79b4a4d687a4b430ad252e1f99bc58f1f2ea0bc6ac4
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 (DR-001)](#csv-dr-001)
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` (DR-005)
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
- ## Exports
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
  ![Console output — basic theme](docs/images/PrettyGitConsole.png)
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-репозиторию: сводка, активность, авторы, файлы, тепловая карта. Вывод в консоль и форматы: JSON, CSV, Markdown, YAML, XML.
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 (DR-001)](#csv-dr-001)
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` (DR-005)
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 &lt;a@example.com&gt;" 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
  ![Console output — basic theme](docs/images/PrettyGitConsole.png)
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
@@ -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
- case report
66
- when 'summary'
67
- Analytics::Summary.call(enum, filters)
68
- when 'activity'
69
- Analytics::Activity.call(enum, filters)
70
- when 'authors'
71
- Analytics::Authors.call(enum, filters)
72
- when 'files'
73
- Analytics::Files.call(enum, filters)
74
- when 'heatmap'
75
- Analytics::Heatmap.call(enum, filters)
76
- when 'languages'
77
- Analytics::Languages.call(enum, filters)
78
- else
79
- raise ArgumentError, "Unknown report: #{report}"
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
@@ -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
- case report
166
- when 'summary'
167
- render_summary(result)
168
- when 'activity'
169
- render_activity(result)
170
- when 'authors'
171
- render_authors(result)
172
- when 'files'
173
- render_files(result)
174
- when 'heatmap'
175
- render_heatmap(result)
176
- when 'languages'
177
- LanguagesSection.render(@io, @table, result, color: @color)
178
- else
179
- @io.puts result.inspect
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
- case report
15
- when 'activity'
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
- case report
14
- when 'summary'
15
- render_summary(result)
16
- when 'activity'
17
- render_table('Activity', %w[bucket timestamp commits additions deletions], result[:items])
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])
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PrettyGit
4
- VERSION = '0.1.2'
4
+ VERSION = '0.1.3'
5
5
  end
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.2
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