pretty-git 0.1.4 → 0.1.6
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 +84 -4
- data/README.md +78 -6
- data/README.ru.md +76 -3
- data/lib/pretty_git/analytics/languages.rb +61 -19
- data/lib/pretty_git/app.rb +22 -15
- data/lib/pretty_git/cli.rb +17 -6
- data/lib/pretty_git/cli_helpers.rb +148 -19
- data/lib/pretty_git/constants.rb +15 -0
- data/lib/pretty_git/filters.rb +41 -22
- data/lib/pretty_git/git/provider.rb +78 -15
- data/lib/pretty_git/logger.rb +20 -0
- data/lib/pretty_git/render/console_renderer.rb +25 -7
- data/lib/pretty_git/render/csv_renderer.rb +4 -15
- data/lib/pretty_git/render/markdown_renderer.rb +5 -15
- data/lib/pretty_git/render/report_schema.rb +39 -0
- 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 +6 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 00b27dd7b14328c3aa2e5d3bf865dd778d512131d3f3b1c7d9fe1ca76939c5e6
|
4
|
+
data.tar.gz: 36c9f80ac26f1982bf2a4b2f1616bf5d309dec2c9dcda12603e2e2422500e3a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8518117f9b018c89a31db647920d871fe68f1ba1a2ddb90e43f58ec08342c4d345393617144ba29705ef6a13f86c184123c72b99ee2de81ada45824df875593d
|
7
|
+
data.tar.gz: 59505cbed77cdf230c6b5c2960caa1bde851a4a17820d886b788b409a04f5b042cfe51e5a4e060471e90a303743369df335206f3fc51b97759c637086775abcf
|
data/CHANGELOG.md
CHANGED
@@ -5,66 +5,144 @@ 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
|
+
|
9
|
+
## [0.1.6] - 2025-10-10
|
10
|
+
|
11
|
+
### Security
|
12
|
+
|
13
|
+
- Git::Provider: added input validation for `--author` and `--branch` parameters to prevent command injection attacks
|
14
|
+
- Git::Provider: `validate_safe_string` method checks for shell metacharacters (`; & | \` $ ( ) < >`)
|
15
|
+
- Git::Provider: `validate_git_ref` method validates git reference format and rejects refs starting with `-`
|
16
|
+
|
17
|
+
### Added
|
18
|
+
|
19
|
+
- Render::ConsoleRenderer: named constants for ANSI color codes (`Colors::AnsiCodes` module)
|
20
|
+
- Render::ReportSchema: centralized module for report headers (eliminates duplication across CSV/Markdown renderers)
|
21
|
+
- CLI: verbose mode now includes full backtrace for `StandardError` exceptions (helps with debugging)
|
22
|
+
- Analytics::Languages: `safe_file_size` and `safe_count_lines` now log warnings to stderr when `verbose=true`
|
23
|
+
- Tests: 20+ negative test cases for input validation (`spec/pretty_git/git/provider_validation_spec.rb`)
|
24
|
+
- Tests: CLI error handling coverage (`spec/pretty_git/cli_error_handling_spec.rb`)
|
25
|
+
|
26
|
+
### Changed
|
27
|
+
|
28
|
+
- CLI: enhanced error handling with specific exit codes: `ArgumentError` → 1, file system errors (`ENOENT`, `EACCES`) → 2, other errors → 99
|
29
|
+
- CLI: improved error messages for `ArgumentError`, `Errno::ENOENT`, and `Errno::EACCES`
|
30
|
+
- Render::CsvRenderer: refactored to use `ReportSchema.headers_for` instead of local `HEADERS` constant
|
31
|
+
- Render::MarkdownRenderer: refactored to use `ReportSchema.headers_for` instead of local `HEADERS` constant
|
32
|
+
|
33
|
+
### Fixed
|
34
|
+
|
35
|
+
- Security: command injection vulnerabilities via `--author` and `--branch` parameters accepting unvalidated shell input
|
36
|
+
|
37
|
+
### Documentation
|
38
|
+
|
39
|
+
- Docs: `docs/testing.md` covering the golden workflow, snapshot update/validation; linked from `README.md`, `README.ru.md`, and `CONTRIBUTING.md`.
|
40
|
+
- Tests: determinism invariants for YAML/XML renderers (stable output regardless of input order).
|
41
|
+
- Scripts: `scripts/release.sh` automated release workflow with validation, testing, and git operations
|
42
|
+
|
43
|
+
### Refactoring
|
44
|
+
|
45
|
+
- Code quality: eliminated ~40 lines of duplicated `HEADERS` definitions across renderers
|
46
|
+
- Readability: replaced magic numbers (`'1;35'`, `'1;36'`) with descriptive constant names (`BRIGHT_TITLE`, `BASIC_TITLE`)
|
47
|
+
|
48
|
+
### Changed (from previous Unreleased)
|
49
|
+
|
50
|
+
- 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.
|
51
|
+
|
52
|
+
## [0.1.5] - 2025-08-17
|
53
|
+
|
54
|
+
### Added
|
55
|
+
|
56
|
+
- CLI: warn to stderr when `--theme`/`--no-color` are used with non-console `--format` values.
|
57
|
+
- Docs: expanded Filters documentation (branches, authors, paths, time semantics, verbose diagnostics), schemas/examples pointers, performance and CI usage.
|
58
|
+
- Tests: unit tests for `PrettyGit::Utils::TimeUtils`.
|
59
|
+
|
60
|
+
### Changed
|
61
|
+
|
62
|
+
- 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).
|
63
|
+
- Verbose mode: documentation clarified to note that diagnostics are printed to stderr for easier CI parsing.
|
64
|
+
|
65
|
+
### Deprecated
|
66
|
+
|
67
|
+
- Filters: legacy `:until` keyword in `PrettyGit::Filters` initialization is accepted for backward compatibility and emits a deprecation warning; use `:until_at` instead.
|
68
|
+
|
69
|
+
### Fixed
|
70
|
+
|
71
|
+
- Filters: allow initialization via a single Hash argument (legacy call sites) while preserving `Struct` keyword semantics.
|
9
72
|
|
10
73
|
## [0.1.4] - 2025-08-17
|
74
|
+
|
11
75
|
### Added
|
76
|
+
|
12
77
|
- Integration tests for new reports exports: CSV/Markdown/YAML/XML for `hotspots`, `churn`, `ownership`.
|
13
78
|
- Schema validations: `rake validate:json`, `rake validate:xml` to ensure format compatibility.
|
14
79
|
- CI: expanded matrix to include macOS; smoke test for installed binary (`--help`, `--version`).
|
15
80
|
|
16
81
|
### Changed
|
82
|
+
|
17
83
|
- Renderers (`MarkdownRenderer`, `YamlRenderer`, `XmlRenderer`): deterministic sorting for all new reports according to `docs/determinism.md`.
|
18
84
|
- XML: per-report root elements in XML exports to match XSDs (`hotspotsReport`, `churnReport`, `ownershipReport`, `languagesReport`, etc.).
|
19
85
|
- Documentation: `README.md` and `README.ru.md` updated with sections and examples for new reports and all export formats.
|
20
86
|
- CLI: keep `--time-bucket` permissive; default `time_bucket=nil`.
|
21
87
|
|
22
88
|
### Fixed
|
89
|
+
|
23
90
|
- Time parsing: interpret date-only inputs (`YYYY-MM-DD`) as UTC midnight and normalize to UTC ISO8601.
|
24
91
|
- CLI UX: error when `--metric` is used outside `languages` report.
|
25
92
|
- Tests/specs: updated XML specs to per-report roots; added timezone edge cases; fixed Open3 `popen3` stubs (`chdir:`) and integration requires.
|
26
93
|
|
27
|
-
|
28
94
|
## [0.1.3] - 2025-08-14
|
95
|
+
|
29
96
|
### Added
|
97
|
+
|
30
98
|
- New analytics reports: `hotspots`, `churn`, `ownership` with sorting, scoring, and limits.
|
31
99
|
- Exporters: CSV and Markdown support for new reports with dynamic headers via mapping constants.
|
32
100
|
- Docs: Detailed sections for new reports in `README.md` and `README.ru.md` with usage and examples (CSV/JSON/YAML/XML).
|
33
101
|
|
34
102
|
### Changed
|
103
|
+
|
35
104
|
- Console: dispatching and rendering wired for new reports; consistent theming and width handling.
|
36
105
|
- CLI/App: unified analytics dispatch for all reports.
|
37
|
-
- Docs: public READMEs cleaned up from internal DR
|
106
|
+
- Docs: public READMEs cleaned up from internal DR-\* mentions; anchors and headings aligned (CSV).
|
38
107
|
|
39
108
|
### Fixed
|
40
|
-
- Minor documentation inaccuracies and anchor mismatches.
|
41
109
|
|
110
|
+
- Minor documentation inaccuracies and anchor mismatches.
|
42
111
|
|
43
112
|
## [0.1.2] - 2025-08-13
|
113
|
+
|
44
114
|
### Added
|
115
|
+
|
45
116
|
- Languages report: support multiple metrics — `bytes`, `files`, `loc`; dynamic columns in Console/CSV/Markdown; color and percent fields in output.
|
46
117
|
- CLI: `--metric` option for the `languages` report with value validation.
|
47
118
|
|
48
119
|
### Changed
|
120
|
+
|
49
121
|
- 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.
|
50
122
|
- Renderers: updated `csv`, `markdown`, and console renderers to work with dynamic metrics.
|
51
123
|
- Internal specs updated: `docs/output_formats.md`, `docs/cli_spec.md`, `docs/languages_map.md`.
|
52
124
|
|
53
125
|
### Fixed
|
126
|
+
|
54
127
|
- 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`).
|
55
128
|
- RuboCop: targeted suppressions for complex methods/classes and style fixes in `cli_helpers.rb`.
|
56
129
|
|
57
130
|
## [0.1.1] - 2025-08-13
|
131
|
+
|
58
132
|
### Changed
|
133
|
+
|
59
134
|
- Release automation: added GitHub Actions workflow to publish gem on tags and open PR to Homebrew tap (`.github/workflows/release.yml`).
|
60
135
|
- Documentation: README badges and installation instructions for Homebrew and RubyGems in `README.md` and `README.ru.md`.
|
61
136
|
- Gemspec: bounded runtime dependencies for `csv` and `rexml` to satisfy RubyGems recommendations.
|
62
137
|
|
63
138
|
### Fixed
|
139
|
+
|
64
140
|
- Homebrew formula installation stability: formula installs gem into `libexec/vendor` and wraps `pretty-git` binary to avoid file collisions on reinstall.
|
65
141
|
|
66
142
|
## [0.1.0] - 2025-08-13
|
143
|
+
|
67
144
|
### Added
|
145
|
+
|
68
146
|
- Languages report: bytes per language, percentages, sorting, limit.
|
69
147
|
- Console: colorized languages section; terminal width handling via `TerminalWidth`.
|
70
148
|
- Export: languages in Markdown/CSV/JSON/YAML/XML.
|
@@ -75,10 +153,12 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve
|
|
75
153
|
- Docs: Added screenshot `PrettyGitConsoleLanguages.png`.
|
76
154
|
|
77
155
|
### Changed
|
156
|
+
|
78
157
|
- Analytics: exclude JSON from language mapping by default to avoid data skew.
|
79
158
|
- Analytics: ignore Python env/cache directories by default.
|
80
159
|
- Refactor: `ConsoleRenderer` split (LanguagesSection, TerminalWidth); reduced complexity.
|
81
160
|
- App: extracted `analytics_for` from `App#run`.
|
82
161
|
|
83
162
|
### Fixed
|
163
|
+
|
84
164
|
- RuboCop violations in new specs and minor guard clause spacing.
|
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`
|
@@ -142,12 +149,15 @@ Key options:
|
|
142
149
|
* **--no-color** Disable colors in console
|
143
150
|
* **--theme** `basic|bright|mono` — console theme (default `basic`; `mono` forces monochrome)
|
144
151
|
* **--metric** `bytes|files|loc` — metric for `languages` report (default `bytes`)
|
152
|
+
* **--verbose** Print debug information (effective git command, filters)
|
145
153
|
|
146
154
|
Examples with multiple values:
|
147
155
|
|
148
156
|
```bash
|
149
|
-
# Multiple branches
|
157
|
+
# Multiple branches (treated as explicit revisions)
|
150
158
|
pretty-git summary . --branch main --branch develop
|
159
|
+
## This is equivalent to:
|
160
|
+
## git log main develop -- ...
|
151
161
|
|
152
162
|
# Filter authors (include/exclude)
|
153
163
|
pretty-git authors . --author alice@example.com --exclude-author bot@company
|
@@ -157,7 +167,30 @@ pretty-git files . --path app,lib --exclude-path vendor,node_modules
|
|
157
167
|
```
|
158
168
|
|
159
169
|
### Filters
|
160
|
-
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.
|
161
194
|
|
162
195
|
### Output format
|
163
196
|
Set via `--format`. For file formats it’s recommended to use `--out`.
|
@@ -397,6 +430,13 @@ _Example terminal output (theme: basic)._
|
|
397
430
|
{"report":"summary","generated_at":"2025-01-31T00:00:00Z","totals":{"commits":123}}
|
398
431
|
```
|
399
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
|
+
|
400
440
|
### CSV
|
401
441
|
* **Structure**: flat table, first line is header.
|
402
442
|
* **Encoding**: UTF‑8 without BOM.
|
@@ -495,16 +535,46 @@ These lists mirror the implementation in `lib/pretty_git/analytics/languages.rb`
|
|
495
535
|
## 🔁 Determinism and Sorting
|
496
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.
|
497
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
|
+
|
498
564
|
## 🪟 Windows Notes
|
499
|
-
Primary targets — macOS/Linux. Windows is supported best‑effort
|
500
|
-
|
501
|
-
|
502
|
-
*
|
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`.
|
503
572
|
|
504
573
|
## 🩺 Diagnostics and Errors
|
505
574
|
Typical issues and solutions:
|
506
575
|
|
507
576
|
* **Unknown report/format** — check the first argument and `--format`.
|
577
|
+
* **Debugging** — add `--verbose` to see the effective `git log` command and applied filters.
|
508
578
|
* **Invalid date format** — use ISO8601 or `YYYY-MM-DD` (e.g., `2025-01-31` or `2025-01-31T12:00:00Z`).
|
509
579
|
* **Git not available** — ensure `git` is installed and in the `PATH`.
|
510
580
|
* **Empty result** — check your filters (`--since/--until`, `--branch`, `--path`); your selection might be too narrow.
|
@@ -527,5 +597,7 @@ bundle exec rubocop
|
|
527
597
|
|
528
598
|
Style — RuboCop clean. Tests cover aggregators, renderers, CLI, and integration scenarios (determinism, format correctness).
|
529
599
|
|
600
|
+
For detailed testing strategy, determinism rules, and golden tests workflow (how to run/update snapshots), see `docs/testing.md`.
|
601
|
+
|
530
602
|
## 📄 License
|
531
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`
|
@@ -142,12 +153,15 @@ pretty-git <report> <repo_path> [options]
|
|
142
153
|
* __--no-color__ Отключить цвета в консоли
|
143
154
|
* __--theme__ `basic|bright|mono` — тема оформления консольного вывода (по умолчанию `basic`; `mono` принудительно отключает цвета)
|
144
155
|
* __--metric__ `bytes|files|loc` — метрика для отчёта `languages` (по умолчанию `bytes`)
|
156
|
+
* **--verbose** Печатать отладочную информацию (эффективная команда git, применённые фильтры)
|
145
157
|
|
146
158
|
Примеры значений с несколькими параметрами:
|
147
159
|
|
148
160
|
```bash
|
149
|
-
# Несколько веток
|
161
|
+
# Несколько веток (трактуются как явные ревизии)
|
150
162
|
pretty-git summary . --branch main --branch develop
|
163
|
+
## Эквивалентно:
|
164
|
+
## git log main develop -- ...
|
151
165
|
|
152
166
|
# Фильтрация по авторам (включая/исключая)
|
153
167
|
pretty-git authors . --author alice@example.com --exclude-author bot@company
|
@@ -157,7 +171,30 @@ pretty-git files . --path app,lib --exclude-path vendor,node_modules
|
|
157
171
|
```
|
158
172
|
|
159
173
|
### Фильтры
|
160
|
-
Фильтры применяются на этапе выборки коммитов и последующей агрегации.
|
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 и воспроизведения результатов локально.
|
161
198
|
|
162
199
|
### Формат вывода
|
163
200
|
Задаётся через `--format`. Для файловых форматов рекомендуется использовать `--out`.
|
@@ -423,6 +460,13 @@ _Пример вывода в терминале (тема: basic)._
|
|
423
460
|
{"report":"summary","generated_at":"2025-01-31T00:00:00Z","totals":{"commits":123}}
|
424
461
|
```
|
425
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
|
+
|
426
470
|
### CSV
|
427
471
|
* __Структура__: плоская таблица, первая строка — заголовок.
|
428
472
|
* __Кодировка__: UTF‑8, без BOM.
|
@@ -496,6 +540,32 @@ _Пример вывода в терминале (тема: basic)._
|
|
496
540
|
## 🔁 Детерминизм и сортировка
|
497
541
|
Вывод детерминирован при одинаковых входных данных. Сортировка для файлов/авторов: по количеству изменений (desc), затем по числу коммитов (desc), затем по пути/имени (asc). Лимиты применяются поверх отсортированного списка; значение `all` или `0` означает отсутствие ограничения.
|
498
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
|
+
|
499
569
|
## 🪟 Советы по Windows
|
500
570
|
Целевая платформа — macOS/Linux. Windows поддерживается в режиме best‑effort:
|
501
571
|
* Запуск через Git Bash/WSL допустим
|
@@ -505,7 +575,8 @@ _Пример вывода в терминале (тема: basic)._
|
|
505
575
|
## 🩺 Диагностика и ошибки
|
506
576
|
Типичные ошибки и решения:
|
507
577
|
|
508
|
-
*
|
578
|
+
* **Неизвестный отчёт/формат** — проверьте первый аргумент и `--format`.
|
579
|
+
* **Отладка** — добавьте `--verbose`, чтобы увидеть фактическую команду `git log` и применённые фильтры.
|
509
580
|
* __Неверный формат даты__ — используйте ISO8601 или `YYYY-MM-DD` (например, `2025-01-31` или `2025-01-31T12:00:00Z`).
|
510
581
|
* __Git недоступен__ — убедитесь, что `git` установлен и доступен в `PATH`.
|
511
582
|
* __Пустой результат__ — проверьте фильтры (`--since/--until`, `--branch`, `--path`), возможно, выборка слишком узкая.
|
@@ -528,5 +599,7 @@ bundle exec rubocop
|
|
528
599
|
|
529
600
|
Стиль — RuboCop без ошибок. Тесты покрывают агрегаторы, рендереры, CLI и интеграционные сценарии (детерминизм, корректность форматов).
|
530
601
|
|
602
|
+
Подробнее о стратегии тестирования, правилах детерминизма и процессе работы с golden‑тестами (как запускать/обновлять снапшоты) см. `docs/testing.md`.
|
603
|
+
|
531
604
|
## 📄 Лицензия
|
532
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,47 @@ 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 } }
|
115
|
+
verbose = ENV['PG_VERBOSE'] == '1'
|
97
116
|
Dir.chdir(repo_path) do
|
98
|
-
each_source_file(include_globs, exclude_globs) do |
|
99
|
-
basename = File.basename(
|
100
|
-
ext = File.extname(
|
117
|
+
each_source_file(include_globs, exclude_globs) do |path|
|
118
|
+
basename = File.basename(path)
|
119
|
+
ext = File.extname(path).downcase
|
101
120
|
lang = FILENAME_TO_LANG[basename] || EXT_TO_LANG[ext]
|
102
121
|
next unless lang
|
103
122
|
|
104
|
-
size = safe_file_size(
|
105
|
-
lines = safe_count_lines(
|
123
|
+
size = safe_file_size(path, verbose: verbose)
|
124
|
+
lines = metric == 'loc' ? safe_count_lines(path, verbose: verbose) : 0
|
106
125
|
agg = by_lang[lang]
|
107
126
|
agg[:bytes] += size
|
108
127
|
agg[:files] += 1
|
@@ -113,26 +132,47 @@ module PrettyGit
|
|
113
132
|
end
|
114
133
|
# rubocop:enable Metrics/AbcSize
|
115
134
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
files =
|
135
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
136
|
+
def self.each_source_file(include_globs, exclude_globs, &block)
|
137
|
+
# Traverse tree with early prune for vendor/binary paths, then apply include/exclude
|
138
|
+
files = []
|
139
|
+
Find.find('.') do |path|
|
140
|
+
rel = path.sub(%r{^\./}, '')
|
141
|
+
# Prune vendor dirs early
|
142
|
+
if File.directory?(path)
|
143
|
+
dir = File.basename(path)
|
144
|
+
if VENDOR_DIRS.include?(dir)
|
145
|
+
Find.prune
|
146
|
+
next
|
147
|
+
end
|
148
|
+
next
|
149
|
+
end
|
150
|
+
next unless File.file?(path)
|
151
|
+
next if rel.empty?
|
152
|
+
next if vendor_path?(rel) || binary_ext?(rel)
|
153
|
+
|
154
|
+
files << rel
|
155
|
+
end
|
156
|
+
|
120
157
|
files = filter_includes(files, include_globs)
|
121
158
|
files = filter_excludes(files, exclude_globs)
|
122
|
-
files.each { |rel|
|
159
|
+
files.each { |rel| block.call(rel) }
|
123
160
|
end
|
161
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
124
162
|
|
125
|
-
def self.safe_file_size(path)
|
163
|
+
def self.safe_file_size(path, verbose: false)
|
126
164
|
File.size(path)
|
127
|
-
rescue StandardError
|
165
|
+
rescue StandardError => e
|
166
|
+
warn("Warning: failed to read size of #{path}: #{e.message}") if verbose
|
128
167
|
0
|
129
168
|
end
|
130
169
|
|
131
|
-
def self.safe_count_lines(path)
|
170
|
+
def self.safe_count_lines(path, verbose: false)
|
132
171
|
count = 0
|
133
172
|
File.foreach(path) { |_l| count += 1 }
|
134
173
|
count
|
135
|
-
rescue StandardError
|
174
|
+
rescue StandardError => e
|
175
|
+
warn("Warning: failed to count lines in #{path}: #{e.message}") if verbose
|
136
176
|
0
|
137
177
|
end
|
138
178
|
|
@@ -141,7 +181,8 @@ module PrettyGit
|
|
141
181
|
return files if globs.empty?
|
142
182
|
|
143
183
|
allowed = globs.flat_map { |g| Dir.glob(g) }
|
144
|
-
|
184
|
+
allowed_map = allowed.each_with_object({}) { |f, h| h[f] = true }
|
185
|
+
files.select { |f| allowed_map[f] }
|
145
186
|
end
|
146
187
|
|
147
188
|
def self.filter_excludes(files, globs)
|
@@ -149,7 +190,8 @@ module PrettyGit
|
|
149
190
|
return files if globs.empty?
|
150
191
|
|
151
192
|
blocked = globs.flat_map { |g| Dir.glob(g) }
|
152
|
-
|
193
|
+
blocked_map = blocked.each_with_object({}) { |f, h| h[f] = true }
|
194
|
+
files.reject { |f| blocked_map[f] }
|
153
195
|
end
|
154
196
|
|
155
197
|
def self.vendor_path?(path)
|
data/lib/pretty_git/app.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'open3'
|
3
4
|
require_relative 'git/provider'
|
4
5
|
require_relative 'analytics/summary'
|
5
6
|
require_relative 'analytics/activity'
|
@@ -37,7 +38,9 @@ module PrettyGit
|
|
37
38
|
private
|
38
39
|
|
39
40
|
def ensure_repo!(path)
|
40
|
-
|
41
|
+
# Use git to reliably detect work-trees/worktrees/bare repos
|
42
|
+
stdout, _stderr, status = Open3.capture3('git', 'rev-parse', '--is-inside-work-tree', chdir: path)
|
43
|
+
return if status.success? && stdout.to_s.strip == 'true'
|
41
44
|
|
42
45
|
raise ArgumentError, "Not a git repository: #{path}"
|
43
46
|
end
|
@@ -47,21 +50,25 @@ module PrettyGit
|
|
47
50
|
end
|
48
51
|
|
49
52
|
def renderer_for(filters, io)
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
when 'md'
|
57
|
-
Render::MarkdownRenderer.new(io: io)
|
58
|
-
when 'yaml'
|
59
|
-
Render::YamlRenderer.new(io: io)
|
60
|
-
when 'xml'
|
61
|
-
Render::XmlRenderer.new(io: io)
|
62
|
-
else
|
63
|
-
Render::JsonRenderer.new(io: io)
|
53
|
+
if filters.format == 'console'
|
54
|
+
return Render::ConsoleRenderer.new(
|
55
|
+
io: io,
|
56
|
+
color: !filters.no_color && filters.theme != 'mono',
|
57
|
+
theme: filters.theme
|
58
|
+
)
|
64
59
|
end
|
60
|
+
|
61
|
+
dispatch = {
|
62
|
+
'csv' => Render::CsvRenderer,
|
63
|
+
'md' => Render::MarkdownRenderer,
|
64
|
+
'yaml' => Render::YamlRenderer,
|
65
|
+
'xml' => Render::XmlRenderer,
|
66
|
+
'json' => Render::JsonRenderer
|
67
|
+
}
|
68
|
+
klass = dispatch[filters.format]
|
69
|
+
raise ArgumentError, "Unknown format: #{filters.format}" unless klass
|
70
|
+
|
71
|
+
klass.new(io: io)
|
65
72
|
end
|
66
73
|
|
67
74
|
def analytics_for(report, enum, filters)
|