git-pkgs 0.1.1 → 0.3.0
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 +22 -1
- data/README.md +123 -10
- data/lib/git/pkgs/cli.rb +9 -3
- data/lib/git/pkgs/color.rb +82 -0
- data/lib/git/pkgs/commands/blame.rb +23 -20
- data/lib/git/pkgs/commands/branch.rb +11 -35
- data/lib/git/pkgs/commands/diff.rb +27 -35
- data/lib/git/pkgs/commands/history.rb +51 -14
- data/lib/git/pkgs/commands/hooks.rb +2 -0
- data/lib/git/pkgs/commands/info.rb +3 -5
- data/lib/git/pkgs/commands/init.rb +4 -5
- data/lib/git/pkgs/commands/list.rb +21 -18
- data/lib/git/pkgs/commands/log.rb +157 -0
- data/lib/git/pkgs/commands/schema.rb +161 -0
- data/lib/git/pkgs/commands/search.rb +10 -11
- data/lib/git/pkgs/commands/show.rb +160 -0
- data/lib/git/pkgs/commands/{outdated.rb → stale.rb} +16 -13
- data/lib/git/pkgs/commands/stats.rb +121 -20
- data/lib/git/pkgs/commands/tree.rb +13 -10
- data/lib/git/pkgs/commands/update.rb +55 -58
- data/lib/git/pkgs/commands/upgrade.rb +54 -0
- data/lib/git/pkgs/commands/why.rb +5 -10
- data/lib/git/pkgs/database.rb +55 -1
- data/lib/git/pkgs/output.rb +44 -0
- data/lib/git/pkgs/pager.rb +68 -0
- data/lib/git/pkgs/repository.rb +3 -3
- data/lib/git/pkgs/version.rb +1 -1
- data/lib/git/pkgs.rb +7 -1
- metadata +9 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6152060237bbc1d9902da8e721bb23cfeaed46bbe2f5241e848aa0853d906710
|
|
4
|
+
data.tar.gz: 5eebb36bd3eed487a100ddc8d3ef4c88cb0cdb28734f777d6fbfbd267f87f43e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b2192d675dd23a3a2c69fc4de998070db17ac05004842855b1fbd1d5b5b148d8063db93db3d84e4e0bbcf42f5d33a173180f641e3309241f7d83e04615c5ba9a
|
|
7
|
+
data.tar.gz: cd7cfbe811f2a809ef428452708b18d1842ef2898d4b78c55e6373773b50546b215ec2dc43591ed4252d20595973ad060581dba72cc1400145068f55294655c4
|
data/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,25 @@
|
|
|
1
|
-
## [
|
|
1
|
+
## [0.3.0] - 2026-01-03
|
|
2
|
+
|
|
3
|
+
- Pager support for long output (respects `GIT_PAGER`, `core.pager`, `PAGER`)
|
|
4
|
+
- `--no-pager` option for commands with long output
|
|
5
|
+
- Colored output (respects `NO_COLOR`, `color.ui`, `color.pkgs`)
|
|
6
|
+
- `GIT_DIR` and `GIT_PKGS_DB` environment variable support
|
|
7
|
+
- `git pkgs stats` now supports `--since` and `--until` date filters
|
|
8
|
+
- Consistent error handling across all commands (JSON errors when `--format=json`)
|
|
9
|
+
- `git pkgs update` now uses a transaction for atomicity and better performance
|
|
10
|
+
- Renamed `git pkgs outdated` to `git pkgs stale` (outdated remains as alias)
|
|
11
|
+
- `git pkgs log` command to list commits with dependency changes
|
|
12
|
+
- `git pkgs schema` command to output database schema in text, SQL, JSON, or markdown
|
|
13
|
+
- `git pkgs praise` alias for `blame`
|
|
14
|
+
- `git pkgs upgrade` command to handle schema upgrades after updating git-pkgs
|
|
15
|
+
- Schema version tracking with automatic detection of outdated databases
|
|
16
|
+
|
|
17
|
+
## [0.2.0] - 2026-01-02
|
|
18
|
+
|
|
19
|
+
- `git pkgs show` command to display dependency changes in a single commit
|
|
20
|
+
- `git pkgs history` now supports `--author`, `--since`, and `--until` filters
|
|
21
|
+
- `git pkgs stats --by-author` shows who added the most dependencies
|
|
22
|
+
- `git pkgs stats --ecosystem=X` filters statistics by ecosystem
|
|
2
23
|
|
|
3
24
|
## [0.1.1] - 2026-01-01
|
|
4
25
|
|
data/README.md
CHANGED
|
@@ -4,11 +4,9 @@ A git subcommand for tracking package dependencies across git history. Analyzes
|
|
|
4
4
|
|
|
5
5
|
## Why this exists
|
|
6
6
|
|
|
7
|
-
Your lockfile shows what dependencies you have
|
|
7
|
+
Your lockfile shows what dependencies you have, but it doesn't show how you got here, and `git log Gemfile.lock` is useless noise. git-pkgs indexes your dependency history into a queryable database so you can ask questions like: when did we add this? who added it? what changed between these two releases? has anyone touched this in the last year?
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
It works across ecosystems. Gemfile, package.json, Dockerfile, GitHub Actions workflows - one unified history instead of separate tools per ecosystem.
|
|
9
|
+
It works across many ecosystems (Gemfile, package.json, Dockerfile, GitHub Actions workflows) giving you one unified history instead of separate tools per ecosystem. Everything runs locally and offline with no external services or network calls, and the database lives in your `.git` directory where you can use it in CI to catch dependency changes in pull requests.
|
|
12
10
|
|
|
13
11
|
## Installation
|
|
14
12
|
|
|
@@ -113,8 +111,11 @@ Gemfile (rubygems):
|
|
|
113
111
|
### View dependency history
|
|
114
112
|
|
|
115
113
|
```bash
|
|
116
|
-
git pkgs history
|
|
117
|
-
git pkgs history rails
|
|
114
|
+
git pkgs history # all dependency changes
|
|
115
|
+
git pkgs history rails # changes for a specific package
|
|
116
|
+
git pkgs history --author=alice # filter by author
|
|
117
|
+
git pkgs history --since=2024-01-01 # changes after date
|
|
118
|
+
git pkgs history --ecosystem=rubygems # filter by ecosystem
|
|
118
119
|
```
|
|
119
120
|
|
|
120
121
|
Shows when packages were added, updated, or removed:
|
|
@@ -145,6 +146,7 @@ Show who added each current dependency:
|
|
|
145
146
|
```bash
|
|
146
147
|
git pkgs blame
|
|
147
148
|
git pkgs blame --ecosystem=rubygems
|
|
149
|
+
git pkgs praise # alias for blame
|
|
148
150
|
```
|
|
149
151
|
|
|
150
152
|
Example output:
|
|
@@ -166,6 +168,10 @@ Gemfile (rubygems):
|
|
|
166
168
|
|
|
167
169
|
```bash
|
|
168
170
|
git pkgs stats
|
|
171
|
+
git pkgs stats --by-author # who added the most dependencies
|
|
172
|
+
git pkgs stats --ecosystem=npm # filter by ecosystem
|
|
173
|
+
git pkgs stats --since=2024-01-01 # changes after date
|
|
174
|
+
git pkgs stats --until=2024-12-31 # changes before date
|
|
169
175
|
```
|
|
170
176
|
|
|
171
177
|
Example output:
|
|
@@ -211,7 +217,7 @@ Manifest Files
|
|
|
211
217
|
git pkgs why rails
|
|
212
218
|
```
|
|
213
219
|
|
|
214
|
-
|
|
220
|
+
This shows the commit that added the dependency along with the author and message.
|
|
215
221
|
|
|
216
222
|
### Dependency tree
|
|
217
223
|
|
|
@@ -220,7 +226,18 @@ git pkgs tree
|
|
|
220
226
|
git pkgs tree --ecosystem=rubygems
|
|
221
227
|
```
|
|
222
228
|
|
|
223
|
-
|
|
229
|
+
This shows dependencies grouped by type (runtime, development, etc).
|
|
230
|
+
|
|
231
|
+
### Find stale dependencies
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
git pkgs stale # list deps by how long since last touched
|
|
235
|
+
git pkgs stale --days=365 # only show deps untouched for a year
|
|
236
|
+
git pkgs stale --ecosystem=npm # filter by ecosystem
|
|
237
|
+
git pkgs outdated # alias for stale
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Shows dependencies sorted by how long since they were last changed in your repo. Useful for finding packages that may have been forgotten or need review.
|
|
224
241
|
|
|
225
242
|
### Diff between commits
|
|
226
243
|
|
|
@@ -229,20 +246,101 @@ git pkgs diff --from=abc123 --to=def456
|
|
|
229
246
|
git pkgs diff --from=HEAD~10
|
|
230
247
|
```
|
|
231
248
|
|
|
232
|
-
|
|
249
|
+
This shows added, removed, and modified packages with version info.
|
|
250
|
+
|
|
251
|
+
### Show changes in a commit
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
git pkgs show # show dependency changes in HEAD
|
|
255
|
+
git pkgs show abc123 # specific commit
|
|
256
|
+
git pkgs show HEAD~5 # relative ref
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Like `git show` but for dependencies. Shows what was added, modified, or removed in a single commit.
|
|
260
|
+
|
|
261
|
+
### List commits with dependency changes
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
git pkgs log # recent commits with dependency changes
|
|
265
|
+
git pkgs log --author=alice # filter by author
|
|
266
|
+
git pkgs log -n 50 # show more commits
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Like `git log` but only shows commits that changed dependencies, with the changes listed under each commit.
|
|
233
270
|
|
|
234
271
|
### Keep database updated
|
|
235
272
|
|
|
273
|
+
After the initial analysis, you can incrementally update the database with new commits:
|
|
274
|
+
|
|
236
275
|
```bash
|
|
237
276
|
git pkgs update
|
|
238
277
|
```
|
|
239
278
|
|
|
240
|
-
|
|
279
|
+
You can also install git hooks to update automatically after commits and merges:
|
|
241
280
|
|
|
242
281
|
```bash
|
|
243
282
|
git pkgs hooks --install
|
|
244
283
|
```
|
|
245
284
|
|
|
285
|
+
### Upgrading
|
|
286
|
+
|
|
287
|
+
After updating git-pkgs, you may need to rebuild the database if the schema has changed:
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
git pkgs upgrade
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
This is detected automatically and you'll see a message if an upgrade is needed.
|
|
294
|
+
|
|
295
|
+
### Show database schema
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
git pkgs schema # human-readable table format
|
|
299
|
+
git pkgs schema --format=sql # CREATE TABLE statements
|
|
300
|
+
git pkgs schema --format=json # JSON structure
|
|
301
|
+
git pkgs schema --format=markdown # markdown tables
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Useful for understanding the [database structure](docs/schema.md) or generating documentation.
|
|
305
|
+
|
|
306
|
+
### CI usage
|
|
307
|
+
|
|
308
|
+
You can run git-pkgs in CI to show dependency changes in pull requests:
|
|
309
|
+
|
|
310
|
+
```yaml
|
|
311
|
+
# .github/workflows/deps.yml
|
|
312
|
+
name: Dependencies
|
|
313
|
+
|
|
314
|
+
on: pull_request
|
|
315
|
+
|
|
316
|
+
jobs:
|
|
317
|
+
diff:
|
|
318
|
+
runs-on: ubuntu-latest
|
|
319
|
+
steps:
|
|
320
|
+
- uses: actions/checkout@v4
|
|
321
|
+
with:
|
|
322
|
+
fetch-depth: 0
|
|
323
|
+
- uses: ruby/setup-ruby@v1
|
|
324
|
+
with:
|
|
325
|
+
ruby-version: '3.3'
|
|
326
|
+
- run: gem install git-pkgs
|
|
327
|
+
- run: git pkgs init
|
|
328
|
+
- run: git pkgs diff --from=origin/${{ github.base_ref }} --to=HEAD
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## Configuration
|
|
332
|
+
|
|
333
|
+
git-pkgs respects [standard git configuration](https://git-scm.com/docs/git-config).
|
|
334
|
+
|
|
335
|
+
**Colors** are enabled when writing to a terminal. Disable with `NO_COLOR=1`, `git config color.ui never`, or `git config color.pkgs never` for git-pkgs only.
|
|
336
|
+
|
|
337
|
+
**Pager** follows git's precedence: `GIT_PAGER` env, `core.pager` config, `PAGER` env, then `less -FRSX`. Use `--no-pager` flag or `git config core.pager cat` to disable.
|
|
338
|
+
|
|
339
|
+
**Environment variables:**
|
|
340
|
+
|
|
341
|
+
- `GIT_DIR` - git directory location (standard git variable)
|
|
342
|
+
- `GIT_PKGS_DB` - database path (default: `.git/pkgs.sqlite3`)
|
|
343
|
+
|
|
246
344
|
## Performance
|
|
247
345
|
|
|
248
346
|
Benchmarked on a MacBook Pro analyzing [octobox](https://github.com/octobox/octobox) (5191 commits, 8 years of history): init takes about 18 seconds at roughly 300 commits/sec, producing an 8.3 MB database. About half the commits (2531) had dependency changes.
|
|
@@ -271,6 +369,21 @@ The database schema stores:
|
|
|
271
369
|
|
|
272
370
|
See [docs/schema.md](docs/schema.md) for full schema documentation.
|
|
273
371
|
|
|
372
|
+
Since the database is just SQLite, you can query it directly for ad-hoc analysis:
|
|
373
|
+
|
|
374
|
+
```bash
|
|
375
|
+
sqlite3 .git/pkgs.sqlite3 "
|
|
376
|
+
-- who added the most dependencies?
|
|
377
|
+
SELECT c.author_name, COUNT(*) as deps_added
|
|
378
|
+
FROM dependency_changes dc
|
|
379
|
+
JOIN commits c ON dc.commit_id = c.id
|
|
380
|
+
WHERE dc.change_type = 'added'
|
|
381
|
+
GROUP BY c.author_name
|
|
382
|
+
ORDER BY deps_added DESC
|
|
383
|
+
LIMIT 10;
|
|
384
|
+
"
|
|
385
|
+
```
|
|
386
|
+
|
|
274
387
|
## Development
|
|
275
388
|
|
|
276
389
|
```bash
|
data/lib/git/pkgs/cli.rb
CHANGED
|
@@ -5,7 +5,8 @@ require "optparse"
|
|
|
5
5
|
module Git
|
|
6
6
|
module Pkgs
|
|
7
7
|
class CLI
|
|
8
|
-
COMMANDS = %w[init update hooks info list tree history search why blame
|
|
8
|
+
COMMANDS = %w[init update hooks info list tree history search why blame stale stats diff branch show log upgrade schema].freeze
|
|
9
|
+
ALIASES = { "praise" => "blame", "outdated" => "stale" }.freeze
|
|
9
10
|
|
|
10
11
|
def self.run(args)
|
|
11
12
|
new(args).run
|
|
@@ -24,7 +25,7 @@ module Git
|
|
|
24
25
|
print_help
|
|
25
26
|
when "-v", "--version", "version"
|
|
26
27
|
puts "git-pkgs #{Git::Pkgs::VERSION}"
|
|
27
|
-
when *COMMANDS
|
|
28
|
+
when *COMMANDS, *ALIASES.keys
|
|
28
29
|
run_command(command)
|
|
29
30
|
else
|
|
30
31
|
$stderr.puts "Unknown command: #{command}"
|
|
@@ -34,6 +35,7 @@ module Git
|
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
def run_command(command)
|
|
38
|
+
command = ALIASES.fetch(command, command)
|
|
37
39
|
command_class = Commands.const_get(command.capitalize.gsub(/_([a-z])/) { $1.upcase })
|
|
38
40
|
command_class.new(@args).run
|
|
39
41
|
rescue NameError
|
|
@@ -57,9 +59,13 @@ module Git
|
|
|
57
59
|
search Find a dependency across all history
|
|
58
60
|
why Explain why a dependency exists
|
|
59
61
|
blame Show who added each dependency
|
|
60
|
-
|
|
62
|
+
stale Show dependencies that haven't been updated
|
|
61
63
|
stats Show dependency statistics
|
|
62
64
|
diff Show dependency changes between commits
|
|
65
|
+
show Show dependency changes in a commit
|
|
66
|
+
log List commits with dependency changes
|
|
67
|
+
upgrade Upgrade database after git-pkgs update
|
|
68
|
+
schema Show database schema
|
|
63
69
|
|
|
64
70
|
Options:
|
|
65
71
|
-h, --help Show this help message
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Git
|
|
4
|
+
module Pkgs
|
|
5
|
+
module Color
|
|
6
|
+
CODES = {
|
|
7
|
+
red: 31,
|
|
8
|
+
green: 32,
|
|
9
|
+
yellow: 33,
|
|
10
|
+
blue: 34,
|
|
11
|
+
magenta: 35,
|
|
12
|
+
cyan: 36,
|
|
13
|
+
bold: 1,
|
|
14
|
+
dim: 2
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
def self.enabled?
|
|
18
|
+
return @enabled if defined?(@enabled)
|
|
19
|
+
|
|
20
|
+
@enabled = determine_color_support
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.enabled=(value)
|
|
24
|
+
@enabled = value
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.reset!
|
|
28
|
+
remove_instance_variable(:@enabled) if defined?(@enabled)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.determine_color_support
|
|
32
|
+
# NO_COLOR takes precedence (https://no-color.org/)
|
|
33
|
+
return false if ENV["NO_COLOR"] && !ENV["NO_COLOR"].empty?
|
|
34
|
+
return false if ENV["TERM"] == "dumb"
|
|
35
|
+
|
|
36
|
+
# Check git config: color.pkgs takes precedence over color.ui
|
|
37
|
+
git_color = git_color_config
|
|
38
|
+
case git_color
|
|
39
|
+
when "always" then return true
|
|
40
|
+
when "never" then return false
|
|
41
|
+
# "auto" falls through to TTY check
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
$stdout.respond_to?(:tty?) && $stdout.tty?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def self.git_color_config
|
|
48
|
+
# color.pkgs overrides color.ui for git-pkgs specific control
|
|
49
|
+
pkgs_color = `git config --get color.pkgs 2>/dev/null`.chomp
|
|
50
|
+
return normalize_color_value(pkgs_color) unless pkgs_color.empty?
|
|
51
|
+
|
|
52
|
+
ui_color = `git config --get color.ui 2>/dev/null`.chomp
|
|
53
|
+
return normalize_color_value(ui_color) unless ui_color.empty?
|
|
54
|
+
|
|
55
|
+
"auto"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.normalize_color_value(value)
|
|
59
|
+
case value.downcase
|
|
60
|
+
when "true", "always" then "always"
|
|
61
|
+
when "false", "never" then "never"
|
|
62
|
+
else "auto"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.colorize(text, *codes)
|
|
67
|
+
return text unless enabled?
|
|
68
|
+
|
|
69
|
+
code_str = codes.map { |c| CODES[c] || c }.join(";")
|
|
70
|
+
"\e[#{code_str}m#{text}\e[0m"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.red(text) = colorize(text, :red)
|
|
74
|
+
def self.green(text) = colorize(text, :green)
|
|
75
|
+
def self.yellow(text) = colorize(text, :yellow)
|
|
76
|
+
def self.blue(text) = colorize(text, :blue)
|
|
77
|
+
def self.cyan(text) = colorize(text, :cyan)
|
|
78
|
+
def self.bold(text) = colorize(text, :bold)
|
|
79
|
+
def self.dim(text) = colorize(text, :dim)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -4,6 +4,8 @@ module Git
|
|
|
4
4
|
module Pkgs
|
|
5
5
|
module Commands
|
|
6
6
|
class Blame
|
|
7
|
+
include Output
|
|
8
|
+
|
|
7
9
|
def initialize(args)
|
|
8
10
|
@args = args
|
|
9
11
|
@options = parse_options
|
|
@@ -11,11 +13,7 @@ module Git
|
|
|
11
13
|
|
|
12
14
|
def run
|
|
13
15
|
repo = Repository.new
|
|
14
|
-
|
|
15
|
-
unless Database.exists?(repo.git_dir)
|
|
16
|
-
$stderr.puts "Database not initialized. Run 'git pkgs init' first."
|
|
17
|
-
exit 1
|
|
18
|
-
end
|
|
16
|
+
require_database(repo)
|
|
19
17
|
|
|
20
18
|
Database.connect(repo.git_dir)
|
|
21
19
|
|
|
@@ -23,10 +21,7 @@ module Git
|
|
|
23
21
|
branch_name = @options[:branch] || repo.default_branch
|
|
24
22
|
branch = Models::Branch.find_by(name: branch_name)
|
|
25
23
|
|
|
26
|
-
unless branch&.last_analyzed_sha
|
|
27
|
-
$stderr.puts "No analysis found for branch '#{branch_name}'"
|
|
28
|
-
exit 1
|
|
29
|
-
end
|
|
24
|
+
error "No analysis found for branch '#{branch_name}'" unless branch&.last_analyzed_sha
|
|
30
25
|
|
|
31
26
|
current_commit = Models::Commit.find_by(sha: branch.last_analyzed_sha)
|
|
32
27
|
snapshots = current_commit&.dependency_snapshots&.includes(:manifest) || []
|
|
@@ -36,7 +31,7 @@ module Git
|
|
|
36
31
|
end
|
|
37
32
|
|
|
38
33
|
if snapshots.empty?
|
|
39
|
-
|
|
34
|
+
empty_result "No dependencies found"
|
|
40
35
|
return
|
|
41
36
|
end
|
|
42
37
|
|
|
@@ -74,20 +69,24 @@ module Git
|
|
|
74
69
|
end
|
|
75
70
|
puts JSON.pretty_generate(json_data)
|
|
76
71
|
else
|
|
77
|
-
|
|
72
|
+
paginate { output_text(blame_data) }
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def output_text(blame_data)
|
|
77
|
+
grouped = blame_data.group_by { |d| [d[:manifest], d[:ecosystem]] }
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
grouped.each do |(manifest, ecosystem), deps|
|
|
80
|
+
puts "#{manifest} (#{ecosystem}):"
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
max_name_len = deps.map { |d| d[:name].length }.max
|
|
83
|
+
max_author_len = deps.map { |d| d[:author].length }.max
|
|
84
84
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
end
|
|
89
|
-
puts
|
|
85
|
+
deps.sort_by { |d| d[:name] }.each do |dep|
|
|
86
|
+
date = dep[:date].strftime("%Y-%m-%d")
|
|
87
|
+
puts " #{dep[:name].ljust(max_name_len)} #{dep[:author].ljust(max_author_len)} #{date} #{dep[:sha]}"
|
|
90
88
|
end
|
|
89
|
+
puts
|
|
91
90
|
end
|
|
92
91
|
end
|
|
93
92
|
|
|
@@ -127,6 +126,10 @@ module Git
|
|
|
127
126
|
options[:format] = v
|
|
128
127
|
end
|
|
129
128
|
|
|
129
|
+
opts.on("--no-pager", "Do not pipe output into a pager") do
|
|
130
|
+
options[:no_pager] = true
|
|
131
|
+
end
|
|
132
|
+
|
|
130
133
|
opts.on("-h", "--help", "Show this help") do
|
|
131
134
|
puts opts
|
|
132
135
|
exit
|
|
@@ -4,6 +4,8 @@ module Git
|
|
|
4
4
|
module Pkgs
|
|
5
5
|
module Commands
|
|
6
6
|
class Branch
|
|
7
|
+
include Output
|
|
8
|
+
|
|
7
9
|
BATCH_SIZE = 100
|
|
8
10
|
SNAPSHOT_INTERVAL = 20
|
|
9
11
|
|
|
@@ -25,32 +27,20 @@ module Git
|
|
|
25
27
|
when nil, "-h", "--help"
|
|
26
28
|
print_help
|
|
27
29
|
else
|
|
28
|
-
|
|
29
|
-
$stderr.puts "Run 'git pkgs branch --help' for usage"
|
|
30
|
-
exit 1
|
|
30
|
+
error "Unknown subcommand: #{subcommand}. Run 'git pkgs branch --help' for usage"
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def add_branch
|
|
35
35
|
branch_name = @args.shift
|
|
36
|
-
unless branch_name
|
|
37
|
-
$stderr.puts "Usage: git pkgs branch add <name>"
|
|
38
|
-
exit 1
|
|
39
|
-
end
|
|
36
|
+
error "Usage: git pkgs branch add <name>" unless branch_name
|
|
40
37
|
|
|
41
38
|
repo = Repository.new
|
|
42
|
-
|
|
43
|
-
unless Database.exists?(repo.git_dir)
|
|
44
|
-
$stderr.puts "Database not initialized. Run 'git pkgs init' first."
|
|
45
|
-
exit 1
|
|
46
|
-
end
|
|
39
|
+
require_database(repo)
|
|
47
40
|
|
|
48
41
|
Database.connect(repo.git_dir)
|
|
49
42
|
|
|
50
|
-
unless repo.branch_exists?(branch_name)
|
|
51
|
-
$stderr.puts "Branch '#{branch_name}' not found"
|
|
52
|
-
exit 1
|
|
53
|
-
end
|
|
43
|
+
error "Branch '#{branch_name}' not found" unless repo.branch_exists?(branch_name)
|
|
54
44
|
|
|
55
45
|
existing = Models::Branch.find_by(name: branch_name)
|
|
56
46
|
if existing
|
|
@@ -84,18 +74,14 @@ module Git
|
|
|
84
74
|
|
|
85
75
|
def list_branches
|
|
86
76
|
repo = Repository.new
|
|
87
|
-
|
|
88
|
-
unless Database.exists?(repo.git_dir)
|
|
89
|
-
$stderr.puts "Database not initialized. Run 'git pkgs init' first."
|
|
90
|
-
exit 1
|
|
91
|
-
end
|
|
77
|
+
require_database(repo)
|
|
92
78
|
|
|
93
79
|
Database.connect(repo.git_dir)
|
|
94
80
|
|
|
95
81
|
branches = Models::Branch.all
|
|
96
82
|
|
|
97
83
|
if branches.empty?
|
|
98
|
-
|
|
84
|
+
empty_result "No branches tracked"
|
|
99
85
|
return
|
|
100
86
|
end
|
|
101
87
|
|
|
@@ -110,25 +96,15 @@ module Git
|
|
|
110
96
|
|
|
111
97
|
def remove_branch
|
|
112
98
|
branch_name = @args.shift
|
|
113
|
-
unless branch_name
|
|
114
|
-
$stderr.puts "Usage: git pkgs branch remove <name>"
|
|
115
|
-
exit 1
|
|
116
|
-
end
|
|
99
|
+
error "Usage: git pkgs branch remove <name>" unless branch_name
|
|
117
100
|
|
|
118
101
|
repo = Repository.new
|
|
119
|
-
|
|
120
|
-
unless Database.exists?(repo.git_dir)
|
|
121
|
-
$stderr.puts "Database not initialized. Run 'git pkgs init' first."
|
|
122
|
-
exit 1
|
|
123
|
-
end
|
|
102
|
+
require_database(repo)
|
|
124
103
|
|
|
125
104
|
Database.connect(repo.git_dir)
|
|
126
105
|
|
|
127
106
|
branch = Models::Branch.find_by(name: branch_name)
|
|
128
|
-
unless branch
|
|
129
|
-
$stderr.puts "Branch '#{branch_name}' not tracked"
|
|
130
|
-
exit 1
|
|
131
|
-
end
|
|
107
|
+
error "Branch '#{branch_name}' not tracked" unless branch
|
|
132
108
|
|
|
133
109
|
# Only delete branch_commits, keep shared commits
|
|
134
110
|
count = branch.branch_commits.count
|
|
@@ -4,6 +4,8 @@ module Git
|
|
|
4
4
|
module Pkgs
|
|
5
5
|
module Commands
|
|
6
6
|
class Diff
|
|
7
|
+
include Output
|
|
8
|
+
|
|
7
9
|
def initialize(args)
|
|
8
10
|
@args = args
|
|
9
11
|
@options = parse_options
|
|
@@ -11,48 +13,27 @@ module Git
|
|
|
11
13
|
|
|
12
14
|
def run
|
|
13
15
|
repo = Repository.new
|
|
14
|
-
|
|
15
|
-
unless Database.exists?(repo.git_dir)
|
|
16
|
-
$stderr.puts "Database not initialized. Run 'git pkgs init' first."
|
|
17
|
-
exit 1
|
|
18
|
-
end
|
|
16
|
+
require_database(repo)
|
|
19
17
|
|
|
20
18
|
Database.connect(repo.git_dir)
|
|
21
19
|
|
|
22
20
|
from_ref = @options[:from]
|
|
23
21
|
to_ref = @options[:to] || "HEAD"
|
|
24
22
|
|
|
25
|
-
unless from_ref
|
|
26
|
-
$stderr.puts "Usage: git pkgs diff --from=REF [--to=REF]"
|
|
27
|
-
exit 1
|
|
28
|
-
end
|
|
23
|
+
error "Usage: git pkgs diff --from=REF [--to=REF]" unless from_ref
|
|
29
24
|
|
|
30
25
|
# Resolve git refs (like HEAD~10) to SHAs
|
|
31
26
|
from_sha = repo.rev_parse(from_ref)
|
|
32
27
|
to_sha = repo.rev_parse(to_ref)
|
|
33
28
|
|
|
34
|
-
unless from_sha
|
|
35
|
-
|
|
36
|
-
exit 1
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
unless to_sha
|
|
40
|
-
$stderr.puts "Could not resolve '#{to_ref}'"
|
|
41
|
-
exit 1
|
|
42
|
-
end
|
|
29
|
+
error "Could not resolve '#{from_ref}'" unless from_sha
|
|
30
|
+
error "Could not resolve '#{to_ref}'" unless to_sha
|
|
43
31
|
|
|
44
32
|
from_commit = find_or_create_commit(repo, from_sha)
|
|
45
33
|
to_commit = find_or_create_commit(repo, to_sha)
|
|
46
34
|
|
|
47
|
-
unless from_commit
|
|
48
|
-
|
|
49
|
-
exit 1
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
unless to_commit
|
|
53
|
-
$stderr.puts "Commit '#{to_sha[0..7]}' not found"
|
|
54
|
-
exit 1
|
|
55
|
-
end
|
|
35
|
+
error "Commit '#{from_sha[0..7]}' not found" unless from_commit
|
|
36
|
+
error "Commit '#{to_sha[0..7]}' not found" unless to_commit
|
|
56
37
|
|
|
57
38
|
# Get all changes between the two commits
|
|
58
39
|
changes = Models::DependencyChange
|
|
@@ -67,10 +48,14 @@ module Git
|
|
|
67
48
|
end
|
|
68
49
|
|
|
69
50
|
if changes.empty?
|
|
70
|
-
|
|
51
|
+
empty_result "No dependency changes between #{from_commit.short_sha} and #{to_commit.short_sha}"
|
|
71
52
|
return
|
|
72
53
|
end
|
|
73
54
|
|
|
55
|
+
paginate { output_text(from_commit, to_commit, changes) }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def output_text(from_commit, to_commit, changes)
|
|
74
59
|
puts "Dependency changes from #{from_commit.short_sha} to #{to_commit.short_sha}:"
|
|
75
60
|
puts
|
|
76
61
|
|
|
@@ -79,35 +64,38 @@ module Git
|
|
|
79
64
|
removed = changes.select { |c| c.change_type == "removed" }
|
|
80
65
|
|
|
81
66
|
if added.any?
|
|
82
|
-
puts "Added:"
|
|
67
|
+
puts Color.green("Added:")
|
|
83
68
|
added.group_by(&:name).each do |name, pkg_changes|
|
|
84
69
|
latest = pkg_changes.last
|
|
85
|
-
puts " + #{name} #{latest.requirement} (#{latest.manifest.path})"
|
|
70
|
+
puts Color.green(" + #{name} #{latest.requirement} (#{latest.manifest.path})")
|
|
86
71
|
end
|
|
87
72
|
puts
|
|
88
73
|
end
|
|
89
74
|
|
|
90
75
|
if modified.any?
|
|
91
|
-
puts "Modified:"
|
|
76
|
+
puts Color.yellow("Modified:")
|
|
92
77
|
modified.group_by(&:name).each do |name, pkg_changes|
|
|
93
78
|
first = pkg_changes.first
|
|
94
79
|
latest = pkg_changes.last
|
|
95
|
-
puts " ~ #{name} #{first.previous_requirement} -> #{latest.requirement}"
|
|
80
|
+
puts Color.yellow(" ~ #{name} #{first.previous_requirement} -> #{latest.requirement}")
|
|
96
81
|
end
|
|
97
82
|
puts
|
|
98
83
|
end
|
|
99
84
|
|
|
100
85
|
if removed.any?
|
|
101
|
-
puts "Removed:"
|
|
86
|
+
puts Color.red("Removed:")
|
|
102
87
|
removed.group_by(&:name).each do |name, pkg_changes|
|
|
103
88
|
latest = pkg_changes.last
|
|
104
|
-
puts " - #{name} (was #{latest.requirement})"
|
|
89
|
+
puts Color.red(" - #{name} (was #{latest.requirement})")
|
|
105
90
|
end
|
|
106
91
|
puts
|
|
107
92
|
end
|
|
108
93
|
|
|
109
94
|
# Summary
|
|
110
|
-
|
|
95
|
+
added_count = Color.green("+#{added.map(&:name).uniq.count}")
|
|
96
|
+
removed_count = Color.red("-#{removed.map(&:name).uniq.count}")
|
|
97
|
+
modified_count = Color.yellow("~#{modified.map(&:name).uniq.count}")
|
|
98
|
+
puts "Summary: #{added_count} #{removed_count} #{modified_count}"
|
|
111
99
|
end
|
|
112
100
|
|
|
113
101
|
def find_or_create_commit(repo, sha)
|
|
@@ -149,6 +137,10 @@ module Git
|
|
|
149
137
|
options[:ecosystem] = v
|
|
150
138
|
end
|
|
151
139
|
|
|
140
|
+
opts.on("--no-pager", "Do not pipe output into a pager") do
|
|
141
|
+
options[:no_pager] = true
|
|
142
|
+
end
|
|
143
|
+
|
|
152
144
|
opts.on("-h", "--help", "Show this help") do
|
|
153
145
|
puts opts
|
|
154
146
|
exit
|