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 +4 -4
- data/CHANGELOG.md +41 -2
- data/README.md +112 -19
- data/README.ru.md +110 -16
- data/lib/pretty_git/analytics/languages.rb +54 -15
- data/lib/pretty_git/app.rb +22 -15
- data/lib/pretty_git/cli.rb +7 -6
- data/lib/pretty_git/cli_helpers.rb +157 -18
- data/lib/pretty_git/constants.rb +15 -0
- data/lib/pretty_git/filters.rb +41 -13
- data/lib/pretty_git/git/provider.rb +67 -10
- data/lib/pretty_git/logger.rb +20 -0
- data/lib/pretty_git/render/csv_renderer.rb +1 -1
- data/lib/pretty_git/render/markdown_renderer.rb +51 -2
- data/lib/pretty_git/render/xml_renderer.rb +71 -3
- data/lib/pretty_git/render/yaml_renderer.rb +58 -2
- data/lib/pretty_git/utils/path_utils.rb +30 -0
- data/lib/pretty_git/utils/time_utils.rb +39 -0
- data/lib/pretty_git/version.rb +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7bd982eee724a56adcb2aa941fdecc2c95309c1bf681a5c3b9a1be0b8bf0306f
|
4
|
+
data.tar.gz: 1ef12cd144493563bbe99a869e81fe6aa9436136dc7aac52b713d5436975a48a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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: `
|
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.
|
170
|
+
Filters apply at commit fetch and later aggregation. Below — exact 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
|
-
|
213
|
-
|
214
|
-
<
|
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
|
-
<
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
</
|
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
|
-
|
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
|
395
|
+
<item>
|
396
|
+
<path>lib/a.rb</path>
|
397
|
+
<owner>Alice <a@example.com></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
|
-
|
480
|
-
|
481
|
-
*
|
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
|
-
Фильтры применяются на этапе выборки коммитов и последующей агрегации.
|
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
|
-
|
213
|
-
|
214
|
-
<
|
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
|
-
<
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
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
|
-
|
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
|
397
|
+
<item>
|
398
|
+
<path>lib/a.rb</path>
|
399
|
+
<owner>Alice <a@example.com></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
|
-
*
|
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
|
-
|
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 |
|
99
|
-
basename = File.basename(
|
100
|
-
ext = File.extname(
|
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(
|
105
|
-
lines = safe_count_lines(
|
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
|
-
|
117
|
-
|
118
|
-
|
119
|
-
files =
|
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|
|
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
|
-
|
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
|
-
|
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)
|