git-pkgs 0.2.0 → 0.4.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 +26 -0
- data/CONTRIBUTING.md +27 -0
- data/LICENSE +189 -189
- data/README.md +101 -2
- data/SECURITY.md +7 -0
- data/lib/git/pkgs/analyzer.rb +1 -1
- data/lib/git/pkgs/cli.rb +12 -4
- data/lib/git/pkgs/color.rb +83 -0
- data/lib/git/pkgs/commands/blame.rb +24 -21
- data/lib/git/pkgs/commands/branch.rb +11 -35
- data/lib/git/pkgs/commands/diff.rb +27 -35
- data/lib/git/pkgs/commands/diff_driver.rb +169 -0
- 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 +8 -6
- data/lib/git/pkgs/commands/init.rb +13 -12
- 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/where.rb +166 -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 +8 -1
- metadata +13 -2
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,33 @@ 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
|
+
### Find where a package is declared
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
git pkgs where rails # find in manifest files
|
|
265
|
+
git pkgs where lodash -C 2 # show 2 lines of context
|
|
266
|
+
git pkgs where express --ecosystem=npm
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Shows which manifest files declare a package and the exact line:
|
|
270
|
+
|
|
271
|
+
```
|
|
272
|
+
Gemfile:5:gem "rails", "~> 7.0"
|
|
273
|
+
Gemfile.lock:142: rails (7.0.8)
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Like `grep` but scoped to manifest files that git-pkgs knows about.
|
|
277
|
+
|
|
278
|
+
### List commits with dependency changes
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
git pkgs log # recent commits with dependency changes
|
|
282
|
+
git pkgs log --author=alice # filter by author
|
|
283
|
+
git pkgs log -n 50 # show more commits
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Like `git log` but only shows commits that changed dependencies, with the changes listed under each commit.
|
|
287
|
+
|
|
247
288
|
### Keep database updated
|
|
248
289
|
|
|
249
290
|
After the initial analysis, you can incrementally update the database with new commits:
|
|
@@ -258,6 +299,27 @@ You can also install git hooks to update automatically after commits and merges:
|
|
|
258
299
|
git pkgs hooks --install
|
|
259
300
|
```
|
|
260
301
|
|
|
302
|
+
### Upgrading
|
|
303
|
+
|
|
304
|
+
After updating git-pkgs, you may need to rebuild the database if the schema has changed:
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
git pkgs upgrade
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
This is detected automatically and you'll see a message if an upgrade is needed.
|
|
311
|
+
|
|
312
|
+
### Show database schema
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
git pkgs schema # human-readable table format
|
|
316
|
+
git pkgs schema --format=sql # CREATE TABLE statements
|
|
317
|
+
git pkgs schema --format=json # JSON structure
|
|
318
|
+
git pkgs schema --format=markdown # markdown tables
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Useful for understanding the [database structure](docs/schema.md) or generating documentation.
|
|
322
|
+
|
|
261
323
|
### CI usage
|
|
262
324
|
|
|
263
325
|
You can run git-pkgs in CI to show dependency changes in pull requests:
|
|
@@ -283,6 +345,43 @@ jobs:
|
|
|
283
345
|
- run: git pkgs diff --from=origin/${{ github.base_ref }} --to=HEAD
|
|
284
346
|
```
|
|
285
347
|
|
|
348
|
+
### Diff driver
|
|
349
|
+
|
|
350
|
+
Install a git textconv driver that shows semantic dependency changes instead of raw lockfile diffs:
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
git pkgs diff-driver --install
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Now `git diff` on lockfiles shows a sorted dependency list instead of raw lockfile changes:
|
|
357
|
+
|
|
358
|
+
```diff
|
|
359
|
+
diff --git a/Gemfile.lock b/Gemfile.lock
|
|
360
|
+
--- a/Gemfile.lock
|
|
361
|
+
+++ b/Gemfile.lock
|
|
362
|
+
@@ -1,3 +1,3 @@
|
|
363
|
+
+kamal 1.0.0
|
|
364
|
+
-puma 5.0.0
|
|
365
|
+
+puma 6.0.0
|
|
366
|
+
rails 7.0.0
|
|
367
|
+
-sidekiq 6.0.0
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Use `git diff --no-textconv` to see the raw lockfile diff. To remove: `git pkgs diff-driver --uninstall`
|
|
371
|
+
|
|
372
|
+
## Configuration
|
|
373
|
+
|
|
374
|
+
git-pkgs respects [standard git configuration](https://git-scm.com/docs/git-config).
|
|
375
|
+
|
|
376
|
+
**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.
|
|
377
|
+
|
|
378
|
+
**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.
|
|
379
|
+
|
|
380
|
+
**Environment variables:**
|
|
381
|
+
|
|
382
|
+
- `GIT_DIR` - git directory location (standard git variable)
|
|
383
|
+
- `GIT_PKGS_DB` - database path (default: `.git/pkgs.sqlite3`)
|
|
384
|
+
|
|
286
385
|
## Performance
|
|
287
386
|
|
|
288
387
|
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/SECURITY.md
ADDED
data/lib/git/pkgs/analyzer.rb
CHANGED
|
@@ -46,7 +46,7 @@ module Git
|
|
|
46
46
|
QUICK_MANIFEST_REGEX = Regexp.union(
|
|
47
47
|
QUICK_MANIFEST_PATTERNS.map do |pattern|
|
|
48
48
|
if pattern.include?('*')
|
|
49
|
-
Regexp.new(
|
|
49
|
+
Regexp.new(Regexp.escape(pattern).gsub('\\*', '.*'))
|
|
50
50
|
else
|
|
51
51
|
/(?:^|\/)#{Regexp.escape(pattern)}$/
|
|
52
52
|
end
|
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 where why blame stale stats diff branch show log upgrade schema diff-driver].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,7 +35,10 @@ module Git
|
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
def run_command(command)
|
|
37
|
-
|
|
38
|
+
command = ALIASES.fetch(command, command)
|
|
39
|
+
# Convert kebab-case or snake_case to PascalCase
|
|
40
|
+
class_name = command.split(/[-_]/).map(&:capitalize).join
|
|
41
|
+
command_class = Commands.const_get(class_name)
|
|
38
42
|
command_class.new(@args).run
|
|
39
43
|
rescue NameError
|
|
40
44
|
$stderr.puts "Command '#{command}' not yet implemented"
|
|
@@ -55,12 +59,16 @@ module Git
|
|
|
55
59
|
tree Show dependency tree grouped by type
|
|
56
60
|
history Show the history of a package
|
|
57
61
|
search Find a dependency across all history
|
|
62
|
+
where Show where a package appears in manifest files
|
|
58
63
|
why Explain why a dependency exists
|
|
59
64
|
blame Show who added each dependency
|
|
60
|
-
|
|
65
|
+
stale Show dependencies that haven't been updated
|
|
61
66
|
stats Show dependency statistics
|
|
62
67
|
diff Show dependency changes between commits
|
|
63
68
|
show Show dependency changes in a commit
|
|
69
|
+
log List commits with dependency changes
|
|
70
|
+
upgrade Upgrade database after git-pkgs update
|
|
71
|
+
schema Show database schema
|
|
64
72
|
|
|
65
73
|
Options:
|
|
66
74
|
-h, --help Show this help message
|
|
@@ -0,0 +1,83 @@
|
|
|
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.magenta(text) = colorize(text, :magenta)
|
|
78
|
+
def self.cyan(text) = colorize(text, :cyan)
|
|
79
|
+
def self.bold(text) = colorize(text, :bold)
|
|
80
|
+
def self.dim(text) = colorize(text, :dim)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
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
|
|
|
@@ -102,7 +101,7 @@ module Git
|
|
|
102
101
|
def parse_coauthors(message)
|
|
103
102
|
return [] unless message
|
|
104
103
|
|
|
105
|
-
message.scan(/^Co-authored-by
|
|
104
|
+
message.scan(/^Co-authored-by:([^<]+)<[^>]+>/i).flatten.map(&:strip)
|
|
106
105
|
end
|
|
107
106
|
|
|
108
107
|
def bot_author?(name)
|
|
@@ -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
|