git-pkgs 0.1.1 → 0.2.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 +7 -0
- data/README.md +65 -10
- data/lib/git/pkgs/cli.rb +2 -1
- data/lib/git/pkgs/commands/history.rb +39 -0
- data/lib/git/pkgs/commands/show.rb +166 -0
- data/lib/git/pkgs/commands/stats.rb +71 -12
- data/lib/git/pkgs/version.rb +1 -1
- data/lib/git/pkgs.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7ff2285e54475944f6c8a9cb98ae9f5efcb78ee0aa74dbea6d46553bbf71caa2
|
|
4
|
+
data.tar.gz: 81aee90f3e0b9f573e1f9e442dc4c487f07957c320d70ca24a809929248279a3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ffdcb5fe7cc217b105f10018ba24101131120fdfcc8305f3142fcc11f29e7f77114e95c0a2abbe661fcac3a74e4ea0662031dc6a997020225bdabcc9b6ab754b
|
|
7
|
+
data.tar.gz: 204dc587c61ccb128957910784ccab0cb84c667627dff1fb81d18a6b6779abc34cfff3a838f06146c954d1a50b9ac095a036a67c898d12699c530476b9b3d31c
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.2.0] - 2026-01-02
|
|
4
|
+
|
|
5
|
+
- `git pkgs show` command to display dependency changes in a single commit
|
|
6
|
+
- `git pkgs history` now supports `--author`, `--since`, and `--until` filters
|
|
7
|
+
- `git pkgs stats --by-author` shows who added the most dependencies
|
|
8
|
+
- `git pkgs stats --ecosystem=X` filters statistics by ecosystem
|
|
9
|
+
|
|
3
10
|
## [0.1.1] - 2026-01-01
|
|
4
11
|
|
|
5
12
|
- `git pkgs history` now works without a package argument to show all dependency changes
|
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:
|
|
@@ -166,6 +167,8 @@ Gemfile (rubygems):
|
|
|
166
167
|
|
|
167
168
|
```bash
|
|
168
169
|
git pkgs stats
|
|
170
|
+
git pkgs stats --by-author # who added the most dependencies
|
|
171
|
+
git pkgs stats --ecosystem=npm # filter by ecosystem
|
|
169
172
|
```
|
|
170
173
|
|
|
171
174
|
Example output:
|
|
@@ -211,7 +214,7 @@ Manifest Files
|
|
|
211
214
|
git pkgs why rails
|
|
212
215
|
```
|
|
213
216
|
|
|
214
|
-
|
|
217
|
+
This shows the commit that added the dependency along with the author and message.
|
|
215
218
|
|
|
216
219
|
### Dependency tree
|
|
217
220
|
|
|
@@ -220,7 +223,7 @@ git pkgs tree
|
|
|
220
223
|
git pkgs tree --ecosystem=rubygems
|
|
221
224
|
```
|
|
222
225
|
|
|
223
|
-
|
|
226
|
+
This shows dependencies grouped by type (runtime, development, etc).
|
|
224
227
|
|
|
225
228
|
### Diff between commits
|
|
226
229
|
|
|
@@ -229,20 +232,57 @@ git pkgs diff --from=abc123 --to=def456
|
|
|
229
232
|
git pkgs diff --from=HEAD~10
|
|
230
233
|
```
|
|
231
234
|
|
|
232
|
-
|
|
235
|
+
This shows added, removed, and modified packages with version info.
|
|
236
|
+
|
|
237
|
+
### Show changes in a commit
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
git pkgs show # show dependency changes in HEAD
|
|
241
|
+
git pkgs show abc123 # specific commit
|
|
242
|
+
git pkgs show HEAD~5 # relative ref
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Like `git show` but for dependencies. Shows what was added, modified, or removed in a single commit.
|
|
233
246
|
|
|
234
247
|
### Keep database updated
|
|
235
248
|
|
|
249
|
+
After the initial analysis, you can incrementally update the database with new commits:
|
|
250
|
+
|
|
236
251
|
```bash
|
|
237
252
|
git pkgs update
|
|
238
253
|
```
|
|
239
254
|
|
|
240
|
-
|
|
255
|
+
You can also install git hooks to update automatically after commits and merges:
|
|
241
256
|
|
|
242
257
|
```bash
|
|
243
258
|
git pkgs hooks --install
|
|
244
259
|
```
|
|
245
260
|
|
|
261
|
+
### CI usage
|
|
262
|
+
|
|
263
|
+
You can run git-pkgs in CI to show dependency changes in pull requests:
|
|
264
|
+
|
|
265
|
+
```yaml
|
|
266
|
+
# .github/workflows/deps.yml
|
|
267
|
+
name: Dependencies
|
|
268
|
+
|
|
269
|
+
on: pull_request
|
|
270
|
+
|
|
271
|
+
jobs:
|
|
272
|
+
diff:
|
|
273
|
+
runs-on: ubuntu-latest
|
|
274
|
+
steps:
|
|
275
|
+
- uses: actions/checkout@v4
|
|
276
|
+
with:
|
|
277
|
+
fetch-depth: 0
|
|
278
|
+
- uses: ruby/setup-ruby@v1
|
|
279
|
+
with:
|
|
280
|
+
ruby-version: '3.3'
|
|
281
|
+
- run: gem install git-pkgs
|
|
282
|
+
- run: git pkgs init
|
|
283
|
+
- run: git pkgs diff --from=origin/${{ github.base_ref }} --to=HEAD
|
|
284
|
+
```
|
|
285
|
+
|
|
246
286
|
## Performance
|
|
247
287
|
|
|
248
288
|
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 +311,21 @@ The database schema stores:
|
|
|
271
311
|
|
|
272
312
|
See [docs/schema.md](docs/schema.md) for full schema documentation.
|
|
273
313
|
|
|
314
|
+
Since the database is just SQLite, you can query it directly for ad-hoc analysis:
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
sqlite3 .git/pkgs.sqlite3 "
|
|
318
|
+
-- who added the most dependencies?
|
|
319
|
+
SELECT c.author_name, COUNT(*) as deps_added
|
|
320
|
+
FROM dependency_changes dc
|
|
321
|
+
JOIN commits c ON dc.commit_id = c.id
|
|
322
|
+
WHERE dc.change_type = 'added'
|
|
323
|
+
GROUP BY c.author_name
|
|
324
|
+
ORDER BY deps_added DESC
|
|
325
|
+
LIMIT 10;
|
|
326
|
+
"
|
|
327
|
+
```
|
|
328
|
+
|
|
274
329
|
## Development
|
|
275
330
|
|
|
276
331
|
```bash
|
data/lib/git/pkgs/cli.rb
CHANGED
|
@@ -5,7 +5,7 @@ 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 outdated stats diff branch].freeze
|
|
8
|
+
COMMANDS = %w[init update hooks info list tree history search why blame outdated stats diff branch show].freeze
|
|
9
9
|
|
|
10
10
|
def self.run(args)
|
|
11
11
|
new(args).run
|
|
@@ -60,6 +60,7 @@ module Git
|
|
|
60
60
|
outdated Show dependencies that haven't been updated
|
|
61
61
|
stats Show dependency statistics
|
|
62
62
|
diff Show dependency changes between commits
|
|
63
|
+
show Show dependency changes in a commit
|
|
63
64
|
|
|
64
65
|
Options:
|
|
65
66
|
-h, --help Show this help message
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "time"
|
|
4
|
+
|
|
3
5
|
module Git
|
|
4
6
|
module Pkgs
|
|
5
7
|
module Commands
|
|
@@ -31,6 +33,24 @@ module Git
|
|
|
31
33
|
changes = changes.for_platform(@options[:ecosystem])
|
|
32
34
|
end
|
|
33
35
|
|
|
36
|
+
if @options[:author]
|
|
37
|
+
author = @options[:author]
|
|
38
|
+
changes = changes.joins(:commit).where(
|
|
39
|
+
"commits.author_name LIKE ? OR commits.author_email LIKE ?",
|
|
40
|
+
"%#{author}%", "%#{author}%"
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
if @options[:since]
|
|
45
|
+
since_time = parse_time(@options[:since])
|
|
46
|
+
changes = changes.joins(:commit).where("commits.committed_at >= ?", since_time)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
if @options[:until]
|
|
50
|
+
until_time = parse_time(@options[:until])
|
|
51
|
+
changes = changes.joins(:commit).where("commits.committed_at <= ?", until_time)
|
|
52
|
+
end
|
|
53
|
+
|
|
34
54
|
if changes.empty?
|
|
35
55
|
if package_name
|
|
36
56
|
puts "No history found for '#{package_name}'"
|
|
@@ -118,6 +138,18 @@ module Git
|
|
|
118
138
|
options[:format] = v
|
|
119
139
|
end
|
|
120
140
|
|
|
141
|
+
opts.on("--author=NAME", "Filter by author name or email") do |v|
|
|
142
|
+
options[:author] = v
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
opts.on("--since=DATE", "Show changes after date (YYYY-MM-DD)") do |v|
|
|
146
|
+
options[:since] = v
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
opts.on("--until=DATE", "Show changes before date (YYYY-MM-DD)") do |v|
|
|
150
|
+
options[:until] = v
|
|
151
|
+
end
|
|
152
|
+
|
|
121
153
|
opts.on("-h", "--help", "Show this help") do
|
|
122
154
|
puts opts
|
|
123
155
|
exit
|
|
@@ -127,6 +159,13 @@ module Git
|
|
|
127
159
|
parser.parse!(@args)
|
|
128
160
|
options
|
|
129
161
|
end
|
|
162
|
+
|
|
163
|
+
def parse_time(str)
|
|
164
|
+
Time.parse(str)
|
|
165
|
+
rescue ArgumentError
|
|
166
|
+
$stderr.puts "Invalid date format: #{str}"
|
|
167
|
+
exit 1
|
|
168
|
+
end
|
|
130
169
|
end
|
|
131
170
|
end
|
|
132
171
|
end
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Git
|
|
4
|
+
module Pkgs
|
|
5
|
+
module Commands
|
|
6
|
+
class Show
|
|
7
|
+
def initialize(args)
|
|
8
|
+
@args = args
|
|
9
|
+
@options = parse_options
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def run
|
|
13
|
+
ref = @args.shift || "HEAD"
|
|
14
|
+
|
|
15
|
+
repo = Repository.new
|
|
16
|
+
|
|
17
|
+
unless Database.exists?(repo.git_dir)
|
|
18
|
+
$stderr.puts "Database not initialized. Run 'git pkgs init' first."
|
|
19
|
+
exit 1
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
Database.connect(repo.git_dir)
|
|
23
|
+
|
|
24
|
+
sha = repo.rev_parse(ref)
|
|
25
|
+
|
|
26
|
+
unless sha
|
|
27
|
+
$stderr.puts "Could not resolve '#{ref}'"
|
|
28
|
+
exit 1
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
commit = find_or_create_commit(repo, sha)
|
|
32
|
+
|
|
33
|
+
unless commit
|
|
34
|
+
$stderr.puts "Commit '#{sha[0..7]}' not found"
|
|
35
|
+
exit 1
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
changes = Models::DependencyChange
|
|
39
|
+
.includes(:commit, :manifest)
|
|
40
|
+
.where(commit_id: commit.id)
|
|
41
|
+
|
|
42
|
+
if @options[:ecosystem]
|
|
43
|
+
changes = changes.where(ecosystem: @options[:ecosystem])
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
if changes.empty?
|
|
47
|
+
puts "No dependency changes in #{commit.short_sha}"
|
|
48
|
+
return
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
if @options[:format] == "json"
|
|
52
|
+
output_json(commit, changes)
|
|
53
|
+
else
|
|
54
|
+
output_text(commit, changes)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def output_text(commit, changes)
|
|
59
|
+
puts "Commit: #{commit.short_sha} #{commit.message&.lines&.first&.strip}"
|
|
60
|
+
puts "Author: #{commit.author_name} <#{commit.author_email}>"
|
|
61
|
+
puts "Date: #{commit.committed_at.strftime("%Y-%m-%d")}"
|
|
62
|
+
puts
|
|
63
|
+
|
|
64
|
+
added = changes.select { |c| c.change_type == "added" }
|
|
65
|
+
modified = changes.select { |c| c.change_type == "modified" }
|
|
66
|
+
removed = changes.select { |c| c.change_type == "removed" }
|
|
67
|
+
|
|
68
|
+
if added.any?
|
|
69
|
+
puts "Added:"
|
|
70
|
+
added.each do |change|
|
|
71
|
+
puts " #{change.name} #{change.requirement} (#{change.ecosystem}, #{change.manifest.path})"
|
|
72
|
+
end
|
|
73
|
+
puts
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
if modified.any?
|
|
77
|
+
puts "Modified:"
|
|
78
|
+
modified.each do |change|
|
|
79
|
+
puts " #{change.name} #{change.previous_requirement} -> #{change.requirement} (#{change.ecosystem}, #{change.manifest.path})"
|
|
80
|
+
end
|
|
81
|
+
puts
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
if removed.any?
|
|
85
|
+
puts "Removed:"
|
|
86
|
+
removed.each do |change|
|
|
87
|
+
puts " #{change.name} #{change.requirement} (#{change.ecosystem}, #{change.manifest.path})"
|
|
88
|
+
end
|
|
89
|
+
puts
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def output_json(commit, changes)
|
|
94
|
+
require "json"
|
|
95
|
+
|
|
96
|
+
data = {
|
|
97
|
+
commit: {
|
|
98
|
+
sha: commit.sha,
|
|
99
|
+
short_sha: commit.short_sha,
|
|
100
|
+
message: commit.message&.lines&.first&.strip,
|
|
101
|
+
author_name: commit.author_name,
|
|
102
|
+
author_email: commit.author_email,
|
|
103
|
+
date: commit.committed_at.iso8601
|
|
104
|
+
},
|
|
105
|
+
changes: changes.map do |change|
|
|
106
|
+
{
|
|
107
|
+
name: change.name,
|
|
108
|
+
change_type: change.change_type,
|
|
109
|
+
requirement: change.requirement,
|
|
110
|
+
previous_requirement: change.previous_requirement,
|
|
111
|
+
ecosystem: change.ecosystem,
|
|
112
|
+
manifest: change.manifest.path
|
|
113
|
+
}
|
|
114
|
+
end
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
puts JSON.pretty_generate(data)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def find_or_create_commit(repo, sha)
|
|
121
|
+
commit = Models::Commit.find_by(sha: sha) ||
|
|
122
|
+
Models::Commit.where("sha LIKE ?", "#{sha}%").first
|
|
123
|
+
return commit if commit
|
|
124
|
+
|
|
125
|
+
rugged_commit = repo.lookup(sha)
|
|
126
|
+
return nil unless rugged_commit
|
|
127
|
+
|
|
128
|
+
Models::Commit.create!(
|
|
129
|
+
sha: rugged_commit.oid,
|
|
130
|
+
message: rugged_commit.message,
|
|
131
|
+
author_name: rugged_commit.author[:name],
|
|
132
|
+
author_email: rugged_commit.author[:email],
|
|
133
|
+
committed_at: rugged_commit.time,
|
|
134
|
+
has_dependency_changes: false
|
|
135
|
+
)
|
|
136
|
+
rescue Rugged::OdbError
|
|
137
|
+
nil
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def parse_options
|
|
141
|
+
options = {}
|
|
142
|
+
|
|
143
|
+
parser = OptionParser.new do |opts|
|
|
144
|
+
opts.banner = "Usage: git pkgs show [commit] [options]"
|
|
145
|
+
|
|
146
|
+
opts.on("-e", "--ecosystem=NAME", "Filter by ecosystem") do |v|
|
|
147
|
+
options[:ecosystem] = v
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
opts.on("-f", "--format=FORMAT", "Output format (text, json)") do |v|
|
|
151
|
+
options[:format] = v
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
opts.on("-h", "--help", "Show this help") do
|
|
155
|
+
puts opts
|
|
156
|
+
exit
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
parser.parse!(@args)
|
|
161
|
+
options
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
@@ -22,19 +22,26 @@ module Git
|
|
|
22
22
|
branch_name = @options[:branch] || repo.default_branch
|
|
23
23
|
branch = Models::Branch.find_by(name: branch_name)
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if @options[:format] == "json"
|
|
28
|
-
require "json"
|
|
29
|
-
puts JSON.pretty_generate(data)
|
|
25
|
+
if @options[:by_author]
|
|
26
|
+
output_by_author
|
|
30
27
|
else
|
|
31
|
-
|
|
28
|
+
data = collect_stats(branch, branch_name)
|
|
29
|
+
|
|
30
|
+
if @options[:format] == "json"
|
|
31
|
+
require "json"
|
|
32
|
+
puts JSON.pretty_generate(data)
|
|
33
|
+
else
|
|
34
|
+
output_text(data)
|
|
35
|
+
end
|
|
32
36
|
end
|
|
33
37
|
end
|
|
34
38
|
|
|
35
39
|
def collect_stats(branch, branch_name)
|
|
40
|
+
ecosystem = @options[:ecosystem]
|
|
41
|
+
|
|
36
42
|
data = {
|
|
37
43
|
branch: branch_name,
|
|
44
|
+
ecosystem: ecosystem,
|
|
38
45
|
commits_analyzed: branch&.commits&.count || 0,
|
|
39
46
|
commits_with_changes: branch&.commits&.where(has_dependency_changes: true)&.count || 0,
|
|
40
47
|
current_dependencies: {},
|
|
@@ -46,6 +53,7 @@ module Git
|
|
|
46
53
|
if branch&.last_analyzed_sha
|
|
47
54
|
current_commit = Models::Commit.find_by(sha: branch.last_analyzed_sha)
|
|
48
55
|
snapshots = current_commit&.dependency_snapshots || []
|
|
56
|
+
snapshots = snapshots.where(ecosystem: ecosystem) if ecosystem
|
|
49
57
|
|
|
50
58
|
data[:current_dependencies] = {
|
|
51
59
|
total: snapshots.count,
|
|
@@ -54,22 +62,28 @@ module Git
|
|
|
54
62
|
}
|
|
55
63
|
end
|
|
56
64
|
|
|
65
|
+
changes = Models::DependencyChange.all
|
|
66
|
+
changes = changes.where(ecosystem: ecosystem) if ecosystem
|
|
67
|
+
|
|
57
68
|
data[:changes] = {
|
|
58
|
-
total:
|
|
59
|
-
by_type:
|
|
69
|
+
total: changes.count,
|
|
70
|
+
by_type: changes.group(:change_type).count
|
|
60
71
|
}
|
|
61
72
|
|
|
62
|
-
most_changed =
|
|
73
|
+
most_changed = changes
|
|
63
74
|
.group(:name, :ecosystem)
|
|
64
75
|
.order("count_all DESC")
|
|
65
76
|
.limit(10)
|
|
66
77
|
.count
|
|
67
78
|
|
|
68
|
-
data[:most_changed] = most_changed.map do |(name,
|
|
69
|
-
{ name: name, ecosystem:
|
|
79
|
+
data[:most_changed] = most_changed.map do |(name, eco), count|
|
|
80
|
+
{ name: name, ecosystem: eco, changes: count }
|
|
70
81
|
end
|
|
71
82
|
|
|
72
|
-
|
|
83
|
+
manifests = Models::Manifest.all
|
|
84
|
+
manifests = manifests.where(ecosystem: ecosystem) if ecosystem
|
|
85
|
+
|
|
86
|
+
data[:manifests] = manifests.map do |manifest|
|
|
73
87
|
{ path: manifest.path, ecosystem: manifest.ecosystem, changes: manifest.dependency_changes.count }
|
|
74
88
|
end
|
|
75
89
|
|
|
@@ -82,6 +96,7 @@ module Git
|
|
|
82
96
|
puts
|
|
83
97
|
|
|
84
98
|
puts "Branch: #{data[:branch]}"
|
|
99
|
+
puts "Ecosystem: #{data[:ecosystem]}" if data[:ecosystem]
|
|
85
100
|
puts "Commits analyzed: #{data[:commits_analyzed]}"
|
|
86
101
|
puts "Commits with changes: #{data[:commits_with_changes]}"
|
|
87
102
|
puts
|
|
@@ -128,6 +143,38 @@ module Git
|
|
|
128
143
|
end
|
|
129
144
|
end
|
|
130
145
|
|
|
146
|
+
def output_by_author
|
|
147
|
+
changes = Models::DependencyChange
|
|
148
|
+
.joins(:commit)
|
|
149
|
+
.where(change_type: "added")
|
|
150
|
+
|
|
151
|
+
changes = changes.where(ecosystem: @options[:ecosystem]) if @options[:ecosystem]
|
|
152
|
+
|
|
153
|
+
counts = changes
|
|
154
|
+
.group("commits.author_name")
|
|
155
|
+
.order("count_all DESC")
|
|
156
|
+
.limit(@options[:limit] || 20)
|
|
157
|
+
.count
|
|
158
|
+
|
|
159
|
+
if counts.empty?
|
|
160
|
+
puts "No dependency additions found"
|
|
161
|
+
return
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
if @options[:format] == "json"
|
|
165
|
+
require "json"
|
|
166
|
+
data = counts.map { |name, count| { author: name, added: count } }
|
|
167
|
+
puts JSON.pretty_generate(data)
|
|
168
|
+
else
|
|
169
|
+
puts "Dependencies Added by Author"
|
|
170
|
+
puts "=" * 40
|
|
171
|
+
puts
|
|
172
|
+
counts.each do |name, count|
|
|
173
|
+
puts " #{count.to_s.rjust(4)} #{name}"
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
131
178
|
def parse_options
|
|
132
179
|
options = {}
|
|
133
180
|
|
|
@@ -138,10 +185,22 @@ module Git
|
|
|
138
185
|
options[:branch] = v
|
|
139
186
|
end
|
|
140
187
|
|
|
188
|
+
opts.on("-e", "--ecosystem=NAME", "Filter by ecosystem") do |v|
|
|
189
|
+
options[:ecosystem] = v
|
|
190
|
+
end
|
|
191
|
+
|
|
141
192
|
opts.on("-f", "--format=FORMAT", "Output format (text, json)") do |v|
|
|
142
193
|
options[:format] = v
|
|
143
194
|
end
|
|
144
195
|
|
|
196
|
+
opts.on("--by-author", "Show dependencies added by author") do
|
|
197
|
+
options[:by_author] = true
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
opts.on("-n", "--limit=N", Integer, "Limit results (default: 20)") do |v|
|
|
201
|
+
options[:limit] = v
|
|
202
|
+
end
|
|
203
|
+
|
|
145
204
|
opts.on("-h", "--help", "Show this help") do
|
|
146
205
|
puts opts
|
|
147
206
|
exit
|
data/lib/git/pkgs/version.rb
CHANGED
data/lib/git/pkgs.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: git-pkgs
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew Nesbitt
|
|
@@ -98,6 +98,7 @@ files:
|
|
|
98
98
|
- lib/git/pkgs/commands/list.rb
|
|
99
99
|
- lib/git/pkgs/commands/outdated.rb
|
|
100
100
|
- lib/git/pkgs/commands/search.rb
|
|
101
|
+
- lib/git/pkgs/commands/show.rb
|
|
101
102
|
- lib/git/pkgs/commands/stats.rb
|
|
102
103
|
- lib/git/pkgs/commands/tree.rb
|
|
103
104
|
- lib/git/pkgs/commands/update.rb
|