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
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Git
|
|
4
|
+
module Pkgs
|
|
5
|
+
module Commands
|
|
6
|
+
class Schema
|
|
7
|
+
include Output
|
|
8
|
+
|
|
9
|
+
FORMATS = %w[text sql json markdown].freeze
|
|
10
|
+
|
|
11
|
+
def initialize(args)
|
|
12
|
+
@args = args
|
|
13
|
+
@options = parse_options
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def run
|
|
17
|
+
repo = Repository.new
|
|
18
|
+
require_database(repo)
|
|
19
|
+
|
|
20
|
+
Database.connect(repo.git_dir)
|
|
21
|
+
tables = fetch_schema
|
|
22
|
+
|
|
23
|
+
case @options[:format]
|
|
24
|
+
when "sql"
|
|
25
|
+
output_sql(tables)
|
|
26
|
+
when "json"
|
|
27
|
+
output_json(tables)
|
|
28
|
+
when "markdown"
|
|
29
|
+
output_markdown(tables)
|
|
30
|
+
else
|
|
31
|
+
output_text(tables)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def fetch_schema
|
|
36
|
+
conn = ActiveRecord::Base.connection
|
|
37
|
+
tables = {}
|
|
38
|
+
|
|
39
|
+
conn.tables.sort.each do |table_name|
|
|
40
|
+
next if table_name == "ar_internal_metadata"
|
|
41
|
+
next if table_name == "schema_migrations"
|
|
42
|
+
|
|
43
|
+
columns = conn.columns(table_name).map do |col|
|
|
44
|
+
{
|
|
45
|
+
name: col.name,
|
|
46
|
+
type: col.type,
|
|
47
|
+
sql_type: col.sql_type,
|
|
48
|
+
null: col.null,
|
|
49
|
+
default: col.default
|
|
50
|
+
}
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
indexes = conn.indexes(table_name).map do |idx|
|
|
54
|
+
{
|
|
55
|
+
name: idx.name,
|
|
56
|
+
columns: idx.columns,
|
|
57
|
+
unique: idx.unique
|
|
58
|
+
}
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
tables[table_name] = { columns: columns, indexes: indexes }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
tables
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def output_text(tables)
|
|
68
|
+
tables.each do |table_name, info|
|
|
69
|
+
puts "#{table_name}"
|
|
70
|
+
puts "-" * table_name.length
|
|
71
|
+
|
|
72
|
+
info[:columns].each do |col|
|
|
73
|
+
nullable = col[:null] ? "NULL" : "NOT NULL"
|
|
74
|
+
default = col[:default] ? " DEFAULT #{col[:default]}" : ""
|
|
75
|
+
puts " #{col[:name].ljust(25)} #{col[:sql_type].ljust(15)} #{nullable}#{default}"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
if info[:indexes].any?
|
|
79
|
+
puts
|
|
80
|
+
puts " Indexes:"
|
|
81
|
+
info[:indexes].each do |idx|
|
|
82
|
+
unique = idx[:unique] ? "UNIQUE " : ""
|
|
83
|
+
puts " #{unique}#{idx[:name]} (#{idx[:columns].join(', ')})"
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
puts
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def output_sql(tables)
|
|
92
|
+
conn = ActiveRecord::Base.connection
|
|
93
|
+
|
|
94
|
+
tables.each do |table_name, info|
|
|
95
|
+
sql = conn.execute("SELECT sql FROM sqlite_master WHERE type='table' AND name='#{table_name}'").first
|
|
96
|
+
puts sql["sql"] + ";" if sql
|
|
97
|
+
puts
|
|
98
|
+
|
|
99
|
+
info[:indexes].each do |idx|
|
|
100
|
+
idx_sql = conn.execute("SELECT sql FROM sqlite_master WHERE type='index' AND name='#{idx[:name]}'").first
|
|
101
|
+
puts idx_sql["sql"] + ";" if idx_sql && idx_sql["sql"]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
puts if info[:indexes].any?
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def output_json(tables)
|
|
109
|
+
require "json"
|
|
110
|
+
puts JSON.pretty_generate(tables)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def output_markdown(tables)
|
|
114
|
+
tables.each do |table_name, info|
|
|
115
|
+
puts "## #{table_name}"
|
|
116
|
+
puts
|
|
117
|
+
puts "| Column | Type | Nullable | Default |"
|
|
118
|
+
puts "|--------|------|----------|---------|"
|
|
119
|
+
|
|
120
|
+
info[:columns].each do |col|
|
|
121
|
+
nullable = col[:null] ? "Yes" : "No"
|
|
122
|
+
default = col[:default] || ""
|
|
123
|
+
puts "| #{col[:name]} | #{col[:sql_type]} | #{nullable} | #{default} |"
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
if info[:indexes].any?
|
|
127
|
+
puts
|
|
128
|
+
puts "**Indexes:**"
|
|
129
|
+
info[:indexes].each do |idx|
|
|
130
|
+
unique = idx[:unique] ? " (unique)" : ""
|
|
131
|
+
puts "- `#{idx[:name]}`#{unique}: #{idx[:columns].join(', ')}"
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
puts
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def parse_options
|
|
140
|
+
options = { format: "text" }
|
|
141
|
+
|
|
142
|
+
parser = OptionParser.new do |opts|
|
|
143
|
+
opts.banner = "Usage: git pkgs schema [options]"
|
|
144
|
+
|
|
145
|
+
opts.on("--format=FORMAT", FORMATS, "Output format: #{FORMATS.join(', ')} (default: text)") do |v|
|
|
146
|
+
options[:format] = v
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
opts.on("-h", "--help", "Show this help") do
|
|
150
|
+
puts opts
|
|
151
|
+
exit
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
parser.parse!(@args)
|
|
156
|
+
options
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
@@ -4,6 +4,8 @@ module Git
|
|
|
4
4
|
module Pkgs
|
|
5
5
|
module Commands
|
|
6
6
|
class Search
|
|
7
|
+
include Output
|
|
8
|
+
|
|
7
9
|
def initialize(args)
|
|
8
10
|
@args = args
|
|
9
11
|
@options = parse_options
|
|
@@ -12,17 +14,10 @@ module Git
|
|
|
12
14
|
def run
|
|
13
15
|
pattern = @args.first
|
|
14
16
|
|
|
15
|
-
unless pattern
|
|
16
|
-
$stderr.puts "Usage: git pkgs search <pattern>"
|
|
17
|
-
exit 1
|
|
18
|
-
end
|
|
17
|
+
error "Usage: git pkgs search <pattern>" unless pattern
|
|
19
18
|
|
|
20
19
|
repo = Repository.new
|
|
21
|
-
|
|
22
|
-
unless Database.exists?(repo.git_dir)
|
|
23
|
-
$stderr.puts "Database not initialized. Run 'git pkgs init' first."
|
|
24
|
-
exit 1
|
|
25
|
-
end
|
|
20
|
+
require_database(repo)
|
|
26
21
|
|
|
27
22
|
Database.connect(repo.git_dir)
|
|
28
23
|
|
|
@@ -43,14 +38,14 @@ module Git
|
|
|
43
38
|
matches = query.distinct.pluck(:name, :ecosystem)
|
|
44
39
|
|
|
45
40
|
if matches.empty?
|
|
46
|
-
|
|
41
|
+
empty_result "No dependencies found matching '#{pattern}'"
|
|
47
42
|
return
|
|
48
43
|
end
|
|
49
44
|
|
|
50
45
|
if @options[:format] == "json"
|
|
51
46
|
output_json(matches, pattern)
|
|
52
47
|
else
|
|
53
|
-
output_text(matches, pattern)
|
|
48
|
+
paginate { output_text(matches, pattern) }
|
|
54
49
|
end
|
|
55
50
|
end
|
|
56
51
|
|
|
@@ -137,6 +132,10 @@ module Git
|
|
|
137
132
|
options[:format] = v
|
|
138
133
|
end
|
|
139
134
|
|
|
135
|
+
opts.on("--no-pager", "Do not pipe output into a pager") do
|
|
136
|
+
options[:no_pager] = true
|
|
137
|
+
end
|
|
138
|
+
|
|
140
139
|
opts.on("-h", "--help", "Show this help") do
|
|
141
140
|
puts opts
|
|
142
141
|
exit
|
|
@@ -4,6 +4,8 @@ module Git
|
|
|
4
4
|
module Pkgs
|
|
5
5
|
module Commands
|
|
6
6
|
class Show
|
|
7
|
+
include Output
|
|
8
|
+
|
|
7
9
|
def initialize(args)
|
|
8
10
|
@args = args
|
|
9
11
|
@options = parse_options
|
|
@@ -13,27 +15,15 @@ module Git
|
|
|
13
15
|
ref = @args.shift || "HEAD"
|
|
14
16
|
|
|
15
17
|
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
|
|
18
|
+
require_database(repo)
|
|
21
19
|
|
|
22
20
|
Database.connect(repo.git_dir)
|
|
23
21
|
|
|
24
22
|
sha = repo.rev_parse(ref)
|
|
25
|
-
|
|
26
|
-
unless sha
|
|
27
|
-
$stderr.puts "Could not resolve '#{ref}'"
|
|
28
|
-
exit 1
|
|
29
|
-
end
|
|
23
|
+
error "Could not resolve '#{ref}'" unless sha
|
|
30
24
|
|
|
31
25
|
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
|
|
26
|
+
error "Commit '#{sha[0..7]}' not found" unless commit
|
|
37
27
|
|
|
38
28
|
changes = Models::DependencyChange
|
|
39
29
|
.includes(:commit, :manifest)
|
|
@@ -44,14 +34,14 @@ module Git
|
|
|
44
34
|
end
|
|
45
35
|
|
|
46
36
|
if changes.empty?
|
|
47
|
-
|
|
37
|
+
empty_result "No dependency changes in #{commit.short_sha}"
|
|
48
38
|
return
|
|
49
39
|
end
|
|
50
40
|
|
|
51
41
|
if @options[:format] == "json"
|
|
52
42
|
output_json(commit, changes)
|
|
53
43
|
else
|
|
54
|
-
output_text(commit, changes)
|
|
44
|
+
paginate { output_text(commit, changes) }
|
|
55
45
|
end
|
|
56
46
|
end
|
|
57
47
|
|
|
@@ -66,25 +56,25 @@ module Git
|
|
|
66
56
|
removed = changes.select { |c| c.change_type == "removed" }
|
|
67
57
|
|
|
68
58
|
if added.any?
|
|
69
|
-
puts "Added:"
|
|
59
|
+
puts Color.green("Added:")
|
|
70
60
|
added.each do |change|
|
|
71
|
-
puts " #{change.name} #{change.requirement} (#{change.ecosystem}, #{change.manifest.path})"
|
|
61
|
+
puts Color.green(" + #{change.name} #{change.requirement} (#{change.ecosystem}, #{change.manifest.path})")
|
|
72
62
|
end
|
|
73
63
|
puts
|
|
74
64
|
end
|
|
75
65
|
|
|
76
66
|
if modified.any?
|
|
77
|
-
puts "Modified:"
|
|
67
|
+
puts Color.yellow("Modified:")
|
|
78
68
|
modified.each do |change|
|
|
79
|
-
puts " #{change.name} #{change.previous_requirement} -> #{change.requirement} (#{change.ecosystem}, #{change.manifest.path})"
|
|
69
|
+
puts Color.yellow(" ~ #{change.name} #{change.previous_requirement} -> #{change.requirement} (#{change.ecosystem}, #{change.manifest.path})")
|
|
80
70
|
end
|
|
81
71
|
puts
|
|
82
72
|
end
|
|
83
73
|
|
|
84
74
|
if removed.any?
|
|
85
|
-
puts "Removed:"
|
|
75
|
+
puts Color.red("Removed:")
|
|
86
76
|
removed.each do |change|
|
|
87
|
-
puts " #{change.name} #{change.requirement} (#{change.ecosystem}, #{change.manifest.path})"
|
|
77
|
+
puts Color.red(" - #{change.name} #{change.requirement} (#{change.ecosystem}, #{change.manifest.path})")
|
|
88
78
|
end
|
|
89
79
|
puts
|
|
90
80
|
end
|
|
@@ -151,6 +141,10 @@ module Git
|
|
|
151
141
|
options[:format] = v
|
|
152
142
|
end
|
|
153
143
|
|
|
144
|
+
opts.on("--no-pager", "Do not pipe output into a pager") do
|
|
145
|
+
options[:no_pager] = true
|
|
146
|
+
end
|
|
147
|
+
|
|
154
148
|
opts.on("-h", "--help", "Show this help") do
|
|
155
149
|
puts opts
|
|
156
150
|
exit
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
module Git
|
|
4
4
|
module Pkgs
|
|
5
5
|
module Commands
|
|
6
|
-
class
|
|
6
|
+
class Stale
|
|
7
|
+
include Output
|
|
8
|
+
|
|
7
9
|
def initialize(args)
|
|
8
10
|
@args = args
|
|
9
11
|
@options = parse_options
|
|
@@ -11,21 +13,14 @@ 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
|
branch_name = @options[:branch] || repo.default_branch
|
|
23
21
|
branch = Models::Branch.find_by(name: branch_name)
|
|
24
22
|
|
|
25
|
-
unless branch&.last_analyzed_sha
|
|
26
|
-
$stderr.puts "No analysis found for branch '#{branch_name}'"
|
|
27
|
-
exit 1
|
|
28
|
-
end
|
|
23
|
+
error "No analysis found for branch '#{branch_name}'" unless branch&.last_analyzed_sha
|
|
29
24
|
|
|
30
25
|
current_commit = Models::Commit.find_by(sha: branch.last_analyzed_sha)
|
|
31
26
|
snapshots = current_commit&.dependency_snapshots&.includes(:manifest) || []
|
|
@@ -35,7 +30,7 @@ module Git
|
|
|
35
30
|
end
|
|
36
31
|
|
|
37
32
|
if snapshots.empty?
|
|
38
|
-
|
|
33
|
+
empty_result "No dependencies found"
|
|
39
34
|
return
|
|
40
35
|
end
|
|
41
36
|
|
|
@@ -72,10 +67,14 @@ module Git
|
|
|
72
67
|
end
|
|
73
68
|
|
|
74
69
|
if outdated_data.empty?
|
|
75
|
-
|
|
70
|
+
empty_result "All dependencies have been updated recently"
|
|
76
71
|
return
|
|
77
72
|
end
|
|
78
73
|
|
|
74
|
+
paginate { output_text(outdated_data) }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def output_text(outdated_data)
|
|
79
78
|
puts "Dependencies by last update:"
|
|
80
79
|
puts
|
|
81
80
|
|
|
@@ -93,7 +92,7 @@ module Git
|
|
|
93
92
|
options = {}
|
|
94
93
|
|
|
95
94
|
parser = OptionParser.new do |opts|
|
|
96
|
-
opts.banner = "Usage: git pkgs
|
|
95
|
+
opts.banner = "Usage: git pkgs stale [options]"
|
|
97
96
|
|
|
98
97
|
opts.on("-e", "--ecosystem=NAME", "Filter by ecosystem") do |v|
|
|
99
98
|
options[:ecosystem] = v
|
|
@@ -107,6 +106,10 @@ module Git
|
|
|
107
106
|
options[:days] = v
|
|
108
107
|
end
|
|
109
108
|
|
|
109
|
+
opts.on("--no-pager", "Do not pipe output into a pager") do
|
|
110
|
+
options[:no_pager] = true
|
|
111
|
+
end
|
|
112
|
+
|
|
110
113
|
opts.on("-h", "--help", "Show this help") do
|
|
111
114
|
puts opts
|
|
112
115
|
exit
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "time"
|
|
4
|
+
|
|
3
5
|
module Git
|
|
4
6
|
module Pkgs
|
|
5
7
|
module Commands
|
|
6
8
|
class Stats
|
|
9
|
+
include Output
|
|
10
|
+
|
|
7
11
|
def initialize(args)
|
|
8
12
|
@args = args
|
|
9
13
|
@options = parse_options
|
|
@@ -11,11 +15,7 @@ module Git
|
|
|
11
15
|
|
|
12
16
|
def run
|
|
13
17
|
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
|
|
18
|
+
require_database(repo)
|
|
19
19
|
|
|
20
20
|
Database.connect(repo.git_dir)
|
|
21
21
|
|
|
@@ -31,19 +31,27 @@ module Git
|
|
|
31
31
|
require "json"
|
|
32
32
|
puts JSON.pretty_generate(data)
|
|
33
33
|
else
|
|
34
|
-
output_text(data)
|
|
34
|
+
paginate { output_text(data) }
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
def collect_stats(branch, branch_name)
|
|
40
40
|
ecosystem = @options[:ecosystem]
|
|
41
|
+
since_time = @options[:since] ? parse_time(@options[:since]) : nil
|
|
42
|
+
until_time = @options[:until] ? parse_time(@options[:until]) : nil
|
|
43
|
+
|
|
44
|
+
commits = branch&.commits || Models::Commit.none
|
|
45
|
+
commits = commits.where("committed_at >= ?", since_time) if since_time
|
|
46
|
+
commits = commits.where("committed_at <= ?", until_time) if until_time
|
|
41
47
|
|
|
42
48
|
data = {
|
|
43
49
|
branch: branch_name,
|
|
44
50
|
ecosystem: ecosystem,
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
since: @options[:since],
|
|
52
|
+
until: @options[:until],
|
|
53
|
+
commits_analyzed: commits.count,
|
|
54
|
+
commits_with_changes: commits.where(has_dependency_changes: true).count,
|
|
47
55
|
current_dependencies: {},
|
|
48
56
|
changes: {},
|
|
49
57
|
most_changed: [],
|
|
@@ -62,8 +70,10 @@ module Git
|
|
|
62
70
|
}
|
|
63
71
|
end
|
|
64
72
|
|
|
65
|
-
changes = Models::DependencyChange.
|
|
73
|
+
changes = Models::DependencyChange.joins(:commit)
|
|
66
74
|
changes = changes.where(ecosystem: ecosystem) if ecosystem
|
|
75
|
+
changes = changes.where("commits.committed_at >= ?", since_time) if since_time
|
|
76
|
+
changes = changes.where("commits.committed_at <= ?", until_time) if until_time
|
|
67
77
|
|
|
68
78
|
data[:changes] = {
|
|
69
79
|
total: changes.count,
|
|
@@ -84,7 +94,10 @@ module Git
|
|
|
84
94
|
manifests = manifests.where(ecosystem: ecosystem) if ecosystem
|
|
85
95
|
|
|
86
96
|
data[:manifests] = manifests.map do |manifest|
|
|
87
|
-
|
|
97
|
+
manifest_changes = manifest.dependency_changes.joins(:commit)
|
|
98
|
+
manifest_changes = manifest_changes.where("commits.committed_at >= ?", since_time) if since_time
|
|
99
|
+
manifest_changes = manifest_changes.where("commits.committed_at <= ?", until_time) if until_time
|
|
100
|
+
{ path: manifest.path, ecosystem: manifest.ecosystem, changes: manifest_changes.count }
|
|
88
101
|
end
|
|
89
102
|
|
|
90
103
|
data
|
|
@@ -97,6 +110,8 @@ module Git
|
|
|
97
110
|
|
|
98
111
|
puts "Branch: #{data[:branch]}"
|
|
99
112
|
puts "Ecosystem: #{data[:ecosystem]}" if data[:ecosystem]
|
|
113
|
+
puts "Since: #{data[:since]}" if data[:since]
|
|
114
|
+
puts "Until: #{data[:until]}" if data[:until]
|
|
100
115
|
puts "Commits analyzed: #{data[:commits_analyzed]}"
|
|
101
116
|
puts "Commits with changes: #{data[:commits_with_changes]}"
|
|
102
117
|
puts
|
|
@@ -144,11 +159,16 @@ module Git
|
|
|
144
159
|
end
|
|
145
160
|
|
|
146
161
|
def output_by_author
|
|
162
|
+
since_time = @options[:since] ? parse_time(@options[:since]) : nil
|
|
163
|
+
until_time = @options[:until] ? parse_time(@options[:until]) : nil
|
|
164
|
+
|
|
147
165
|
changes = Models::DependencyChange
|
|
148
166
|
.joins(:commit)
|
|
149
167
|
.where(change_type: "added")
|
|
150
168
|
|
|
151
169
|
changes = changes.where(ecosystem: @options[:ecosystem]) if @options[:ecosystem]
|
|
170
|
+
changes = changes.where("commits.committed_at >= ?", since_time) if since_time
|
|
171
|
+
changes = changes.where("commits.committed_at <= ?", until_time) if until_time
|
|
152
172
|
|
|
153
173
|
counts = changes
|
|
154
174
|
.group("commits.author_name")
|
|
@@ -157,7 +177,7 @@ module Git
|
|
|
157
177
|
.count
|
|
158
178
|
|
|
159
179
|
if counts.empty?
|
|
160
|
-
|
|
180
|
+
empty_result "No dependency additions found"
|
|
161
181
|
return
|
|
162
182
|
end
|
|
163
183
|
|
|
@@ -166,15 +186,25 @@ module Git
|
|
|
166
186
|
data = counts.map { |name, count| { author: name, added: count } }
|
|
167
187
|
puts JSON.pretty_generate(data)
|
|
168
188
|
else
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
189
|
+
paginate { output_by_author_text(counts) }
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def output_by_author_text(counts)
|
|
194
|
+
puts "Dependencies Added by Author"
|
|
195
|
+
puts "=" * 40
|
|
196
|
+
puts
|
|
197
|
+
counts.each do |name, count|
|
|
198
|
+
puts " #{count.to_s.rjust(4)} #{name}"
|
|
175
199
|
end
|
|
176
200
|
end
|
|
177
201
|
|
|
202
|
+
def parse_time(str)
|
|
203
|
+
Time.parse(str)
|
|
204
|
+
rescue ArgumentError
|
|
205
|
+
error "Invalid date format: #{str}"
|
|
206
|
+
end
|
|
207
|
+
|
|
178
208
|
def parse_options
|
|
179
209
|
options = {}
|
|
180
210
|
|
|
@@ -193,6 +223,14 @@ module Git
|
|
|
193
223
|
options[:format] = v
|
|
194
224
|
end
|
|
195
225
|
|
|
226
|
+
opts.on("--since=DATE", "Show changes after date") do |v|
|
|
227
|
+
options[:since] = v
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
opts.on("--until=DATE", "Show changes before date") do |v|
|
|
231
|
+
options[:until] = v
|
|
232
|
+
end
|
|
233
|
+
|
|
196
234
|
opts.on("--by-author", "Show dependencies added by author") do
|
|
197
235
|
options[:by_author] = true
|
|
198
236
|
end
|
|
@@ -201,6 +239,10 @@ module Git
|
|
|
201
239
|
options[:limit] = v
|
|
202
240
|
end
|
|
203
241
|
|
|
242
|
+
opts.on("--no-pager", "Do not pipe output into a pager") do
|
|
243
|
+
options[:no_pager] = true
|
|
244
|
+
end
|
|
245
|
+
|
|
204
246
|
opts.on("-h", "--help", "Show this help") do
|
|
205
247
|
puts opts
|
|
206
248
|
exit
|
|
@@ -4,6 +4,8 @@ module Git
|
|
|
4
4
|
module Pkgs
|
|
5
5
|
module Commands
|
|
6
6
|
class Tree
|
|
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
|
commit_sha = @options[:commit] || repo.head_sha
|
|
24
22
|
commit = find_commit_with_snapshot(commit_sha, repo)
|
|
25
23
|
|
|
26
|
-
unless commit
|
|
27
|
-
$stderr.puts "No dependency data found for commit #{commit_sha[0, 7]}"
|
|
28
|
-
exit 1
|
|
29
|
-
end
|
|
24
|
+
error "No dependency data found for commit #{commit_sha[0, 7]}" unless commit
|
|
30
25
|
|
|
31
26
|
# Get current snapshots
|
|
32
27
|
snapshots = commit.dependency_snapshots.includes(:manifest)
|
|
@@ -36,13 +31,17 @@ 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
|
|
|
43
38
|
# Group by manifest and build tree
|
|
44
39
|
grouped = snapshots.group_by { |s| s.manifest }
|
|
45
40
|
|
|
41
|
+
paginate { output_text(grouped, snapshots) }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def output_text(grouped, snapshots)
|
|
46
45
|
grouped.each do |manifest, deps|
|
|
47
46
|
puts "#{manifest.path} (#{manifest.ecosystem})"
|
|
48
47
|
puts
|
|
@@ -109,6 +108,10 @@ module Git
|
|
|
109
108
|
options[:branch] = v
|
|
110
109
|
end
|
|
111
110
|
|
|
111
|
+
opts.on("--no-pager", "Do not pipe output into a pager") do
|
|
112
|
+
options[:no_pager] = true
|
|
113
|
+
end
|
|
114
|
+
|
|
112
115
|
opts.on("-h", "--help", "Show this help") do
|
|
113
116
|
puts opts
|
|
114
117
|
exit
|