pretty-git 0.1.1 → 0.1.2

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: 1c00f6c3da7b91643351da8e2af40157ce5bc7e32948df975e93f08b0f87f8d2
4
- data.tar.gz: c2e42b369d5c1bbda73dc33ae13bd35893619b572f26f6c50f8bdff4abd35f35
3
+ metadata.gz: 2200709c51eb32592b4fe79a3acea35178fab498dbd781edcc4237ceabfa00c7
4
+ data.tar.gz: c83bdd9cb01c5b345dfa52f0f1d35a03ed167a78307a6a085819453d96c88293
5
5
  SHA512:
6
- metadata.gz: 149db95a428adbffe290dc4b1ef72351375d3cb7130cf59c0f9c98a467a6863874e5ae440120ef448cfa70938cb1274c91e999d568266c255d5985962fd04e25
7
- data.tar.gz: 6c420e0e7144c3325610b26e81e7c30f37851b49443f55f70c6bf6808f0e6ee83b5ff3440f9bb077dc104f036298a44086147bfde4205d5a38b9abd8826784d7
6
+ metadata.gz: 1d23439b48405024896bec5e173a8573ce0b8fb1b8d1872b98d0201d4cceb85bd07f246f0d2e0a406d1c9f25d40f903280536b32e8dd030dfb04f801bc617d32
7
+ data.tar.gz: 3137c6d5b19ecbc496f14c9db9395a598d7e7bb8f33f5863db6eb8f3bf9141db141bd2429b8346101a53d79b4a4d687a4b430ad252e1f99bc58f1f2ea0bc6ac4
data/CHANGELOG.md ADDED
@@ -0,0 +1,51 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
6
+
7
+ ## [Unreleased]
8
+
9
+
10
+ ## [0.1.2] - 2025-08-13
11
+ ### Added
12
+ - Languages report: support multiple metrics — `bytes`, `files`, `loc`; dynamic columns in Console/CSV/Markdown; color and percent fields in output.
13
+ - CLI: `--metric` option for the `languages` report with value validation.
14
+
15
+ ### Changed
16
+ - 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.
17
+ - Renderers: updated `csv`, `markdown`, and console renderers to work with dynamic metrics.
18
+ - Internal specs updated: `specs/output_formats.md`, `specs/cli_spec.md`, `specs/languages_map.md`.
19
+
20
+ ### Fixed
21
+ - 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`).
22
+ - RuboCop: targeted suppressions for complex methods/classes and style fixes in `cli_helpers.rb`.
23
+
24
+ ## [0.1.1] - 2025-08-13
25
+ ### Changed
26
+ - Release automation: added GitHub Actions workflow to publish gem on tags and open PR to Homebrew tap (`.github/workflows/release.yml`).
27
+ - Documentation: README badges and installation instructions for Homebrew and RubyGems in `README.md` and `README.ru.md`.
28
+ - Gemspec: bounded runtime dependencies for `csv` and `rexml` to satisfy RubyGems recommendations.
29
+
30
+ ### Fixed
31
+ - Homebrew formula installation stability: formula installs gem into `libexec/vendor` and wraps `pretty-git` binary to avoid file collisions on reinstall.
32
+
33
+ ## [0.1.0] - 2025-08-13
34
+ ### Added
35
+ - Languages report: bytes per language, percentages, sorting, limit.
36
+ - Console: colorized languages section; terminal width handling via `TerminalWidth`.
37
+ - Export: languages in Markdown/CSV/JSON/YAML/XML.
38
+ - CLI: `languages` report wired into App and renderers.
39
+ - Tests: analytics/languages specs (aggregation, globs, filename detection, limit).
40
+ - Docs: English primary `README.md` + `README.ru.md` with language switcher.
41
+ - Docs: Ignored directories and binary extensions list.
42
+ - Docs: Added screenshot `PrettyGitConsoleLanguages.png`.
43
+
44
+ ### Changed
45
+ - Analytics: exclude JSON from language mapping by default to avoid data skew.
46
+ - Analytics: ignore Python env/cache directories by default.
47
+ - Refactor: `ConsoleRenderer` split (LanguagesSection, TerminalWidth); reduced complexity.
48
+ - App: extracted `analytics_for` from `App#run`.
49
+
50
+ ### Fixed
51
+ - RuboCop violations in new specs and minor guard clause spacing.
data/README.md CHANGED
@@ -61,19 +61,19 @@ Generator of rich reports for a local Git repository: summary, activity, authors
61
61
 
62
62
  ## Installation
63
63
 
64
- ### Homebrew (recommended)
64
+ ### 🍺 Homebrew (recommended)
65
65
  ```bash
66
66
  brew tap MikoMikocchi/tap
67
67
  brew install pretty-git
68
68
  ```
69
69
 
70
- ### RubyGems
70
+ ### ♦️ RubyGems
71
71
  ```bash
72
72
  gem install pretty-git
73
73
  ```
74
74
  Choose one:
75
75
 
76
- 1) From source (recommended for development)
76
+ 1) 🛠️ From source (recommended for development)
77
77
 
78
78
  ```bash
79
79
  git clone <repo_url>
@@ -83,14 +83,14 @@ bin/setup
83
83
  bundle exec bin/pretty-git --help
84
84
  ```
85
85
 
86
- 2) As a gem (after the first release)
86
+ 2) ♦️ As a gem (after the first release)
87
87
 
88
88
  ```bash
89
89
  gem install pretty-git
90
90
  pretty-git --version
91
91
  ```
92
92
 
93
- 3) Via Bundler
93
+ 3) 📦 Via Bundler
94
94
 
95
95
  ```ruby
96
96
  # Gemfile
@@ -134,6 +134,7 @@ Key options:
134
134
  * **--path/--exclude-path** Filter by paths (comma-separated or repeated option)
135
135
  * **--no-color** Disable colors in console
136
136
  * **--theme** `basic|bright|mono` — console theme (default `basic`; `mono` forces monochrome)
137
+ * **--metric** `bytes|files|loc` — metric for `languages` report (default `bytes`)
137
138
 
138
139
  Examples with multiple values:
139
140
 
@@ -234,7 +235,7 @@ dow,hour,commits
234
235
  ```bash
235
236
  pretty-git languages . --format md --limit 10
236
237
  ```
237
- Determines language distribution in a repository by summing file bytes per language (similar to GitHub Linguist). Output includes language, total size (bytes) and percent share.
238
+ Determines language distribution in a repository. Supports multiple metrics: `bytes`, `files`, `loc` (default: `bytes`). Output includes language, selected metric column, and percent share; console shows language colors.
238
239
 
239
240
  Console example:
240
241
  ```text
@@ -252,7 +253,7 @@ Markdown 1200 1.7
252
253
  Notes:
253
254
  - **Detection**: by file extensions and certain filenames (`Makefile`, `Dockerfile`).
254
255
  - **Exclusions**: binary files and "vendor"-like directories are ignored. By default `vendor/`, `node_modules/`, `.git/`, build artifacts and caches are skipped. For Python projects additional directories are skipped: `.venv/`, `venv/`, `env/`, `__pycache__/`, `.mypy_cache/`, `.pytest_cache/`, `.tox/`, `.eggs/`, `.ruff_cache/`, `.ipynb_checkpoints/`.
255
- - **JSON**: JSON is not counted as a language by default to avoid data files skewing statistics.
256
+ - **JSON**: JSON is included as a language. If large data files skew results, consider narrowing with `--path/--exclude-path`.
256
257
  - **Path filters**: use `--path/--exclude-path` (glob patterns supported) to focus on relevant areas.
257
258
  - **Limit**: `--limit N` restricts number of rows; `0`/`all` — no limit.
258
259
  - **Console colors**: language names use approximate GitHub colors; `--no-color` disables, `--theme mono` makes output monochrome.
@@ -260,8 +261,8 @@ Notes:
260
261
  See also: [Ignored directories and files](#ignored-directories-and-files).
261
262
 
262
263
  Export:
263
- - CSV/MD: columns — `language,bytes,percent`.
264
- - JSON/YAML/XML: full report structure including metadata (`report`, `generated_at`, `repo_path`).
264
+ - CSV/MD: columns are dynamic — `language,<metric>,percent`. Markdown also includes a `color` column.
265
+ - JSON/YAML/XML: full report structure including per-language `color` and metadata (`report`, `generated_at`, `repo_path`).
265
266
 
266
267
  ## Exports
267
268
 
@@ -291,7 +292,7 @@ _Example terminal output (theme: basic)._
291
292
  {"report":"summary","generated_at":"2025-01-31T00:00:00Z","totals":{"commits":123}}
292
293
  ```
293
294
 
294
- ### CSV (DR-001)
295
+ ### CSV
295
296
  * **Structure**: flat table, first line is header.
296
297
  * **Encoding**: UTF‑8 without BOM.
297
298
  * **Delimiter**: comma `,`.
data/README.ru.md ADDED
@@ -0,0 +1,427 @@
1
+ # Pretty Git
2
+
3
+ [![CI](https://github.com/MikoMikocchi/pretty-git/actions/workflows/ci.yml/badge.svg)](https://github.com/MikoMikocchi/pretty-git/actions/workflows/ci.yml)
4
+ [![Gem Version](https://img.shields.io/gem/v/pretty-git)](https://rubygems.org/gems/pretty-git)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
+ ![Ruby 3.4+](https://img.shields.io/badge/ruby-3.4%2B-red)
7
+
8
+ <p align="right">
9
+ <a href="./README.md">English</a> | <b>Русский</b>
10
+ </p>
11
+
12
+ <p align="center">
13
+ <img src="docs/images/PrettyGitIcon.png" alt="Pretty Git Logo" width="200">
14
+ <br>
15
+ </p>
16
+
17
+ Генератор отчётов по локальному Git-репозиторию: сводка, активность, авторы, файлы, тепловая карта. Вывод в консоль и форматы: JSON, CSV, Markdown, YAML, XML.
18
+
19
+ — Лицензия: MIT.
20
+
21
+ ## Содержание
22
+ - [Возможности](#возможности)
23
+ - [Требования](#требования)
24
+ - [Установка](#установка)
25
+ - [Быстрый старт](#быстрый-старт)
26
+ - [CLI и параметры](#cli-и-параметры)
27
+ - [Фильтры](#фильтры)
28
+ - [Формат вывода](#формат-вывода)
29
+ - [Вывод в файл](#вывод-в-файл)
30
+ - [Коды возврата](#коды-возврата)
31
+ - [Отчёты и примеры](#отчёты-и-примеры)
32
+ - [summary — сводка](#summary--сводка)
33
+ - [activity — активность (day/week/month)](#activity--активность-dayweekmonth)
34
+ - [authors — по авторам](#authors--по-авторам)
35
+ - [files — по файлам](#files--по-файлам)
36
+ - [heatmap — тепловая карта](#heatmap--тепловая-карта)
37
+ - [languages — языки](#languages--языки)
38
+ - [Экспорт в форматы](#экспорт-в-форматы)
39
+ - [Console](#console)
40
+ - [JSON](#json)
41
+ - [CSV (DR-001)](#csv-dr-001)
42
+ - [Markdown](#markdown)
43
+ - [YAML](#yaml)
44
+ - [XML](#xml)
45
+ - [Детерминизм и сортировка](#детерминизм-и-сортировка)
46
+ - [Советы по Windows](#советы-по-windows)
47
+ - [Диагностика и ошибки](#диагностика-и-ошибки)
48
+ - [FAQ](#faq)
49
+ - [Разработка](#разработка)
50
+ - [Лицензия](#лицензия)
51
+
52
+ ## Возможности
53
+ * __Отчёты__: `summary`, `activity`, `authors`, `files`, `heatmap`, `languages`.
54
+ * __Фильтры__: ветки, авторы, пути, период времени.
55
+ * __Экспорт__: `console`, `json`, `csv`, `md`, `yaml`, `xml`.
56
+ * __Вывод__: в stdout или файл через `--out`.
57
+
58
+ ## Требования
59
+ * __Ruby__: >= 3.4 (рекомендуется 3.4.x)
60
+ * __Git__: установлен и доступен в `PATH`
61
+
62
+ ## Установка
63
+
64
+ ### 🍺 Homebrew (рекомендуется)
65
+ ```bash
66
+ brew tap MikoMikocchi/tap
67
+ brew install pretty-git
68
+ ```
69
+
70
+ ### ♦️ RubyGems
71
+ ```bash
72
+ gem install pretty-git
73
+ ```
74
+ Выберите один из вариантов:
75
+
76
+ 1) 🛠️ Локально из исходников (рекомендуется для разработки)
77
+
78
+ ```bash
79
+ git clone <repo_url>
80
+ cd pretty-git
81
+ bin/setup
82
+ # запуск:
83
+ bundle exec bin/pretty-git --help
84
+ ```
85
+
86
+ 2) ♦️ Как gem (после первого релиза)
87
+
88
+ ```bash
89
+ gem install pretty-git
90
+ pretty-git --version
91
+ ```
92
+
93
+ 3) 📦 Использование через Bundler
94
+
95
+ ```ruby
96
+ # Gemfile
97
+ gem 'pretty-git', '~> 0.1'
98
+ ```
99
+ ```bash
100
+ bundle install
101
+ bundle exec pretty-git --help
102
+ ```
103
+
104
+ ## Быстрый старт
105
+ ```bash
106
+ # Сводка репозитория в консоль
107
+ bundle exec bin/pretty-git summary .
108
+
109
+ # Авторы в JSON и запись в файл
110
+ bundle exec bin/pretty-git authors . --format json --out authors.json
111
+
112
+ # Активность по неделям за период и только по указанным путям
113
+ bundle exec bin/pretty-git activity . --time-bucket week --since 2025-01-01 \
114
+ --paths app,lib --format csv --out activity.csv
115
+ ```
116
+
117
+ ## CLI и параметры
118
+ Общий вид:
119
+
120
+ ```bash
121
+ pretty-git <report> <repo_path> [options]
122
+ ```
123
+
124
+ Доступные отчёты: `summary`, `activity`, `authors`, `files`, `heatmap`, `languages`.
125
+
126
+ Ключевые опции:
127
+ * __--format, -f__ `console|json|csv|md|yaml|xml` (по умолчанию `console`)
128
+ * __--out, -o__ Путь для записи в файл
129
+ * __--limit, -l__ Число элементов в топах/выводе; `all` или `0` — без ограничения
130
+ * __--time-bucket__ `day|week|month` (для `activity`)
131
+ * __--since/--until__ Дата/время в ISO8601 или `YYYY-MM-DD` (DR-005)
132
+ * __--branch__ Мульти-опция, можно указывать несколько веток
133
+ * __--author/--exclude-author__ Фильтрация по авторам
134
+ * __--path/--exclude-path__ Фильтрация по путям (через запятую или повтор опции)
135
+ * __--no-color__ Отключить цвета в консоли
136
+ * __--theme__ `basic|bright|mono` — тема оформления консольного вывода (по умолчанию `basic`; `mono` принудительно отключает цвета)
137
+ * __--metric__ `bytes|files|loc` — метрика для отчёта `languages` (по умолчанию `bytes`)
138
+
139
+ Примеры значений с несколькими параметрами:
140
+
141
+ ```bash
142
+ # Несколько веток
143
+ pretty-git summary . --branch main --branch develop
144
+
145
+ # Фильтрация по авторам (включая/исключая)
146
+ pretty-git authors . --author alice@example.com --exclude-author bot@company
147
+
148
+ # Фильтрация по путям
149
+ pretty-git files . --path app,lib --exclude-path vendor,node_modules
150
+ ```
151
+
152
+ ### Фильтры
153
+ Фильтры применяются на этапе выборки коммитов и последующей агрегации. Формат дат: ISO8601 или `YYYY-MM-DD`. Если часовой пояс не указан — используется локальная зона пользователя; на выводе время нормализуется к UTC.
154
+
155
+ ### Формат вывода
156
+ Задаётся через `--format`. Для файловых форматов рекомендуется использовать `--out`.
157
+
158
+ ### Вывод в файл
159
+ ```bash
160
+ pretty-git authors . --format csv --out authors.csv
161
+ ```
162
+
163
+ ### Коды возврата
164
+ * `0` — успех
165
+ * `1` — ошибка пользователя (неизвестный отчёт/формат, неверные аргументы)
166
+ * `2` — системная ошибка (ошибка Git и пр.)
167
+
168
+ ## Отчёты и примеры
169
+
170
+ ### summary — сводка
171
+ ```bash
172
+ pretty-git summary . --format json
173
+ ```
174
+ Содержит totals (commits, authors, additions, deletions) и топы по авторам/файлам.
175
+
176
+ ### activity — активность (day/week/month)
177
+ ```bash
178
+ pretty-git activity . --time-bucket week --format csv
179
+ ```
180
+ CSV-колонки: `bucket,timestamp,commits,additions,deletions`.
181
+ Пример JSON:
182
+ ```json
183
+ [
184
+ {"bucket":"week","timestamp":"2025-06-02T00:00:00Z","commits":120,"additions":3456,"deletions":2100},
185
+ {"bucket":"week","timestamp":"2025-06-09T00:00:00Z","commits":98,"additions":2890,"deletions":1760}
186
+ ]
187
+ ```
188
+
189
+ ### authors — по авторам
190
+ ```bash
191
+ pretty-git authors . --format md --limit 10
192
+ ```
193
+ CSV-колонки: `author,author_email,commits,additions,deletions,avg_commit_size`.
194
+ Пример Markdown:
195
+ ```markdown
196
+ | author | author_email | commits | additions | deletions | avg_commit_size |
197
+ |---|---|---:|---:|---:|---:|
198
+ | Alice | a@example.com | 2 | 5 | 1 | 3.0 |
199
+ | Bob | b@example.com | 1 | 2 | 0 | 2.0 |
200
+ ```
201
+
202
+ ### files — по файлам
203
+ ```bash
204
+ pretty-git files . --paths app,lib --format csv
205
+ ```
206
+ CSV-колонки: `path,commits,additions,deletions,changes`.
207
+ Пример XML:
208
+ ```xml
209
+ <files>
210
+ <item path="app/models/user.rb" commits="42" additions="2100" deletions="1400" changes="3500" />
211
+ <item path="app/services/auth.rb" commits="35" additions="1500" deletions="900" changes="2400" />
212
+ <generated_at>2025-01-31T00:00:00Z</generated_at>
213
+ <repo_path>/abs/path/to/repo</repo_path>
214
+ <report>files</report>
215
+ <period>
216
+ <since/>
217
+ <until/>
218
+ </period>
219
+ </files>
220
+ ```
221
+
222
+ ### heatmap — тепловая карта
223
+ ```bash
224
+ pretty-git heatmap . --format json
225
+ ```
226
+ JSON: массив бакетов по (день недели × час) с числом коммитов.
227
+ Пример CSV:
228
+ ```csv
229
+ dow,hour,commits
230
+ 1,10,5
231
+ 1,11,7
232
+ ```
233
+
234
+ ### languages — языки
235
+ ```bash
236
+ pretty-git languages . --format md --limit 10
237
+ ```
238
+ Определяет распределение языков в репозитории. Поддерживаются несколько метрик: `bytes`, `files`, `loc` (по умолчанию: `bytes`). Вывод включает язык, выбранную метрику и долю в процентах; в консоли отображаются цвета языков.
239
+
240
+ Пример консольного вывода:
241
+ ```text
242
+ Languages for .
243
+
244
+ language bytes percent
245
+ -------- ---------- -------
246
+ Ruby 123456 60.0
247
+ JavaScript 78901 38.3
248
+ Markdown 1200 1.7
249
+ ```
250
+
251
+ ![Console output — languages](docs/images/PrettyGitConsoleLanguages.png)
252
+
253
+ Замечания:
254
+ - __Определение языка__: по расширениям файлов и некоторым именам файлов (`Makefile`, `Dockerfile`).
255
+ - __Исключения__: бинарные файлы и "vendor"‑директории игнорируются. По умолчанию пропускаются `vendor/`, `node_modules/`, `.git/`, артефакты сборки и кэши. Для Python дополнительно исключаются `.venv/`, `venv/`, `env/`, `__pycache__/`, `.mypy_cache/`, `.pytest_cache/`, `.tox/`, `.eggs/`, `.ruff_cache/`, `.ipynb_checkpoints/`.
256
+ - __JSON__: JSON включён как язык. Если крупные файлы данных искажают картину, сузьте область `--path/--exclude-path`.
257
+ - __Фильтры путей__: используйте `--path/--exclude-path` (поддерживаются glob‑маски), чтобы сосредоточиться на соответствующих директориях.
258
+ - __Лимит__: `--limit N` ограничивает число строк в итоговой таблице; `0`/`all` — без ограничения.
259
+ - __Цвета в консоли__: имена языков подсвечиваются приблизительными цветами GitHub; `--no-color` отключает, `--theme mono` делает монохром.
260
+
261
+ Экспорт:
262
+ - CSV/MD: колонки динамические — `language,<metric>,percent`. В Markdown дополнительно присутствует колонка `color`.
263
+ - JSON/YAML/XML: полная структура отчёта, включая `color` на язык и метаданные (`report`, `generated_at`, `repo_path`).
264
+
265
+ ## Игнорируемые директории и файлы
266
+
267
+ Чтобы статистика по языкам оставалась релевантной, некоторые директории и типы файлов пропускаются по умолчанию.
268
+
269
+ **Игнорируемые директории** (если сегмент пути совпадает):
270
+
271
+ ```
272
+ vendor, node_modules, .git, .bundle, dist, build, out, target, coverage,
273
+ .venv, venv, env, __pycache__, .mypy_cache, .pytest_cache, .tox, .eggs, .ruff_cache,
274
+ .ipynb_checkpoints
275
+ ```
276
+
277
+ **Игнорируемые бинарные/данные расширения**:
278
+
279
+ ```
280
+ .png, .jpg, .jpeg, .gif, .svg, .webp, .ico, .bmp,
281
+ .pdf, .zip, .tar, .gz, .tgz, .bz2, .7z, .rar,
282
+ .mp3, .ogg, .wav, .mp4, .mov, .avi, .mkv,
283
+ .woff, .woff2, .ttf, .otf, .eot,
284
+ .jar, .class, .dll, .so, .dylib,
285
+ .exe, .bin, .dat
286
+ ```
287
+
288
+ Списки соответствуют реализации в `lib/pretty_git/analytics/languages.rb` и могут изменяться.
289
+
290
+ ## Экспорт в форматы
291
+
292
+ Ниже — точные правила сериализации для каждого формата, чтобы обеспечивалась совместимость с популярными инструментами (Excel, BI, CI и т.п.).
293
+
294
+ ### Console
295
+ ![Console output — basic theme](docs/images/PrettyGitConsole.png)
296
+ _Пример вывода в терминале (тема: basic)._
297
+ * __Цвета__: заголовки и шапки таблиц подсвечены; суммы: `commits` — жёлтым, `+additions` — зелёным, `-deletions` — красным. `--no-color` полностью отключает раскраску.
298
+ * __Темы__: `--theme basic|bright|mono`. `bright` — более насыщенные заголовки, `mono` — монохром (аналогично `--no-color`).
299
+ * __Выделение максимума__: на числовых колонках максимальные значения подчёркиваются жирным для быстрого сканирования.
300
+ * __Ширина терминала__: при выводе таблиц автоматически учитывается ширина терминала. Если ширины не хватает, первый столбец аккуратно обрезается с многоточием `…`.
301
+ * __Кодировка__: UTF‑8, переводы строк LF.
302
+ * __Назначение__: человекочитаемый вывод в терминал.
303
+ * __Оформление__: таблицы с границами, авто-обрезка длинных значений.
304
+ * __Цвета__: включены, если есть TTY; отключаются `--no-color`.
305
+ * __Пустые наборы__: печатается `No data`.
306
+ * __Кодировка/переводы строк__: UTF‑8, LF (`\n`).
307
+
308
+ ### JSON
309
+ * __Ключи__: `snake_case`.
310
+ * __Числа__: целые/вещественные без локализации (точка как разделитель).
311
+ * __Булевы__: `true/false`; __null__: `null`.
312
+ * __Даты/время__: ISO8601 в UTC, например `2025-01-31T00:00:00Z`.
313
+ * __Порядок__: поля в объектах стабильно упорядочены логически (например, `report`, `generated_at`, `repo_path`, затем данные).
314
+ * __Кодировка/переводы строк__: UTF‑8, LF.
315
+ * __Рекомендуемое расширение__: `.json`.
316
+ * __Пример__:
317
+ ```json
318
+ {"report":"summary","generated_at":"2025-01-31T00:00:00Z","totals":{"commits":123}}
319
+ ```
320
+
321
+ ### CSV
322
+ * __Структура__: плоская таблица, первая строка — заголовок.
323
+ * __Кодировка__: UTF‑8, без BOM.
324
+ * __Разделитель__: запятая `,`.
325
+ * __Экранирование__: по RFC 4180 — поля с запятыми/кавычками/переводами строк заключаются в двойные кавычки, двойные кавычки внутри поля удваиваются.
326
+ * __Пустые значения__: пустая ячейка (не `null`).
327
+ * __Числа__: без тысячных разделителей, десятичная точка.
328
+ * __Даты/время__: ISO8601 UTC.
329
+ * __Порядок колонок__: фиксирован на отчёт и стабилен.
330
+ * __Переводы строк__: LF.
331
+ * __Рекомендуемое расширение__: `.csv`.
332
+ * __Открытие в Excel__: указывайте кодировку UTF‑8 при импорте.
333
+ * __Пример__:
334
+ ```csv
335
+ author,author_email,commits,additions,deletions,avg_commit_size
336
+ Alice,a@example.com,2,5,1,3.0
337
+ Bob,b@example.com,1,2,0,2.0
338
+ ```
339
+
340
+ ### Markdown
341
+ * __Таблицы__: стандартный синтаксис GitHub Flavored Markdown.
342
+ * __Выравнивание__: числовые колонки выравниваются по правому краю (`---:`).
343
+ * __Кодировка/переводы строк__: UTF‑8, LF.
344
+ * __Рекомендуемое расширение__: `.md`.
345
+ * __Пустые наборы__: таблица с заголовком и без строк либо краткое сообщение `No data` (в зависимости от отчёта).
346
+ * __Пример__:
347
+ ```markdown
348
+ | path | commits | additions | deletions |
349
+ |---|---:|---:|---:|
350
+ | app/models/user.rb | 42 | 2100 | 1400 |
351
+ ```
352
+
353
+ ### YAML
354
+ * __Структура__: полная иерархия результата.
355
+ * __Ключи__: сериализуются строками.
356
+ * __Числа/булевы/null__: стандарт YAML (`123`, `true/false`, `null`).
357
+ * __Даты/время__: ISO8601 UTC как строки.
358
+ * __Кодировка/переводы строк__: UTF‑8, LF.
359
+ * __Рекомендуемое расширение__: `.yml` или `.yaml`.
360
+ * __Пример__:
361
+ ```yaml
362
+ report: authors
363
+ generated_at: "2025-01-31T00:00:00Z"
364
+ items:
365
+ - author: Alice
366
+ author_email: a@example.com
367
+ commits: 2
368
+ - author: Bob
369
+ author_email: b@example.com
370
+ commits: 1
371
+ ```
372
+
373
+ ### XML
374
+ * __Структура__: элементы соответствуют ключам; массивы — повторяющиеся `<item>` или специализированные теги.
375
+ * __Атрибуты__: для компактных записей (например, строк файлового отчёта) основные поля могут быть атрибутами элемента.
376
+ * __Текстовые узлы__: применяются для скалярных значений при необходимости.
377
+ * __Экранирование__: `& < > " ' ` по правилам XML, при необходимости CDATA для произвольного текста.
378
+ * __Даты/время__: ISO8601 UTC.
379
+ * __Кодировка/переводы строк__: UTF‑8, LF; декларация `<?xml version="1.0" encoding="UTF-8"?>` может добавляться генератором.
380
+ * __Рекомендуемое расширение__: `.xml`.
381
+ * __Пример__:
382
+ ```xml
383
+ <authors>
384
+ <item author="Alice" author_email="a@example.com" commits="2" />
385
+ <item author="Bob" author_email="b@example.com" commits="1" />
386
+ <generated_at>2025-01-31T00:00:00Z</generated_at>
387
+ <repo_path>/abs/path</repo_path>
388
+ </authors>
389
+ ```
390
+
391
+ ## Детерминизм и сортировка
392
+ Вывод детерминирован при одинаковых входных данных. Сортировка для файлов/авторов: по количеству изменений (desc), затем по числу коммитов (desc), затем по пути/имени (asc). Лимиты применяются поверх отсортированного списка; значение `all` или `0` означает отсутствие ограничения.
393
+
394
+ ## Советы по Windows
395
+ Целевая платформа — macOS/Linux. Windows поддерживается в режиме best‑effort:
396
+ * Запуск через Git Bash/WSL допустим
397
+ * Цвета можно отключить `--no-color`
398
+ * Аккуратное квотирование аргументов при работе с путями
399
+
400
+ ## Диагностика и ошибки
401
+ Типичные ошибки и решения:
402
+
403
+ * __Неизвестный отчёт/формат__ — проверьте значение первого аргумента и `--format`.
404
+ * __Неверный формат даты__ — используйте ISO8601 или `YYYY-MM-DD` (например, `2025-01-31` или `2025-01-31T12:00:00Z`).
405
+ * __Git недоступен__ — убедитесь, что `git` установлен и доступен в `PATH`.
406
+ * __Пустой результат__ — проверьте фильтры (`--since/--until`, `--branch`, `--path`), возможно, выборка слишком узкая.
407
+ * __Проблемы с кодировкой CSV__ — файлы сохраняются в UTF‑8, при открытии в Excel выбирайте UTF‑8.
408
+
409
+ ## FAQ
410
+ * __Почему Ruby 3.4+?__ Проект использует зависимости, согласованные с версией 3.4+, и ориентируется на актуальную экосистему.
411
+ * __Можно ли добавить новые форматы?__ Да, добавьте рендерер в `lib/pretty_git/render/` и зарегистрируйте его в приложении.
412
+ * __Откуда берутся данные?__ Из системного `git` через вызовы CLI.
413
+
414
+ ## Разработка
415
+ ```bash
416
+ # Установка зависимостей
417
+ bin/setup
418
+
419
+ # Запуск тестов и линтера
420
+ bundle exec rspec
421
+ bundle exec rubocop
422
+ ```
423
+
424
+ Стиль — RuboCop без ошибок. Тесты покрывают агрегаторы, рендереры, CLI и интеграционные сценарии (детерминизм, корректность форматов).
425
+
426
+ ## Лицензия
427
+ MIT © Contributors
@@ -15,7 +15,7 @@ module PrettyGit
15
15
  '.sh' => 'Shell', '.bash' => 'Shell', '.zsh' => 'Shell',
16
16
  '.ps1' => 'PowerShell', '.psm1' => 'PowerShell',
17
17
  '.bat' => 'Batchfile', '.cmd' => 'Batchfile',
18
- # Intentionally excluding JSON (usually data, not source code)
18
+ '.json' => 'JSON',
19
19
  '.yml' => 'YAML', '.yaml' => 'YAML', '.toml' => 'TOML', '.ini' => 'INI', '.xml' => 'XML',
20
20
  '.html' => 'HTML', '.htm' => 'HTML', '.css' => 'CSS', '.scss' => 'SCSS', '.sass' => 'SCSS',
21
21
  '.md' => 'Markdown', '.markdown' => 'Markdown',
@@ -43,6 +43,25 @@ module PrettyGit
43
43
  'Dockerfile' => 'Dockerfile'
44
44
  }.freeze
45
45
 
46
+ # Language → HEX color (without leading #) for exports (CSV/JSON/YAML/XML)
47
+ LANG_HEX_COLORS = {
48
+ 'Ruby' => 'cc342d',
49
+ 'JavaScript' => 'f1e05a', 'TypeScript' => '3178c6',
50
+ 'JSX' => 'f1e05a', 'TSX' => '3178c6',
51
+ 'Python' => '3572a5', 'Go' => '00add8', 'Rust' => 'dea584', 'Java' => 'b07219',
52
+ 'C' => '555555', 'C++' => 'f34b7d', 'C#' => '178600', 'Objective-C' => '438eff', 'Swift' => 'ffac45',
53
+ 'Kotlin' => 'a97bff', 'Scala' => 'c22d40', 'Groovy' => 'e69f56', 'Dart' => '00b4ab',
54
+ 'PHP' => '4f5d95', 'Perl' => '0298c3', 'R' => '198ce7', 'Lua' => '000080', 'Haskell' => '5e5086',
55
+ 'Elixir' => '6e4a7e', 'Erlang' => 'b83998',
56
+ 'Shell' => '89e051', 'PowerShell' => '012456', 'Batchfile' => 'c1f12e',
57
+ 'HTML' => 'e34c26', 'CSS' => '563d7c', 'SCSS' => 'c6538c',
58
+ 'JSON' => 'eeeeee',
59
+ 'YAML' => 'cb171e', 'TOML' => '9c4221', 'INI' => '6b7280', 'XML' => '0060ac',
60
+ 'Markdown' => '083fa1', 'Makefile' => '427819', 'Dockerfile' => '384d54',
61
+ 'SQL' => 'e38c00', 'GraphQL' => 'e10098', 'Proto' => '3b5998',
62
+ 'Svelte' => 'ff3e00', 'Vue' => '41b883'
63
+ }.freeze
64
+
46
65
  VENDOR_DIRS = %w[
47
66
  vendor node_modules .git .bundle dist build out target coverage
48
67
  .venv venv env __pycache__ .mypy_cache .pytest_cache .tox .eggs .ruff_cache
@@ -56,21 +75,25 @@ module PrettyGit
56
75
  .jar .class .dll .so .dylib
57
76
  .exe .bin .dat
58
77
  ].freeze
59
- # Computes language distribution by summing file sizes per language.
60
- # Similar to GitHub Linguist approach (bytes per language).
78
+ # Computes language distribution by bytes, files, and LOC per language.
79
+ # Default metric: bytes (similar to GitHub Linguist approach).
80
+ # rubocop:disable Metrics/ClassLength
61
81
  class Languages
62
82
  def self.call(_enum, filters)
63
83
  repo = filters.repo_path
64
84
  items = calculate(repo, include_globs: filters.paths, exclude_globs: filters.exclude_paths)
65
- total = total_bytes(items)
66
- items = add_percents(items, total)
67
- items = sort_and_limit(items, filters.limit)
85
+ metric = (filters.metric || 'bytes').to_s
86
+ totals = compute_totals(items)
87
+ items = add_percents(items, totals, metric)
88
+ items = add_colors(items)
89
+ items = sort_and_limit(items, filters.limit, metric)
68
90
 
69
- build_result(repo, items, total)
91
+ build_result(repo, items, totals, metric)
70
92
  end
71
93
 
94
+ # rubocop:disable Metrics/AbcSize
72
95
  def self.calculate(repo_path, include_globs:, exclude_globs:)
73
- by_lang = Hash.new(0)
96
+ by_lang = Hash.new { |h, k| h[k] = { bytes: 0, files: 0, loc: 0 } }
74
97
  Dir.chdir(repo_path) do
75
98
  each_source_file(include_globs, exclude_globs) do |abs_path|
76
99
  basename = File.basename(abs_path)
@@ -78,16 +101,17 @@ module PrettyGit
78
101
  lang = FILENAME_TO_LANG[basename] || EXT_TO_LANG[ext]
79
102
  next unless lang
80
103
 
81
- size = begin
82
- File.size(abs_path)
83
- rescue StandardError
84
- 0
85
- end
86
- by_lang[lang] += size
104
+ size = safe_file_size(abs_path)
105
+ lines = safe_count_lines(abs_path)
106
+ agg = by_lang[lang]
107
+ agg[:bytes] += size
108
+ agg[:files] += 1
109
+ agg[:loc] += lines
87
110
  end
88
111
  end
89
- by_lang.map { |lang, bytes| { language: lang, bytes: bytes } }
112
+ by_lang.map { |lang, h| { language: lang, bytes: h[:bytes], files: h[:files], loc: h[:loc] } }
90
113
  end
114
+ # rubocop:enable Metrics/AbcSize
91
115
 
92
116
  def self.each_source_file(include_globs, exclude_globs)
93
117
  # Build list of files under repo respecting includes/excludes
@@ -98,6 +122,20 @@ module PrettyGit
98
122
  files.each { |rel| yield File.expand_path(rel) }
99
123
  end
100
124
 
125
+ def self.safe_file_size(path)
126
+ File.size(path)
127
+ rescue StandardError
128
+ 0
129
+ end
130
+
131
+ def self.safe_count_lines(path)
132
+ count = 0
133
+ File.foreach(path) { |_l| count += 1 }
134
+ count
135
+ rescue StandardError
136
+ 0
137
+ end
138
+
101
139
  def self.filter_includes(files, globs)
102
140
  globs = Array(globs).compact
103
141
  return files if globs.empty?
@@ -123,33 +161,52 @@ module PrettyGit
123
161
  BINARY_EXTS.include?(File.extname(path).downcase)
124
162
  end
125
163
 
126
- def self.total_bytes(items)
127
- items.sum { |item| item[:bytes] }
164
+ def self.compute_totals(items)
165
+ {
166
+ bytes: items.sum { |i| i[:bytes] },
167
+ files: items.sum { |i| i[:files] },
168
+ loc: items.sum { |i| i[:loc] }
169
+ }
128
170
  end
129
171
 
130
- def self.add_percents(items, total)
172
+ def self.add_percents(items, totals, metric)
173
+ total = totals[metric.to_sym].to_f
131
174
  return items.map { |item| item.merge(percent: 0.0) } unless total.positive?
132
175
 
133
- items.map { |item| item.merge(percent: (item[:bytes] * 100.0 / total)) }
176
+ items.map do |item|
177
+ val = item[metric.to_sym].to_f
178
+ pct = (val * 100.0 / total).round(2)
179
+ item.merge(percent: pct)
180
+ end
181
+ end
182
+
183
+ def self.add_colors(items)
184
+ items.map do |item|
185
+ color = LANG_HEX_COLORS[item[:language]]
186
+ item.merge(color: color)
187
+ end
134
188
  end
135
189
 
136
- def self.sort_and_limit(items, limit)
137
- sorted = items.sort_by { |item| [-item[:percent], item[:language]] }
190
+ def self.sort_and_limit(items, limit, metric)
191
+ key = metric.to_sym
192
+ sorted = items.sort_by { |item| [-item[key], item[:language]] }
138
193
  lim = limit.to_i
139
194
  return sorted if lim <= 0
140
195
 
141
196
  sorted.first(lim)
142
197
  end
143
198
 
144
- def self.build_result(repo, items, total)
199
+ def self.build_result(repo, items, totals, metric)
145
200
  {
146
201
  report: 'languages',
147
202
  repo_path: repo,
203
+ metric: metric,
148
204
  generated_at: Time.now.utc.iso8601,
149
- totals: { bytes: total, languages: items.size },
205
+ totals: totals.merge(languages: items.size),
150
206
  items: items
151
207
  }
152
208
  end
153
209
  end
210
+ # rubocop:enable Metrics/ClassLength
154
211
  end
155
212
  end
@@ -9,7 +9,7 @@ require_relative 'cli_helpers'
9
9
  module PrettyGit
10
10
  # Command-line interface entry point.
11
11
  class CLI
12
- SUPPORTED_REPORTS = %w[summary activity authors files heatmap].freeze
12
+ SUPPORTED_REPORTS = %w[summary activity authors files heatmap languages].freeze
13
13
  SUPPORTED_FORMATS = %w[console json csv md yaml xml].freeze
14
14
 
15
15
  # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
@@ -7,9 +7,11 @@ require_relative 'app'
7
7
  module PrettyGit
8
8
  # Helpers extracted from `PrettyGit::CLI` to keep the CLI class small
9
9
  # and RuboCop-compliant. Provides parser configuration and execution utilities.
10
+ # rubocop:disable Metrics/ModuleLength
10
11
  module CLIHelpers
11
12
  REPORTS = %w[summary activity authors files heatmap languages].freeze
12
13
  FORMATS = %w[console json csv md yaml xml].freeze
14
+ METRICS = %w[bytes files loc].freeze
13
15
 
14
16
  module_function
15
17
 
@@ -19,6 +21,7 @@ module PrettyGit
19
21
  add_time_author_options(opts, options)
20
22
  add_path_limit_options(opts, options)
21
23
  add_format_output_options(opts, options)
24
+ add_metric_options(opts, options)
22
25
  add_misc_options(opts, options)
23
26
  end
24
27
 
@@ -48,6 +51,12 @@ module PrettyGit
48
51
  opts.on('--theme NAME', 'console color theme: basic|bright|mono') { |val| options[:theme] = val }
49
52
  end
50
53
 
54
+ def add_metric_options(opts, options)
55
+ opts.on('--metric NAME', 'languages metric: bytes|files|loc (default: bytes)') do |val|
56
+ options[:metric] = val
57
+ end
58
+ end
59
+
51
60
  def add_misc_options(opts, options)
52
61
  opts.on('--version', 'Show version') { options[:_version] = true }
53
62
  opts.on('--help', 'Show help') { options[:_help] = true }
@@ -66,7 +75,7 @@ module PrettyGit
66
75
  code = handle_version_help(options, parser, out)
67
76
  return code unless code.nil?
68
77
 
69
- return nil if valid_report?(options[:report]) && valid_theme?(options[:theme])
78
+ return nil if valid_report?(options[:report]) && valid_theme?(options[:theme]) && valid_metric?(options[:metric])
70
79
 
71
80
  print_validation_errors(options, err)
72
81
  1
@@ -87,15 +96,20 @@ module PrettyGit
87
96
  def valid_report?(report) = REPORTS.include?(report)
88
97
  def valid_theme?(theme) = %w[basic bright mono].include?(theme)
89
98
 
99
+ def valid_metric?(metric)
100
+ metric.nil? || METRICS.include?(metric)
101
+ end
102
+
90
103
  def print_validation_errors(options, err)
91
104
  supported = REPORTS.join(', ')
92
105
  unless valid_report?(options[:report])
93
106
  err.puts "Unknown report: #{options[:report]}."
94
107
  err.puts "Supported: #{supported}"
95
108
  end
96
- return if valid_theme?(options[:theme])
109
+ err.puts "Unknown theme: #{options[:theme]}. Supported: basic, bright, mono" unless valid_theme?(options[:theme])
110
+ return if valid_metric?(options[:metric])
97
111
 
98
- err.puts "Unknown theme: #{options[:theme]}. Supported: basic, bright, mono"
112
+ err.puts "Unknown metric: #{options[:metric]}. Supported: #{METRICS.join(', ')}"
99
113
  end
100
114
 
101
115
  def build_filters(options)
@@ -109,6 +123,7 @@ module PrettyGit
109
123
  paths: options[:paths],
110
124
  exclude_paths: options[:exclude_paths],
111
125
  time_bucket: options[:time_bucket],
126
+ metric: options[:metric],
112
127
  limit: options[:limit],
113
128
  format: options[:format],
114
129
  out: options[:out],
@@ -127,4 +142,5 @@ module PrettyGit
127
142
  PrettyGit::App.new.run(report, filters, out: out, err: err)
128
143
  end
129
144
  end
145
+ # rubocop:enable Metrics/ModuleLength
130
146
  end
@@ -13,6 +13,7 @@ module PrettyGit
13
13
  :paths,
14
14
  :exclude_paths,
15
15
  :time_bucket,
16
+ :metric,
16
17
  :limit,
17
18
  :format,
18
19
  :out,
@@ -16,7 +16,7 @@ module PrettyGit
16
16
  end
17
17
 
18
18
  # Returns Enumerator of PrettyGit::Types::Commit
19
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
19
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
20
20
  def each_commit
21
21
  Enumerator.new do |yld|
22
22
  cmd = build_git_command
@@ -24,17 +24,15 @@ module PrettyGit
24
24
  current = nil
25
25
  stdout.each_line do |line|
26
26
  line = line.chomp
27
- if record_separator?(line)
27
+ # Try to start a new commit from header on any line
28
+ header = start_commit_from_header(line)
29
+ if header
30
+ # emit previous commit if any
28
31
  emit_current(yld, current)
29
- current = nil
32
+ current = header
30
33
  next
31
34
  end
32
35
 
33
- if current.nil?
34
- current = start_commit_from_header(line)
35
- next if current
36
- end
37
-
38
36
  next if line.empty?
39
37
 
40
38
  append_numstat_line(current, line)
@@ -50,7 +48,7 @@ module PrettyGit
50
48
  end
51
49
  end
52
50
  end
53
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
51
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
54
52
 
55
53
  private
56
54
 
@@ -84,7 +82,7 @@ module PrettyGit
84
82
  author_name: author_name,
85
83
  author_email: author_email,
86
84
  authored_at: Time.parse(authored_at).utc.iso8601,
87
- message: subject,
85
+ message: subject.delete(SEP_RECORD),
88
86
  files: []
89
87
  }
90
88
  end
@@ -21,7 +21,9 @@ module PrettyGit
21
21
  when 'heatmap'
22
22
  write_csv(%w[dow hour commits], result[:items])
23
23
  when 'languages'
24
- write_csv(%w[language bytes percent], result[:items])
24
+ metric = (result[:metric] || 'bytes').to_s
25
+ headers = ['language', metric, 'percent', 'color']
26
+ write_csv(headers, result[:items])
25
27
  else
26
28
  raise ArgumentError, "CSV output for report '#{report}' is not supported yet"
27
29
  end
@@ -26,9 +26,11 @@ module PrettyGit
26
26
  def render(io, table, data, color: true)
27
27
  title(io, data, color)
28
28
  io.puts
29
- table_rows = rows(data[:items])
29
+ metric = (data[:metric] || 'bytes').to_s
30
+ table_rows = rows(data[:items], metric)
30
31
  colorizer = ->(row) { LANG_ANSI_COLOR_CODES[row[:language]] }
31
- table.print(%w[language bytes percent], table_rows, highlight_max: false, first_col_colorizer: colorizer)
32
+ headers = ['language', metric, 'percent']
33
+ table.print(headers, table_rows, highlight_max: false, first_col_colorizer: colorizer)
32
34
  io.puts
33
35
  io.puts "Generated at: #{data[:generated_at]}"
34
36
  end
@@ -37,9 +39,10 @@ module PrettyGit
37
39
  io.puts Colors.title("Languages for #{data[:repo_path]}", color)
38
40
  end
39
41
 
40
- def rows(items)
42
+ def rows(items, metric)
43
+ key = metric.to_sym
41
44
  items.map do |item|
42
- { language: item[:language], bytes: item[:bytes], percent: format('%.1f', item[:percent]) }
45
+ { language: item[:language], key => item[key], percent: format('%.2f', item[:percent]) }
43
46
  end
44
47
  end
45
48
  end
@@ -8,6 +8,7 @@ module PrettyGit
8
8
  @io = io
9
9
  end
10
10
 
11
+ # rubocop:disable Metrics/CyclomaticComplexity
11
12
  def call(report, result, _filters)
12
13
  case report
13
14
  when 'summary'
@@ -21,11 +22,14 @@ module PrettyGit
21
22
  when 'heatmap'
22
23
  render_table('Heatmap', %w[dow hour commits], result[:items])
23
24
  when 'languages'
24
- render_table('Languages', %w[language bytes percent], result[:items])
25
+ metric = (result[:metric] || 'bytes').to_s
26
+ headers = ['language', metric, 'percent', 'color']
27
+ render_table('Languages', headers, result[:items])
25
28
  else
26
29
  @io.puts result.inspect
27
30
  end
28
31
  end
32
+ # rubocop:enable Metrics/CyclomaticComplexity
29
33
 
30
34
  private
31
35
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PrettyGit
4
- VERSION = '0.1.1'
4
+ VERSION = '0.1.2'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pretty-git
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pretty Git Authors
@@ -58,8 +58,10 @@ executables:
58
58
  extensions: []
59
59
  extra_rdoc_files: []
60
60
  files:
61
+ - CHANGELOG.md
61
62
  - LICENSE
62
63
  - README.md
64
+ - README.ru.md
63
65
  - bin/pretty-git
64
66
  - lib/pretty_git.rb
65
67
  - lib/pretty_git/analytics/activity.rb