git-pkgs 0.2.0 → 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 +15 -1
- data/README.md +60 -2
- data/lib/git/pkgs/cli.rb +8 -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 +14 -16
- 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 +17 -23
- data/lib/git/pkgs/commands/{outdated.rb → stale.rb} +16 -13
- data/lib/git/pkgs/commands/stats.rb +59 -17
- 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 +6 -1
- metadata +8 -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,18 @@
|
|
|
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
|
|
2
16
|
|
|
3
17
|
## [0.2.0] - 2026-01-02
|
|
4
18
|
|
data/README.md
CHANGED
|
@@ -146,6 +146,7 @@ Show who added each current dependency:
|
|
|
146
146
|
```bash
|
|
147
147
|
git pkgs blame
|
|
148
148
|
git pkgs blame --ecosystem=rubygems
|
|
149
|
+
git pkgs praise # alias for blame
|
|
149
150
|
```
|
|
150
151
|
|
|
151
152
|
Example output:
|
|
@@ -167,8 +168,10 @@ Gemfile (rubygems):
|
|
|
167
168
|
|
|
168
169
|
```bash
|
|
169
170
|
git pkgs stats
|
|
170
|
-
git pkgs stats --by-author
|
|
171
|
-
git pkgs stats --ecosystem=npm
|
|
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
|
|
172
175
|
```
|
|
173
176
|
|
|
174
177
|
Example output:
|
|
@@ -225,6 +228,17 @@ git pkgs tree --ecosystem=rubygems
|
|
|
225
228
|
|
|
226
229
|
This shows dependencies grouped by type (runtime, development, etc).
|
|
227
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.
|
|
241
|
+
|
|
228
242
|
### Diff between commits
|
|
229
243
|
|
|
230
244
|
```bash
|
|
@@ -244,6 +258,16 @@ git pkgs show HEAD~5 # relative ref
|
|
|
244
258
|
|
|
245
259
|
Like `git show` but for dependencies. Shows what was added, modified, or removed in a single commit.
|
|
246
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.
|
|
270
|
+
|
|
247
271
|
### Keep database updated
|
|
248
272
|
|
|
249
273
|
After the initial analysis, you can incrementally update the database with new commits:
|
|
@@ -258,6 +282,27 @@ You can also install git hooks to update automatically after commits and merges:
|
|
|
258
282
|
git pkgs hooks --install
|
|
259
283
|
```
|
|
260
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
|
+
|
|
261
306
|
### CI usage
|
|
262
307
|
|
|
263
308
|
You can run git-pkgs in CI to show dependency changes in pull requests:
|
|
@@ -283,6 +328,19 @@ jobs:
|
|
|
283
328
|
- run: git pkgs diff --from=origin/${{ github.base_ref }} --to=HEAD
|
|
284
329
|
```
|
|
285
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
|
+
|
|
286
344
|
## Performance
|
|
287
345
|
|
|
288
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.
|
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,10 +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
|
|
63
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
|
|
64
69
|
|
|
65
70
|
Options:
|
|
66
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
|
|
@@ -6,6 +6,8 @@ module Git
|
|
|
6
6
|
module Pkgs
|
|
7
7
|
module Commands
|
|
8
8
|
class History
|
|
9
|
+
include Output
|
|
10
|
+
|
|
9
11
|
def initialize(args)
|
|
10
12
|
@args = args
|
|
11
13
|
@options = parse_options
|
|
@@ -15,11 +17,7 @@ module Git
|
|
|
15
17
|
package_name = @args.shift
|
|
16
18
|
|
|
17
19
|
repo = Repository.new
|
|
18
|
-
|
|
19
|
-
unless Database.exists?(repo.git_dir)
|
|
20
|
-
$stderr.puts "Database not initialized. Run 'git pkgs init' first."
|
|
21
|
-
exit 1
|
|
22
|
-
end
|
|
20
|
+
require_database(repo)
|
|
23
21
|
|
|
24
22
|
Database.connect(repo.git_dir)
|
|
25
23
|
|
|
@@ -52,18 +50,15 @@ module Git
|
|
|
52
50
|
end
|
|
53
51
|
|
|
54
52
|
if changes.empty?
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
else
|
|
58
|
-
puts "No dependency changes found"
|
|
59
|
-
end
|
|
53
|
+
msg = package_name ? "No history found for '#{package_name}'" : "No dependency changes found"
|
|
54
|
+
empty_result msg
|
|
60
55
|
return
|
|
61
56
|
end
|
|
62
57
|
|
|
63
58
|
if @options[:format] == "json"
|
|
64
59
|
output_json(changes)
|
|
65
60
|
else
|
|
66
|
-
output_text(changes, package_name)
|
|
61
|
+
paginate { output_text(changes, package_name) }
|
|
67
62
|
end
|
|
68
63
|
end
|
|
69
64
|
|
|
@@ -81,13 +76,13 @@ module Git
|
|
|
81
76
|
|
|
82
77
|
case change.change_type
|
|
83
78
|
when "added"
|
|
84
|
-
action = "Added"
|
|
79
|
+
action = Color.green("Added")
|
|
85
80
|
version_info = change.requirement
|
|
86
81
|
when "modified"
|
|
87
|
-
action = "Updated"
|
|
82
|
+
action = Color.yellow("Updated")
|
|
88
83
|
version_info = "#{change.previous_requirement} -> #{change.requirement}"
|
|
89
84
|
when "removed"
|
|
90
|
-
action = "Removed"
|
|
85
|
+
action = Color.red("Removed")
|
|
91
86
|
version_info = change.requirement
|
|
92
87
|
end
|
|
93
88
|
|
|
@@ -150,6 +145,10 @@ module Git
|
|
|
150
145
|
options[:until] = v
|
|
151
146
|
end
|
|
152
147
|
|
|
148
|
+
opts.on("--no-pager", "Do not pipe output into a pager") do
|
|
149
|
+
options[:no_pager] = true
|
|
150
|
+
end
|
|
151
|
+
|
|
153
152
|
opts.on("-h", "--help", "Show this help") do
|
|
154
153
|
puts opts
|
|
155
154
|
exit
|
|
@@ -163,8 +162,7 @@ module Git
|
|
|
163
162
|
def parse_time(str)
|
|
164
163
|
Time.parse(str)
|
|
165
164
|
rescue ArgumentError
|
|
166
|
-
|
|
167
|
-
exit 1
|
|
165
|
+
error "Invalid date format: #{str}"
|
|
168
166
|
end
|
|
169
167
|
end
|
|
170
168
|
end
|