pretty-git 0.1.1 → 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.
data/README.ru.md ADDED
@@ -0,0 +1,511 @@
1
+ # Pretty Git
2
+
3
+ [![CI](https://github.com/MikoMikocchi/pretty-git/actions/workflows/ci.yml/badge.svg)](https://github.com/MikoMikocchi/pretty-git/actions/workflows/ci.yml)
4
+ [![Gem Version](https://img.shields.io/gem/v/pretty-git)](https://rubygems.org/gems/pretty-git)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
+ ![Ruby 3.4+](https://img.shields.io/badge/ruby-3.4%2B-red)
7
+
8
+ <p align="right">
9
+ <a href="./README.md">English</a> | <b>Русский</b>
10
+ </p>
11
+
12
+ <p align="center">
13
+ <img src="docs/images/PrettyGitIcon.png" alt="Pretty Git Logo" width="200">
14
+ <br>
15
+ </p>
16
+
17
+ Генератор отчётов по локальному Git-репозиторию: сводка, активность, авторы, файлы, теплокарта, языки, hotspots, churn, ownership. Вывод в консоль и форматы: JSON, CSV, Markdown, YAML, XML.
18
+
19
+ — Лицензия: MIT.
20
+
21
+ ## Содержание
22
+ - [Возможности](#возможности)
23
+ - [Требования](#требования)
24
+ - [Установка](#установка)
25
+ - [Быстрый старт](#быстрый-старт)
26
+ - [CLI и параметры](#cli-и-параметры)
27
+ - [Фильтры](#фильтры)
28
+ - [Формат вывода](#формат-вывода)
29
+ - [Вывод в файл](#вывод-в-файл)
30
+ - [Коды возврата](#коды-возврата)
31
+ - [Отчёты и примеры](#отчёты-и-примеры)
32
+ - [summary — сводка](#summary--сводка)
33
+ - [activity — активность (day/week/month)](#activity--активность-dayweekmonth)
34
+ - [authors — по авторам](#authors--по-авторам)
35
+ - [files — по файлам](#files--по-файлам)
36
+ - [heatmap — тепловая карта](#heatmap--тепловая-карта)
37
+ - [languages — языки](#languages--языки)
38
+ - [hotspots — рискованные файлы](#hotspots--рискованные-файлы)
39
+ - [churn — churn по файлам](#churn--churn-по-файлам)
40
+ - [ownership — владение кодом](#ownership--владение-кодом)
41
+ - [Экспорт в форматы](#экспорт-в-форматы)
42
+ - [Console](#console)
43
+ - [JSON](#json)
44
+ - [CSV](#csv)
45
+ - [Markdown](#markdown)
46
+ - [YAML](#yaml)
47
+ - [XML](#xml)
48
+ - [Детерминизм и сортировка](#детерминизм-и-сортировка)
49
+ - [Советы по Windows](#советы-по-windows)
50
+ - [Диагностика и ошибки](#диагностика-и-ошибки)
51
+ - [FAQ](#faq)
52
+ - [Разработка](#разработка)
53
+ - [Лицензия](#лицензия)
54
+
55
+ ## ✨ Возможности
56
+ * __Отчёты__: `summary`, `activity`, `authors`, `files`, `heatmap`, `languages`, `hotspots`, `churn`, `ownership`.
57
+ * __Фильтры__: ветки, авторы, пути, период времени.
58
+ * __Экспорт__: `console`, `json`, `csv`, `md`, `yaml`, `xml`.
59
+ * __Вывод__: в stdout или файл через `--out`.
60
+
61
+ ## ⚙️ Требования
62
+ * __Ruby__: >= 3.4 (рекомендуется 3.4.x)
63
+ * __Git__: установлен и доступен в `PATH`
64
+
65
+ ## 📦 Установка
66
+
67
+ ### 🍺 Homebrew (рекомендуется)
68
+ ```bash
69
+ brew tap MikoMikocchi/tap
70
+ brew install pretty-git
71
+ ```
72
+
73
+ ### ♦️ RubyGems
74
+ ```bash
75
+ gem install pretty-git
76
+ ```
77
+ Выберите один из вариантов:
78
+
79
+ 1) 🛠️ Локально из исходников (рекомендуется для разработки)
80
+
81
+ ```bash
82
+ git clone <repo_url>
83
+ cd pretty-git
84
+ bin/setup
85
+ # запуск:
86
+ bundle exec bin/pretty-git --help
87
+ ```
88
+
89
+ 2) ♦️ Как gem (после первого релиза)
90
+
91
+ ```bash
92
+ gem install pretty-git
93
+ pretty-git --version
94
+ ```
95
+
96
+ 3) 📦 Использование через Bundler
97
+
98
+ ```ruby
99
+ # Gemfile
100
+ gem 'pretty-git', '~> 0.1'
101
+ ```
102
+ ```bash
103
+ bundle install
104
+ bundle exec pretty-git --help
105
+ ```
106
+
107
+ ## 🚀 Быстрый старт
108
+ ```bash
109
+ # Сводка репозитория в консоль
110
+ bundle exec bin/pretty-git summary .
111
+
112
+ # Авторы в JSON и запись в файл
113
+ bundle exec bin/pretty-git authors . --format json --out authors.json
114
+
115
+ # Активность по неделям за период и только по указанным путям
116
+ bundle exec bin/pretty-git activity . --time-bucket week --since 2025-01-01 \
117
+ --paths app,lib --format csv --out activity.csv
118
+ ```
119
+
120
+ ## 🧰 CLI и параметры
121
+ Общий вид:
122
+
123
+ ```bash
124
+ pretty-git <report> <repo_path> [options]
125
+ ```
126
+
127
+ Доступные отчёты: `summary`, `activity`, `authors`, `files`, `heatmap`, `languages`, `hotspots`, `churn`, `ownership`.
128
+
129
+ Ключевые опции:
130
+ * __--format, -f__ `console|json|csv|md|yaml|xml` (по умолчанию `console`)
131
+ * __--out, -o__ Путь для записи в файл
132
+ * __--limit, -l__ Число элементов в топах/выводе; `all` или `0` — без ограничения
133
+ * __--time-bucket__ `day|week|month` (для `activity`)
134
+ * __--since/--until__ Дата/время в ISO8601 или `YYYY-MM-DD`
135
+ * __--branch__ Мульти-опция, можно указывать несколько веток
136
+ * __--author/--exclude-author__ Фильтрация по авторам
137
+ * __--path/--exclude-path__ Фильтрация по путям (через запятую или повтор опции)
138
+ * __--no-color__ Отключить цвета в консоли
139
+ * __--theme__ `basic|bright|mono` — тема оформления консольного вывода (по умолчанию `basic`; `mono` принудительно отключает цвета)
140
+ * __--metric__ `bytes|files|loc` — метрика для отчёта `languages` (по умолчанию `bytes`)
141
+
142
+ Примеры значений с несколькими параметрами:
143
+
144
+ ```bash
145
+ # Несколько веток
146
+ pretty-git summary . --branch main --branch develop
147
+
148
+ # Фильтрация по авторам (включая/исключая)
149
+ pretty-git authors . --author alice@example.com --exclude-author bot@company
150
+
151
+ # Фильтрация по путям
152
+ pretty-git files . --path app,lib --exclude-path vendor,node_modules
153
+ ```
154
+
155
+ ### Фильтры
156
+ Фильтры применяются на этапе выборки коммитов и последующей агрегации. Формат дат: ISO8601 или `YYYY-MM-DD`. Если часовой пояс не указан — используется локальная зона пользователя; на выводе время нормализуется к UTC.
157
+
158
+ ### Формат вывода
159
+ Задаётся через `--format`. Для файловых форматов рекомендуется использовать `--out`.
160
+
161
+ ### Вывод в файл
162
+ ```bash
163
+ pretty-git authors . --format csv --out authors.csv
164
+ ```
165
+
166
+ ### Коды возврата
167
+ * `0` — успех
168
+ * `1` — ошибка пользователя (неизвестный отчёт/формат, неверные аргументы)
169
+ * `2` — системная ошибка (ошибка Git и пр.)
170
+
171
+ ## 📊 Отчёты и примеры
172
+
173
+ ### 🧭 summary — сводка
174
+ ```bash
175
+ pretty-git summary . --format json
176
+ ```
177
+ Содержит totals (commits, authors, additions, deletions) и топы по авторам/файлам.
178
+
179
+ ### 📆 activity — активность (day/week/month)
180
+ ```bash
181
+ pretty-git activity . --time-bucket week --format csv
182
+ ```
183
+ CSV-колонки: `bucket,timestamp,commits,additions,deletions`.
184
+ Пример JSON:
185
+ ```json
186
+ [
187
+ {"bucket":"week","timestamp":"2025-06-02T00:00:00Z","commits":120,"additions":3456,"deletions":2100},
188
+ {"bucket":"week","timestamp":"2025-06-09T00:00:00Z","commits":98,"additions":2890,"deletions":1760}
189
+ ]
190
+ ```
191
+
192
+ ### 👤 authors — по авторам
193
+ ```bash
194
+ pretty-git authors . --format md --limit 10
195
+ ```
196
+ CSV-колонки: `author,author_email,commits,additions,deletions,avg_commit_size`.
197
+ Пример Markdown:
198
+ ```markdown
199
+ | author | author_email | commits | additions | deletions | avg_commit_size |
200
+ |---|---|---:|---:|---:|---:|
201
+ | Alice | a@example.com | 2 | 5 | 1 | 3.0 |
202
+ | Bob | b@example.com | 1 | 2 | 0 | 2.0 |
203
+ ```
204
+
205
+ ### 📁 files — по файлам
206
+ ```bash
207
+ pretty-git files . --paths app,lib --format csv
208
+ ```
209
+ CSV-колонки: `path,commits,additions,deletions,changes`.
210
+ Пример XML:
211
+ ```xml
212
+ <files>
213
+ <item path="app/models/user.rb" commits="42" additions="2100" deletions="1400" changes="3500" />
214
+ <item path="app/services/auth.rb" commits="35" additions="1500" deletions="900" changes="2400" />
215
+ <generated_at>2025-01-31T00:00:00Z</generated_at>
216
+ <repo_path>/abs/path/to/repo</repo_path>
217
+ <report>files</report>
218
+ <period>
219
+ <since/>
220
+ <until/>
221
+ </period>
222
+ </files>
223
+ ```
224
+
225
+ ### 🔥 heatmap — тепловая карта
226
+ ```bash
227
+ pretty-git heatmap . --format json
228
+ ```
229
+ JSON: массив бакетов по (день недели × час) с числом коммитов.
230
+ Пример CSV:
231
+ ```csv
232
+ dow,hour,commits
233
+ 1,10,5
234
+ 1,11,7
235
+ ```
236
+
237
+ ### 🈺 languages — языки
238
+ ```bash
239
+ pretty-git languages . --format md --limit 10
240
+ ```
241
+ Определяет распределение языков в репозитории. Поддерживаются несколько метрик: `bytes`, `files`, `loc` (по умолчанию: `bytes`). Вывод включает язык, выбранную метрику и долю в процентах; в консоли отображаются цвета языков.
242
+
243
+ Пример консольного вывода:
244
+ ```text
245
+ Languages for .
246
+
247
+ language bytes percent
248
+ -------- ---------- -------
249
+ Ruby 123456 60.0
250
+ JavaScript 78901 38.3
251
+ Markdown 1200 1.7
252
+ ```
253
+
254
+ ![Console output — languages](docs/images/PrettyGitConsoleLanguages.png)
255
+
256
+ Замечания:
257
+ - __Определение языка__: по расширениям файлов и некоторым именам файлов (`Makefile`, `Dockerfile`).
258
+ - __Исключения__: бинарные файлы и "vendor"‑директории игнорируются. По умолчанию пропускаются `vendor/`, `node_modules/`, `.git/`, артефакты сборки и кэши. Для Python дополнительно исключаются `.venv/`, `venv/`, `env/`, `__pycache__/`, `.mypy_cache/`, `.pytest_cache/`, `.tox/`, `.eggs/`, `.ruff_cache/`, `.ipynb_checkpoints/`.
259
+ - __JSON__: JSON включён как язык. Если крупные файлы данных искажают картину, сузьте область `--path/--exclude-path`.
260
+ - __Фильтры путей__: используйте `--path/--exclude-path` (поддерживаются glob‑маски), чтобы сосредоточиться на соответствующих директориях.
261
+ - __Лимит__: `--limit N` ограничивает число строк в итоговой таблице; `0`/`all` — без ограничения.
262
+ - __Цвета в консоли__: имена языков подсвечиваются приблизительными цветами GitHub; `--no-color` отключает, `--theme mono` делает монохром.
263
+
264
+ Экспорт:
265
+ - CSV/MD: колонки динамические — `language,<metric>,percent`. В Markdown дополнительно присутствует колонка `color`.
266
+ - JSON/YAML/XML: полная структура отчёта, включая `color` на язык и метаданные (`report`, `generated_at`, `repo_path`).
267
+
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
+ ## 🚫 Игнорируемые директории и файлы
350
+
351
+ Чтобы статистика по языкам оставалась релевантной, некоторые директории и типы файлов пропускаются по умолчанию.
352
+
353
+ **Игнорируемые директории** (если сегмент пути совпадает):
354
+
355
+ ```
356
+ vendor, node_modules, .git, .bundle, dist, build, out, target, coverage,
357
+ .venv, venv, env, __pycache__, .mypy_cache, .pytest_cache, .tox, .eggs, .ruff_cache,
358
+ .ipynb_checkpoints
359
+ ```
360
+
361
+ **Игнорируемые бинарные/данные расширения**:
362
+
363
+ ```
364
+ .png, .jpg, .jpeg, .gif, .svg, .webp, .ico, .bmp,
365
+ .pdf, .zip, .tar, .gz, .tgz, .bz2, .7z, .rar,
366
+ .mp3, .ogg, .wav, .mp4, .mov, .avi, .mkv,
367
+ .woff, .woff2, .ttf, .otf, .eot,
368
+ .jar, .class, .dll, .so, .dylib,
369
+ .exe, .bin, .dat
370
+ ```
371
+
372
+ Списки соответствуют реализации в `lib/pretty_git/analytics/languages.rb` и могут изменяться.
373
+
374
+ ## 📤 Экспорт в форматы
375
+
376
+ Ниже — точные правила сериализации для каждого формата, чтобы обеспечивалась совместимость с популярными инструментами (Excel, BI, CI и т.п.).
377
+
378
+ ### 🖥️ Console
379
+ ![Console output — basic theme](docs/images/PrettyGitConsole.png)
380
+ _Пример вывода в терминале (тема: basic)._
381
+ * __Цвета__: заголовки и шапки таблиц подсвечены; суммы: `commits` — жёлтым, `+additions` — зелёным, `-deletions` — красным. `--no-color` полностью отключает раскраску.
382
+ * __Темы__: `--theme basic|bright|mono`. `bright` — более насыщенные заголовки, `mono` — монохром (аналогично `--no-color`).
383
+ * __Выделение максимума__: на числовых колонках максимальные значения подчёркиваются жирным для быстрого сканирования.
384
+ * __Ширина терминала__: при выводе таблиц автоматически учитывается ширина терминала. Если ширины не хватает, первый столбец аккуратно обрезается с многоточием `…`.
385
+ * __Кодировка__: UTF‑8, переводы строк LF.
386
+ * __Назначение__: человекочитаемый вывод в терминал.
387
+ * __Оформление__: таблицы с границами, авто-обрезка длинных значений.
388
+ * __Цвета__: включены, если есть TTY; отключаются `--no-color`.
389
+ * __Пустые наборы__: печатается `No data`.
390
+ * __Кодировка/переводы строк__: UTF‑8, LF (`\n`).
391
+
392
+ ### 🧾 JSON
393
+ * __Ключи__: `snake_case`.
394
+ * __Числа__: целые/вещественные без локализации (точка как разделитель).
395
+ * __Булевы__: `true/false`; __null__: `null`.
396
+ * __Даты/время__: ISO8601 в UTC, например `2025-01-31T00:00:00Z`.
397
+ * __Порядок__: поля в объектах стабильно упорядочены логически (например, `report`, `generated_at`, `repo_path`, затем данные).
398
+ * __Кодировка/переводы строк__: UTF‑8, LF.
399
+ * __Рекомендуемое расширение__: `.json`.
400
+ * __Пример__:
401
+ ```json
402
+ {"report":"summary","generated_at":"2025-01-31T00:00:00Z","totals":{"commits":123}}
403
+ ```
404
+
405
+ ### CSV
406
+ * __Структура__: плоская таблица, первая строка — заголовок.
407
+ * __Кодировка__: UTF‑8, без BOM.
408
+ * __Разделитель__: запятая `,`.
409
+ * __Экранирование__: по RFC 4180 — поля с запятыми/кавычками/переводами строк заключаются в двойные кавычки, двойные кавычки внутри поля удваиваются.
410
+ * __Пустые значения__: пустая ячейка (не `null`).
411
+ * __Числа__: без тысячных разделителей, десятичная точка.
412
+ * __Даты/время__: ISO8601 UTC.
413
+ * __Порядок колонок__: фиксирован на отчёт и стабилен.
414
+ * __Переводы строк__: LF.
415
+ * __Рекомендуемое расширение__: `.csv`.
416
+ * __Открытие в Excel__: указывайте кодировку UTF‑8 при импорте.
417
+ * __Пример__:
418
+ ```csv
419
+ author,author_email,commits,additions,deletions,avg_commit_size
420
+ Alice,a@example.com,2,5,1,3.0
421
+ Bob,b@example.com,1,2,0,2.0
422
+ ```
423
+
424
+ ### 📝 Markdown
425
+ * __Таблицы__: стандартный синтаксис GitHub Flavored Markdown.
426
+ * __Выравнивание__: числовые колонки выравниваются по правому краю (`---:`).
427
+ * __Кодировка/переводы строк__: UTF‑8, LF.
428
+ * __Рекомендуемое расширение__: `.md`.
429
+ * __Пустые наборы__: таблица с заголовком и без строк либо краткое сообщение `No data` (в зависимости от отчёта).
430
+ * __Пример__:
431
+ ```markdown
432
+ | path | commits | additions | deletions |
433
+ |---|---:|---:|---:|
434
+ | app/models/user.rb | 42 | 2100 | 1400 |
435
+ ```
436
+
437
+ ### 📄 YAML
438
+ * __Структура__: полная иерархия результата.
439
+ * __Ключи__: сериализуются строками.
440
+ * __Числа/булевы/null__: стандарт YAML (`123`, `true/false`, `null`).
441
+ * __Даты/время__: ISO8601 UTC как строки.
442
+ * __Кодировка/переводы строк__: UTF‑8, LF.
443
+ * __Рекомендуемое расширение__: `.yml` или `.yaml`.
444
+ * __Пример__:
445
+ ```yaml
446
+ report: authors
447
+ generated_at: "2025-01-31T00:00:00Z"
448
+ items:
449
+ - author: Alice
450
+ author_email: a@example.com
451
+ commits: 2
452
+ - author: Bob
453
+ author_email: b@example.com
454
+ commits: 1
455
+ ```
456
+
457
+ ### 🗂️ XML
458
+ * __Структура__: элементы соответствуют ключам; массивы — повторяющиеся `<item>` или специализированные теги.
459
+ * __Атрибуты__: для компактных записей (например, строк файлового отчёта) основные поля могут быть атрибутами элемента.
460
+ * __Текстовые узлы__: применяются для скалярных значений при необходимости.
461
+ * __Экранирование__: `& < > " ' ` по правилам XML, при необходимости CDATA для произвольного текста.
462
+ * __Даты/время__: ISO8601 UTC.
463
+ * __Кодировка/переводы строк__: UTF‑8, LF; декларация `<?xml version="1.0" encoding="UTF-8"?>` может добавляться генератором.
464
+ * __Рекомендуемое расширение__: `.xml`.
465
+ * __Пример__:
466
+ ```xml
467
+ <authors>
468
+ <item author="Alice" author_email="a@example.com" commits="2" />
469
+ <item author="Bob" author_email="b@example.com" commits="1" />
470
+ <generated_at>2025-01-31T00:00:00Z</generated_at>
471
+ <repo_path>/abs/path</repo_path>
472
+ </authors>
473
+ ```
474
+
475
+ ## 🔁 Детерминизм и сортировка
476
+ Вывод детерминирован при одинаковых входных данных. Сортировка для файлов/авторов: по количеству изменений (desc), затем по числу коммитов (desc), затем по пути/имени (asc). Лимиты применяются поверх отсортированного списка; значение `all` или `0` означает отсутствие ограничения.
477
+
478
+ ## 🪟 Советы по Windows
479
+ Целевая платформа — macOS/Linux. Windows поддерживается в режиме best‑effort:
480
+ * Запуск через Git Bash/WSL допустим
481
+ * Цвета можно отключить `--no-color`
482
+ * Аккуратное квотирование аргументов при работе с путями
483
+
484
+ ## 🩺 Диагностика и ошибки
485
+ Типичные ошибки и решения:
486
+
487
+ * __Неизвестный отчёт/формат__ — проверьте значение первого аргумента и `--format`.
488
+ * __Неверный формат даты__ — используйте ISO8601 или `YYYY-MM-DD` (например, `2025-01-31` или `2025-01-31T12:00:00Z`).
489
+ * __Git недоступен__ — убедитесь, что `git` установлен и доступен в `PATH`.
490
+ * __Пустой результат__ — проверьте фильтры (`--since/--until`, `--branch`, `--path`), возможно, выборка слишком узкая.
491
+ * __Проблемы с кодировкой CSV__ — файлы сохраняются в UTF‑8, при открытии в Excel выбирайте UTF‑8.
492
+
493
+ ## ❓ FAQ
494
+ * __Почему Ruby 3.4+?__ Проект использует зависимости, согласованные с версией 3.4+, и ориентируется на актуальную экосистему.
495
+ * __Можно ли добавить новые форматы?__ Да, добавьте рендерер в `lib/pretty_git/render/` и зарегистрируйте его в приложении.
496
+ * __Откуда берутся данные?__ Из системного `git` через вызовы CLI.
497
+
498
+ ## 🛠️ Разработка
499
+ ```bash
500
+ # Установка зависимостей
501
+ bin/setup
502
+
503
+ # Запуск тестов и линтера
504
+ bundle exec rspec
505
+ bundle exec rubocop
506
+ ```
507
+
508
+ Стиль — RuboCop без ошибок. Тесты покрывают агрегаторы, рендереры, CLI и интеграционные сценарии (детерминизм, корректность форматов).
509
+
510
+ ## 📄 Лицензия
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