pretty-git 0.1.3 → 0.1.5

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: 84571ab2bdd16f92dc31c1883a70433a140253ee0d0c66f1079453fc30f34419
4
- data.tar.gz: 532ee3a2e436163f12821073297dc013e874746c5ac269f89c455826566c9f27
3
+ metadata.gz: 7bd982eee724a56adcb2aa941fdecc2c95309c1bf681a5c3b9a1be0b8bf0306f
4
+ data.tar.gz: 1ef12cd144493563bbe99a869e81fe6aa9436136dc7aac52b713d5436975a48a
5
5
  SHA512:
6
- metadata.gz: a0ba7918e1179ebfe3c4771c995b8b0eda978263be1fc6484f0f913e7b2129472272660e3c6dfb24a11f9d64300c26748d0b01fc14f0594e04e9b88cf244b96c
7
- data.tar.gz: afaa201615eff920f1af0b86384964437fe7a119fc0d43d2a5ae10ee36211215b84c9607b92354646a20d691f0382f0b5d214385a214e8107596cdcf06eefcb2
6
+ metadata.gz: fba3dc59a53571d80cf38e13a555b2ad62b44964f29039a1fe68000349f642d024bb854542d62d58608dd8f9a8169548b0444a0fc8488cccc9a8e3497ba68ade
7
+ data.tar.gz: 1f8037f3e7ef97e13b60be37c6d447c684586ffb78017d1905fc6b75d4d055ce533d4a87f04baf6e15fa734fa20fcbfe8854f5f4ef429c402106cbbf4a9a70b5
data/CHANGELOG.md CHANGED
@@ -5,7 +5,46 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
6
6
 
7
7
  ## [Unreleased]
8
-
8
+ ### Added
9
+ - Docs: `docs/testing.md` covering the golden workflow, snapshot update/validation; linked from `README.md`, `README.ru.md`, and `CONTRIBUTING.md`.
10
+ - Tests: determinism invariants for YAML/XML renderers (stable output regardless of input order).
11
+
12
+ ### Changed
13
+ - Completions: refreshed bash/zsh completions — added short flags `-f`/`-o`/`-l`, values for `--time-bucket` (`day|week|month`), and repo path completion as the 2nd positional argument.
14
+
15
+ ## [0.1.5] - 2025-08-17
16
+ ### Added
17
+ - CLI: warn to stderr when `--theme`/`--no-color` are used with non-console `--format` values.
18
+ - Docs: expanded Filters documentation (branches, authors, paths, time semantics, verbose diagnostics), schemas/examples pointers, performance and CI usage.
19
+ - Tests: unit tests for `PrettyGit::Utils::TimeUtils`.
20
+
21
+ ### Changed
22
+ - Internals: extracted time parsing/normalization to `PrettyGit::Utils::TimeUtils` and centralized verbose logging via `PrettyGit::Logger`. `Git::Provider` routes verbose messages through the centralized logger (stderr).
23
+ - Verbose mode: documentation clarified to note that diagnostics are printed to stderr for easier CI parsing.
24
+
25
+ ### Deprecated
26
+ - Filters: legacy `:until` keyword in `PrettyGit::Filters` initialization is accepted for backward compatibility and emits a deprecation warning; use `:until_at` instead.
27
+
28
+ ### Fixed
29
+ - Filters: allow initialization via a single Hash argument (legacy call sites) while preserving `Struct` keyword semantics.
30
+
31
+ ## [0.1.4] - 2025-08-17
32
+ ### Added
33
+ - Integration tests for new reports exports: CSV/Markdown/YAML/XML for `hotspots`, `churn`, `ownership`.
34
+ - Schema validations: `rake validate:json`, `rake validate:xml` to ensure format compatibility.
35
+ - CI: expanded matrix to include macOS; smoke test for installed binary (`--help`, `--version`).
36
+
37
+ ### Changed
38
+ - Renderers (`MarkdownRenderer`, `YamlRenderer`, `XmlRenderer`): deterministic sorting for all new reports according to `docs/determinism.md`.
39
+ - XML: per-report root elements in XML exports to match XSDs (`hotspotsReport`, `churnReport`, `ownershipReport`, `languagesReport`, etc.).
40
+ - Documentation: `README.md` and `README.ru.md` updated with sections and examples for new reports and all export formats.
41
+ - CLI: keep `--time-bucket` permissive; default `time_bucket=nil`.
42
+
43
+ ### Fixed
44
+ - Time parsing: interpret date-only inputs (`YYYY-MM-DD`) as UTC midnight and normalize to UTC ISO8601.
45
+ - CLI UX: error when `--metric` is used outside `languages` report.
46
+ - Tests/specs: updated XML specs to per-report roots; added timezone edge cases; fixed Open3 `popen3` stubs (`chdir:`) and integration requires.
47
+
9
48
 
10
49
  ## [0.1.3] - 2025-08-14
11
50
  ### Added
@@ -30,7 +69,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve
30
69
  ### Changed
31
70
  - Languages: JSON language reinstated in the mapping and color scheme; sorting and percent calculations are based on the selected metric; percentages rounded to two decimals.
32
71
  - Renderers: updated `csv`, `markdown`, and console renderers to work with dynamic metrics.
33
- - Internal specs updated: `specs/output_formats.md`, `specs/cli_spec.md`, `specs/languages_map.md`.
72
+ - Internal specs updated: `docs/output_formats.md`, `docs/cli_spec.md`, `docs/languages_map.md`.
34
73
 
35
74
  ### Fixed
36
75
  - Git provider: correct commit counting — emit a new commit when a header is read and remove the record separator from the subject (`lib/pretty_git/git/provider.rb`).
data/README.md CHANGED
@@ -58,6 +58,13 @@ Generator of rich reports for a local Git repository: summary, activity, authors
58
58
  * **Exports**: `console`, `json`, `csv`, `md`, `yaml`, `xml`.
59
59
  * **Output**: to stdout or file via `--out`.
60
60
 
61
+ ## ❓ Why Pretty Git
62
+ * **One tool, many views**: activity, authorship, hotspots, languages, ownership — consistent UX and outputs.
63
+ * **Deterministic results**: stable sorting and formatting make it reliable for CI and diffs.
64
+ * **Format-first**: JSON/CSV/Markdown/YAML/XML out of the box with strict and documented rules.
65
+ * **Fast enough for daily use**: streams `git log` and aggregates in-memory; tips below for large repos.
66
+ * **Safe defaults**: sensible path and binary ignores for the `languages` report; colorized console output with themes.
67
+
61
68
  ## ⚙️ Requirements
62
69
  * **Ruby**: >= 3.4 (recommended 3.4.x)
63
70
  * **Git**: installed and available in `PATH`
@@ -124,6 +131,10 @@ General form:
124
131
  pretty-git <report> <repo_path> [options]
125
132
  ```
126
133
 
134
+ Notes:
135
+ * `<repo_path>` defaults to `.` if omitted.
136
+ * You can also pass the repository via `--repo PATH` as an alternative to the positional argument.
137
+
127
138
  Available reports: `summary`, `activity`, `authors`, `files`, `heatmap`, `languages`, `hotspots`, `churn`, `ownership`.
128
139
 
129
140
  Key options:
@@ -138,12 +149,15 @@ Key options:
138
149
  * **--no-color** Disable colors in console
139
150
  * **--theme** `basic|bright|mono` — console theme (default `basic`; `mono` forces monochrome)
140
151
  * **--metric** `bytes|files|loc` — metric for `languages` report (default `bytes`)
152
+ * **--verbose** Print debug information (effective git command, filters)
141
153
 
142
154
  Examples with multiple values:
143
155
 
144
156
  ```bash
145
- # Multiple branches
157
+ # Multiple branches (treated as explicit revisions)
146
158
  pretty-git summary . --branch main --branch develop
159
+ ## This is equivalent to:
160
+ ## git log main develop -- ...
147
161
 
148
162
  # Filter authors (include/exclude)
149
163
  pretty-git authors . --author alice@example.com --exclude-author bot@company
@@ -153,7 +167,30 @@ pretty-git files . --path app,lib --exclude-path vendor,node_modules
153
167
  ```
154
168
 
155
169
  ### Filters
156
- Filters apply at commit fetch and later aggregation. Date format: ISO8601 or `YYYY-MM-DD`. If timezone is omitted your local zone is assumed; output timestamps are normalized to UTC.
170
+ Filters apply at commit fetch and later aggregation. Belowexact semantics and tips.
171
+
172
+ #### Branches / revisions
173
+ * `--branch BRANCH` may be provided multiple times.
174
+ * Multiple branches are treated as explicit revisions to `git log` (no implicit merge-base range). Example: `--branch main --branch develop` → `git log main develop -- ...`.
175
+ * If no branches are specified, the repository’s current `HEAD` is used.
176
+
177
+ #### Authors
178
+ * `--author` and `--exclude-author` accept name or email substrings (case-insensitive match by `git log`).
179
+ * Multiple values may be provided by repeating the option.
180
+
181
+ #### Paths
182
+ * `--path` and `--exclude-path` accept comma-separated values or repeated options.
183
+ * Globs are supported by git pathspec. Excludes are translated to `:(exclude)pattern` and applied consistently.
184
+ * When only excludes are present, `.` is included to ensure the pathspec is valid (mirrors tests in `spec/pretty_git/git/provider_spec.rb`).
185
+
186
+ #### Time period
187
+ * `--since` / `--until`: ISO8601 (e.g. `2025-01-31T12:00:00Z`) or `YYYY-MM-DD`.
188
+ * Date-only values are interpreted as UTC midnight to avoid timezone drift in different environments.
189
+ * Time values are normalized to UTC in outputs.
190
+
191
+ #### Verbose diagnostics
192
+ * `--verbose` prints the effective `git log` command and active filters to stderr.
193
+ * Useful for debugging filters, CI logs, or when reproducing results locally.
157
194
 
158
195
  ### Output format
159
196
  Set via `--format`. For file formats it’s recommended to use `--out`.
@@ -207,19 +244,31 @@ Markdown example:
207
244
  pretty-git files . --paths app,lib --format csv
208
245
  ```
209
246
  CSV columns: `path,commits,additions,deletions,changes`.
247
+
210
248
  XML example:
211
249
  ```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" />
250
+ <?xml version="1.0" encoding="UTF-8"?>
251
+ <report>
252
+ <report>files</report>
215
253
  <generated_at>2025-01-31T00:00:00Z</generated_at>
216
254
  <repo_path>/abs/path/to/repo</repo_path>
217
- <report>files</report>
218
- <period>
219
- <since/>
220
- <until/>
221
- </period>
222
- </files>
255
+ <items>
256
+ <item>
257
+ <path>app/models/user.rb</path>
258
+ <commits>42</commits>
259
+ <additions>2100</additions>
260
+ <deletions>1400</deletions>
261
+ <changes>3500</changes>
262
+ </item>
263
+ <item>
264
+ <path>app/services/auth.rb</path>
265
+ <commits>35</commits>
266
+ <additions>1500</additions>
267
+ <deletions>900</deletions>
268
+ <changes>2400</changes>
269
+ </item>
270
+ </items>
271
+ </report>
223
272
  ```
224
273
 
225
274
  ### 🔥 heatmap — commit heatmap
@@ -337,15 +386,20 @@ Notes:
337
386
 
338
387
  XML example:
339
388
  ```xml
340
- <ownership>
389
+ <?xml version="1.0" encoding="UTF-8"?>
390
+ <report>
341
391
  <report>ownership</report>
342
392
  <generated_at>2025-01-31T00:00:00Z</generated_at>
343
393
  <repo_path>.</repo_path>
344
394
  <items>
345
- <item path="lib/a.rb" owner="Alice <a@example.com>" owner_share="82.5" authors="2"/>
395
+ <item>
396
+ <path>lib/a.rb</path>
397
+ <owner>Alice &lt;a@example.com&gt;</owner>
398
+ <owner_share>82.5</owner_share>
399
+ <authors>2</authors>
400
+ </item>
346
401
  </items>
347
-
348
- </ownership>
402
+ </report>
349
403
  ```
350
404
 
351
405
  ## 📤 Exports
@@ -376,6 +430,13 @@ _Example terminal output (theme: basic)._
376
430
  {"report":"summary","generated_at":"2025-01-31T00:00:00Z","totals":{"commits":123}}
377
431
  ```
378
432
 
433
+ ## 🧾 Schemas and Examples
434
+ Machine-readable examples and schemas live under `docs/export_schemas/` and `docs/examples/`.
435
+
436
+ * **Schemas**: see `docs/export_schemas/README.md` for JSON and XML schema notes.
437
+ * **Examples**: example payloads for JSON/XML for each report under `docs/examples/`.
438
+ * Intended use: validation in CI, contract documentation, and integration tests.
439
+
379
440
  ### CSV
380
441
  * **Structure**: flat table, first line is header.
381
442
  * **Encoding**: UTF‑8 without BOM.
@@ -474,16 +535,46 @@ These lists mirror the implementation in `lib/pretty_git/analytics/languages.rb`
474
535
  ## 🔁 Determinism and Sorting
475
536
  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.
476
537
 
538
+ ## ⚡ Performance Tips
539
+ * Prefer narrowing by `--path`/`--exclude-path` and `--since/--until` on large repositories.
540
+ * Use multiple `--branch` only when you explicitly want to include several heads; otherwise rely on current `HEAD`.
541
+ * For CI, cache the repository and fetch shallow history if full history is unnecessary for your report.
542
+
543
+ ## 🤖 CI Usage
544
+ Examples for common pipelines:
545
+
546
+ ```yaml
547
+ # GitHub Actions (excerpt)
548
+ jobs:
549
+ reports:
550
+ runs-on: ubuntu-latest
551
+ steps:
552
+ - uses: actions/checkout@v4
553
+ - uses: ruby/setup-ruby@v1
554
+ with:
555
+ ruby-version: '3.4'
556
+ - run: gem install pretty-git
557
+ - run: pretty-git authors . --format json --out authors.json
558
+ - uses: actions/upload-artifact@v4
559
+ with:
560
+ name: authors-report
561
+ path: authors.json
562
+ ```
563
+
477
564
  ## 🪟 Windows Notes
478
- Primary targets — macOS/Linux. Windows is supported best‑effort:
479
- * Running via Git Bash/WSL is OK
480
- * Colors can be disabled by `--no-color`
481
- * Carefully quote arguments when working with paths
565
+ Primary targets — macOS/Linux. Windows is supported best‑effort. See detailed notes in [docs/windows.md](docs/windows.md).
566
+
567
+ Highlights:
568
+ * Running via Git Bash/WSL is recommended.
569
+ * CRLF output from git is handled by the parser; exports use UTF‑8 with LF.
570
+ * Path filters are normalized to Unicode NFC when available; otherwise pass‑through.
571
+ * Colors can be disabled by `--no-color` or `--theme mono`.
482
572
 
483
573
  ## 🩺 Diagnostics and Errors
484
574
  Typical issues and solutions:
485
575
 
486
576
  * **Unknown report/format** — check the first argument and `--format`.
577
+ * **Debugging** — add `--verbose` to see the effective `git log` command and applied filters.
487
578
  * **Invalid date format** — use ISO8601 or `YYYY-MM-DD` (e.g., `2025-01-31` or `2025-01-31T12:00:00Z`).
488
579
  * **Git not available** — ensure `git` is installed and in the `PATH`.
489
580
  * **Empty result** — check your filters (`--since/--until`, `--branch`, `--path`); your selection might be too narrow.
@@ -506,5 +597,7 @@ bundle exec rubocop
506
597
 
507
598
  Style — RuboCop clean. Tests cover aggregators, renderers, CLI, and integration scenarios (determinism, format correctness).
508
599
 
600
+ For detailed testing strategy, determinism rules, and golden tests workflow (how to run/update snapshots), see `docs/testing.md`.
601
+
509
602
  ## 📄 License
510
603
  MIT © Contributors
data/README.ru.md CHANGED
@@ -20,6 +20,7 @@
20
20
 
21
21
  ## Содержание
22
22
  - [Возможности](#возможности)
23
+ - [Почему Pretty Git](#почему-pretty-git)
23
24
  - [Требования](#требования)
24
25
  - [Установка](#установка)
25
26
  - [Быстрый старт](#быстрый-старт)
@@ -41,11 +42,14 @@
41
42
  - [Экспорт в форматы](#экспорт-в-форматы)
42
43
  - [Console](#console)
43
44
  - [JSON](#json)
45
+ - [Схемы и примеры](#схемы-и-примеры)
44
46
  - [CSV](#csv)
45
47
  - [Markdown](#markdown)
46
48
  - [YAML](#yaml)
47
49
  - [XML](#xml)
48
50
  - [Детерминизм и сортировка](#детерминизм-и-сортировка)
51
+ - [Советы по производительности](#советы-по-производительности)
52
+ - [Использование в CI](#использование-в-ci)
49
53
  - [Советы по Windows](#советы-по-windows)
50
54
  - [Диагностика и ошибки](#диагностика-и-ошибки)
51
55
  - [FAQ](#faq)
@@ -58,6 +62,13 @@
58
62
  * __Экспорт__: `console`, `json`, `csv`, `md`, `yaml`, `xml`.
59
63
  * __Вывод__: в stdout или файл через `--out`.
60
64
 
65
+ ## ❓ Почему Pretty Git
66
+ * __Один инструмент — много представлений__: активность, авторство, риск, языки, владение — единый UX и форматы.
67
+ * __Детерминированные результаты__: стабильные сортировки и форматирование — удобно для CI и диффов.
68
+ * __Сразу форматы__: JSON/CSV/Markdown/YAML/XML из коробки с чёткими правилами.
69
+ * __Достаточно быстро__: потоковый `git log` и ин‑мемори агрегации; советы для больших репозиториев ниже.
70
+ * __Безопасные дефолты__: разумные игноры путей и бинарников для отчёта `languages`; цветной консольный вывод с темами.
71
+
61
72
  ## ⚙️ Требования
62
73
  * __Ruby__: >= 3.4 (рекомендуется 3.4.x)
63
74
  * __Git__: установлен и доступен в `PATH`
@@ -124,6 +135,10 @@ bundle exec bin/pretty-git activity . --time-bucket week --since 2025-01-01 \
124
135
  pretty-git <report> <repo_path> [options]
125
136
  ```
126
137
 
138
+ Примечания:
139
+ * `<repo_path>` по умолчанию — `.` (если опущен).
140
+ * Репозиторий можно указать и через флаг `--repo PATH` как альтернативу позиционному аргументу.
141
+
127
142
  Доступные отчёты: `summary`, `activity`, `authors`, `files`, `heatmap`, `languages`, `hotspots`, `churn`, `ownership`.
128
143
 
129
144
  Ключевые опции:
@@ -138,12 +153,15 @@ pretty-git <report> <repo_path> [options]
138
153
  * __--no-color__ Отключить цвета в консоли
139
154
  * __--theme__ `basic|bright|mono` — тема оформления консольного вывода (по умолчанию `basic`; `mono` принудительно отключает цвета)
140
155
  * __--metric__ `bytes|files|loc` — метрика для отчёта `languages` (по умолчанию `bytes`)
156
+ * **--verbose** Печатать отладочную информацию (эффективная команда git, применённые фильтры)
141
157
 
142
158
  Примеры значений с несколькими параметрами:
143
159
 
144
160
  ```bash
145
- # Несколько веток
161
+ # Несколько веток (трактуются как явные ревизии)
146
162
  pretty-git summary . --branch main --branch develop
163
+ ## Эквивалентно:
164
+ ## git log main develop -- ...
147
165
 
148
166
  # Фильтрация по авторам (включая/исключая)
149
167
  pretty-git authors . --author alice@example.com --exclude-author bot@company
@@ -153,7 +171,30 @@ pretty-git files . --path app,lib --exclude-path vendor,node_modules
153
171
  ```
154
172
 
155
173
  ### Фильтры
156
- Фильтры применяются на этапе выборки коммитов и последующей агрегации. Формат дат: ISO8601 или `YYYY-MM-DD`. Если часовой пояс не указан используется локальная зона пользователя; на выводе время нормализуется к UTC.
174
+ Фильтры применяются на этапе выборки коммитов и последующей агрегации. Нижеточные правила и советы.
175
+
176
+ #### Ветки / ревизии
177
+ * `--branch BRANCH` можно указывать несколько раз.
178
+ * Несколько веток трактуются как явные ревизии для `git log` (без неявного диапазона от merge-base). Пример: `--branch main --branch develop` → `git log main develop -- ...`.
179
+ * Если ветки не указаны — используется текущий `HEAD` репозитория.
180
+
181
+ #### Авторы
182
+ * `--author` и `--exclude-author` принимают подстроки имени или email (регистронезависимо по логике `git log`).
183
+ * Можно передавать несколько значений повтором опции.
184
+
185
+ #### Пути
186
+ * `--path` и `--exclude-path` принимают значения через запятую или повтором опции.
187
+ * Поддерживаются glob‑маски git pathspec. Исключения переводятся в `:(exclude)pattern` и применяются последовательно.
188
+ * Если заданы только исключения — автоматически добавляется `.` для корректного pathspec (как в тестах `spec/pretty_git/git/provider_spec.rb`).
189
+
190
+ #### Период времени
191
+ * `--since` / `--until`: ISO8601 (например, `2025-01-31T12:00:00Z`) или `YYYY-MM-DD`.
192
+ * Даты без времени интерпретируются как полночь UTC, чтобы исключить сдвиги между средами.
193
+ * На выводе время нормализуется к UTC.
194
+
195
+ #### Подробная диагностика (verbose)
196
+ * `--verbose` печатает эффективную команду `git log` и активные фильтры в stderr.
197
+ * Полезно для отладки фильтров, логов CI и воспроизведения результатов локально.
157
198
 
158
199
  ### Формат вывода
159
200
  Задаётся через `--format`. Для файловых форматов рекомендуется использовать `--out`.
@@ -207,19 +248,31 @@ CSV-колонки: `author,author_email,commits,additions,deletions,avg_commit_
207
248
  pretty-git files . --paths app,lib --format csv
208
249
  ```
209
250
  CSV-колонки: `path,commits,additions,deletions,changes`.
251
+
210
252
  Пример XML:
211
253
  ```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" />
254
+ <?xml version="1.0" encoding="UTF-8"?>
255
+ <report>
256
+ <report>files</report>
215
257
  <generated_at>2025-01-31T00:00:00Z</generated_at>
216
258
  <repo_path>/abs/path/to/repo</repo_path>
217
- <report>files</report>
218
- <period>
219
- <since/>
220
- <until/>
221
- </period>
222
- </files>
259
+ <items>
260
+ <item>
261
+ <path>app/models/user.rb</path>
262
+ <commits>42</commits>
263
+ <additions>2100</additions>
264
+ <deletions>1400</deletions>
265
+ <changes>3500</changes>
266
+ </item>
267
+ <item>
268
+ <path>app/services/auth.rb</path>
269
+ <commits>35</commits>
270
+ <additions>1500</additions>
271
+ <deletions>900</deletions>
272
+ <changes>2400</changes>
273
+ </item>
274
+ </items>
275
+ </report>
223
276
  ```
224
277
 
225
278
  ### 🔥 heatmap — тепловая карта
@@ -335,15 +388,20 @@ CSV‑колонки: `path,owner,owner_share,authors`.
335
388
 
336
389
  Пример XML:
337
390
  ```xml
338
- <ownership>
391
+ <?xml version="1.0" encoding="UTF-8"?>
392
+ <report>
339
393
  <report>ownership</report>
340
394
  <generated_at>2025-01-31T00:00:00Z</generated_at>
341
395
  <repo_path>.</repo_path>
342
396
  <items>
343
- <item path="lib/a.rb" owner="Alice &lt;a@example.com&gt;" owner_share="82.5" authors="2"/>
397
+ <item>
398
+ <path>lib/a.rb</path>
399
+ <owner>Alice &lt;a@example.com&gt;</owner>
400
+ <owner_share>82.5</owner_share>
401
+ <authors>2</authors>
402
+ </item>
344
403
  </items>
345
-
346
- </ownership>
404
+ </report>
347
405
  ```
348
406
 
349
407
  ## 🚫 Игнорируемые директории и файлы
@@ -402,6 +460,13 @@ _Пример вывода в терминале (тема: basic)._
402
460
  {"report":"summary","generated_at":"2025-01-31T00:00:00Z","totals":{"commits":123}}
403
461
  ```
404
462
 
463
+ ## 🧾 Схемы и примеры
464
+ Машиночитаемые примеры и схемы лежат в `docs/export_schemas/` и `docs/examples/`.
465
+
466
+ * __Схемы__: смотрите `docs/export_schemas/README.md` с заметками по JSON и XML схемам.
467
+ * __Примеры__: примеры нагрузок для JSON/XML по каждому отчёту — в `docs/examples/`.
468
+ * Назначение: валидация в CI, документация контрактов и интеграционные тесты.
469
+
405
470
  ### CSV
406
471
  * __Структура__: плоская таблица, первая строка — заголовок.
407
472
  * __Кодировка__: UTF‑8, без BOM.
@@ -475,6 +540,32 @@ _Пример вывода в терминале (тема: basic)._
475
540
  ## 🔁 Детерминизм и сортировка
476
541
  Вывод детерминирован при одинаковых входных данных. Сортировка для файлов/авторов: по количеству изменений (desc), затем по числу коммитов (desc), затем по пути/имени (asc). Лимиты применяются поверх отсортированного списка; значение `all` или `0` означает отсутствие ограничения.
477
542
 
543
+ ## ⚡ Советы по производительности
544
+ * На больших репозиториях сужайте выборку `--path/--exclude-path` и `--since/--until`.
545
+ * Несколько `--branch` используйте, только если нужно включить несколько голов; иначе полагайтесь на текущий `HEAD`.
546
+ * В CI кешируйте репозиторий и используйте shallow‑fetch, если полная история не нужна для выбранного отчёта.
547
+
548
+ ## 🤖 Использование в CI
549
+ Пример для GitHub Actions:
550
+
551
+ ```yaml
552
+ # GitHub Actions (фрагмент)
553
+ jobs:
554
+ reports:
555
+ runs-on: ubuntu-latest
556
+ steps:
557
+ - uses: actions/checkout@v4
558
+ - uses: ruby/setup-ruby@v1
559
+ with:
560
+ ruby-version: '3.4'
561
+ - run: gem install pretty-git
562
+ - run: pretty-git authors . --format json --out authors.json
563
+ - uses: actions/upload-artifact@v4
564
+ with:
565
+ name: authors-report
566
+ path: authors.json
567
+ ```
568
+
478
569
  ## 🪟 Советы по Windows
479
570
  Целевая платформа — macOS/Linux. Windows поддерживается в режиме best‑effort:
480
571
  * Запуск через Git Bash/WSL допустим
@@ -484,7 +575,8 @@ _Пример вывода в терминале (тема: basic)._
484
575
  ## 🩺 Диагностика и ошибки
485
576
  Типичные ошибки и решения:
486
577
 
487
- * __Неизвестный отчёт/формат__ — проверьте значение первого аргумента и `--format`.
578
+ * **Неизвестный отчёт/формат** — проверьте первый аргумент и `--format`.
579
+ * **Отладка** — добавьте `--verbose`, чтобы увидеть фактическую команду `git log` и применённые фильтры.
488
580
  * __Неверный формат даты__ — используйте ISO8601 или `YYYY-MM-DD` (например, `2025-01-31` или `2025-01-31T12:00:00Z`).
489
581
  * __Git недоступен__ — убедитесь, что `git` установлен и доступен в `PATH`.
490
582
  * __Пустой результат__ — проверьте фильтры (`--since/--until`, `--branch`, `--path`), возможно, выборка слишком узкая.
@@ -507,5 +599,7 @@ bundle exec rubocop
507
599
 
508
600
  Стиль — RuboCop без ошибок. Тесты покрывают агрегаторы, рендереры, CLI и интеграционные сценарии (детерминизм, корректность форматов).
509
601
 
602
+ Подробнее о стратегии тестирования, правилах детерминизма и процессе работы с golden‑тестами (как запускать/обновлять снапшоты) см. `docs/testing.md`.
603
+
510
604
  ## 📄 Лицензия
511
605
  MIT © Contributors
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'time'
4
+ require 'json'
5
+ require 'find'
4
6
 
5
7
  module PrettyGit
6
8
  module Analytics
@@ -79,30 +81,46 @@ module PrettyGit
79
81
  # Default metric: bytes (similar to GitHub Linguist approach).
80
82
  # rubocop:disable Metrics/ClassLength
81
83
  class Languages
84
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
82
85
  def self.call(_enum, filters)
83
86
  repo = filters.repo_path
84
- items = calculate(repo, include_globs: filters.paths, exclude_globs: filters.exclude_paths)
87
+ prof = ENV['PG_PROF'] == '1'
88
+ t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC) if prof
85
89
  metric = (filters.metric || 'bytes').to_s
90
+ items = calculate(repo, include_globs: filters.paths, exclude_globs: filters.exclude_paths, metric: metric)
86
91
  totals = compute_totals(items)
87
92
  items = add_percents(items, totals, metric)
88
93
  items = add_colors(items)
89
94
  items = sort_and_limit(items, filters.limit, metric)
90
95
 
91
- build_result(repo, items, totals, metric)
96
+ res = build_result(repo, items, totals, metric)
97
+ if prof
98
+ t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
99
+ elapsed = (t1 - t0)
100
+ files = totals[:files]
101
+ warn format(
102
+ '[pg_prof] languages: time=%<sec>.3fs files=%<files>d metric=%<metric>s',
103
+ { sec: elapsed, files: files, metric: metric }
104
+ )
105
+ summary = { component: 'languages', time_sec: elapsed, files: files, metric: metric }
106
+ warn("[pg_prof_json] #{summary.to_json}")
107
+ end
108
+ res
92
109
  end
110
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
93
111
 
94
112
  # rubocop:disable Metrics/AbcSize
95
- def self.calculate(repo_path, include_globs:, exclude_globs:)
113
+ def self.calculate(repo_path, include_globs:, exclude_globs:, metric: 'bytes')
96
114
  by_lang = Hash.new { |h, k| h[k] = { bytes: 0, files: 0, loc: 0 } }
97
115
  Dir.chdir(repo_path) do
98
- each_source_file(include_globs, exclude_globs) do |abs_path|
99
- basename = File.basename(abs_path)
100
- ext = File.extname(abs_path).downcase
116
+ each_source_file(include_globs, exclude_globs) do |path|
117
+ basename = File.basename(path)
118
+ ext = File.extname(path).downcase
101
119
  lang = FILENAME_TO_LANG[basename] || EXT_TO_LANG[ext]
102
120
  next unless lang
103
121
 
104
- size = safe_file_size(abs_path)
105
- lines = safe_count_lines(abs_path)
122
+ size = safe_file_size(path)
123
+ lines = metric == 'loc' ? safe_count_lines(path) : 0
106
124
  agg = by_lang[lang]
107
125
  agg[:bytes] += size
108
126
  agg[:files] += 1
@@ -113,14 +131,33 @@ module PrettyGit
113
131
  end
114
132
  # rubocop:enable Metrics/AbcSize
115
133
 
116
- def self.each_source_file(include_globs, exclude_globs)
117
- # Build list of files under repo respecting includes/excludes
118
- all = Dir.glob('**/*', File::FNM_DOTMATCH).select { |p| File.file?(p) }
119
- files = all.reject { |p| vendor_path?(p) || binary_ext?(p) }
134
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
135
+ def self.each_source_file(include_globs, exclude_globs, &block)
136
+ # Traverse tree with early prune for vendor/binary paths, then apply include/exclude
137
+ files = []
138
+ Find.find('.') do |path|
139
+ rel = path.sub(%r{^\./}, '')
140
+ # Prune vendor dirs early
141
+ if File.directory?(path)
142
+ dir = File.basename(path)
143
+ if VENDOR_DIRS.include?(dir)
144
+ Find.prune
145
+ next
146
+ end
147
+ next
148
+ end
149
+ next unless File.file?(path)
150
+ next if rel.empty?
151
+ next if vendor_path?(rel) || binary_ext?(rel)
152
+
153
+ files << rel
154
+ end
155
+
120
156
  files = filter_includes(files, include_globs)
121
157
  files = filter_excludes(files, exclude_globs)
122
- files.each { |rel| yield File.expand_path(rel) }
158
+ files.each { |rel| block.call(rel) }
123
159
  end
160
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
124
161
 
125
162
  def self.safe_file_size(path)
126
163
  File.size(path)
@@ -141,7 +178,8 @@ module PrettyGit
141
178
  return files if globs.empty?
142
179
 
143
180
  allowed = globs.flat_map { |g| Dir.glob(g) }
144
- files.select { |f| allowed.include?(f) }
181
+ allowed_map = allowed.each_with_object({}) { |f, h| h[f] = true }
182
+ files.select { |f| allowed_map[f] }
145
183
  end
146
184
 
147
185
  def self.filter_excludes(files, globs)
@@ -149,7 +187,8 @@ module PrettyGit
149
187
  return files if globs.empty?
150
188
 
151
189
  blocked = globs.flat_map { |g| Dir.glob(g) }
152
- files.reject { |f| blocked.include?(f) }
190
+ blocked_map = blocked.each_with_object({}) { |f, h| h[f] = true }
191
+ files.reject { |f| blocked_map[f] }
153
192
  end
154
193
 
155
194
  def self.vendor_path?(path)