git-pkgs 0.5.0 → 0.6.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 +8 -0
- data/README.md +1 -3
- data/lib/git/pkgs/commands/blame.rb +4 -4
- data/lib/git/pkgs/commands/branch.rb +17 -18
- data/lib/git/pkgs/commands/diff.rb +10 -8
- data/lib/git/pkgs/commands/history.rb +12 -10
- data/lib/git/pkgs/commands/hooks.rb +8 -8
- data/lib/git/pkgs/commands/info.rb +4 -9
- data/lib/git/pkgs/commands/init.rb +11 -21
- data/lib/git/pkgs/commands/list.rb +15 -13
- data/lib/git/pkgs/commands/log.rb +13 -7
- data/lib/git/pkgs/commands/schema.rb +19 -22
- data/lib/git/pkgs/commands/search.rb +15 -11
- data/lib/git/pkgs/commands/show.rb +3 -1
- data/lib/git/pkgs/commands/stale.rb +4 -4
- data/lib/git/pkgs/commands/stats.rb +52 -33
- data/lib/git/pkgs/commands/tree.rb +14 -11
- data/lib/git/pkgs/commands/update.rb +15 -18
- data/lib/git/pkgs/commands/where.rb +5 -2
- data/lib/git/pkgs/commands/why.rb +1 -1
- data/lib/git/pkgs/config.rb +1 -16
- data/lib/git/pkgs/database.rb +143 -100
- data/lib/git/pkgs/models/branch.rb +4 -6
- data/lib/git/pkgs/models/branch_commit.rb +3 -5
- data/lib/git/pkgs/models/commit.rb +10 -9
- data/lib/git/pkgs/models/dependency_change.rb +23 -10
- data/lib/git/pkgs/models/dependency_snapshot.rb +18 -10
- data/lib/git/pkgs/models/manifest.rb +7 -9
- data/lib/git/pkgs/version.rb +1 -1
- metadata +8 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 456f37ab775f4c2262efc63bb71a3d9e9901a69dcd6b78f08a7eacbe1f3991d2
|
|
4
|
+
data.tar.gz: a4e20b318a4244ddebbd34710ffb75ba79c8e96d17183c646b10fbc07e57e0cc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6d2e71fa0e93fd21a9364f641bfab05d90c71cf76b5bd19b02cf03064d205070aa49ffe99780cda4d5004a9aabc27ea6c47692e28f2457ed6671af3b94e4661b
|
|
7
|
+
data.tar.gz: 8f3ce053d35962a58c66d70c00f3f0ba9510ebd8116ee01e388f6d967ce7f64cc20cf7e6b86aee9d03316a28a80a21836678e4c8e5b71f2b19c2c6a17294939a
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.6.0] - 2026-01-05
|
|
4
|
+
|
|
5
|
+
- Replace ActiveRecord with Sequel (~3x faster init, ~2x faster queries)
|
|
6
|
+
- `git pkgs stats` now shows top authors in default output
|
|
7
|
+
- Update ecosystems-bibliothecary to ~> 15.0 (~10x faster lockfile parsing)
|
|
8
|
+
- Fewer runtime dependencies
|
|
9
|
+
- Quieter output from `init` and `update` commands
|
|
10
|
+
|
|
3
11
|
## [0.5.0] - 2026-01-04
|
|
4
12
|
|
|
5
13
|
- `git pkgs init` now installs git hooks by default (use `--no-hooks` to skip)
|
data/README.md
CHANGED
|
@@ -429,9 +429,7 @@ Optimizations:
|
|
|
429
429
|
|
|
430
430
|
git-pkgs uses [ecosystems-bibliothecary](https://github.com/ecosyste-ms/bibliothecary) for parsing, supporting:
|
|
431
431
|
|
|
432
|
-
Actions, BentoML, Bower, Cargo, CocoaPods, Cog, Conda, CPAN, CRAN, Docker, Dub, DVC, Elm, Go, Haxelib, Homebrew, Julia, Maven, Meteor, MLflow, npm, NuGet, Ollama, Packagist, Pub, PyPI, RubyGems, Shards, Vcpkg
|
|
433
|
-
|
|
434
|
-
Some ecosystems require remote parsing services and are disabled by default: Carthage, Clojars, Hackage, Hex, SwiftPM. Enable with `git config --add pkgs.ecosystems <name>`.
|
|
432
|
+
Actions, BentoML, Bower, Cargo, Carthage, Clojars, CocoaPods, Cog, Conda, CPAN, CRAN, Docker, Dub, DVC, Elm, Go, Hackage, Haxelib, Hex, Homebrew, Julia, Maven, Meteor, MLflow, npm, NuGet, Ollama, Packagist, Pub, PyPI, RubyGems, Shards, SwiftPM, Vcpkg
|
|
435
433
|
|
|
436
434
|
SBOM formats (CycloneDX, SPDX) are not supported as they duplicate information from the actual lockfiles.
|
|
437
435
|
|
|
@@ -19,12 +19,12 @@ module Git
|
|
|
19
19
|
|
|
20
20
|
# Get current dependencies at the last analyzed commit
|
|
21
21
|
branch_name = @options[:branch] || repo.default_branch
|
|
22
|
-
branch = Models::Branch.
|
|
22
|
+
branch = Models::Branch.first(name: branch_name)
|
|
23
23
|
|
|
24
24
|
error "No analysis found for branch '#{branch_name}'. Run 'git pkgs init' first." unless branch&.last_analyzed_sha
|
|
25
25
|
|
|
26
|
-
current_commit = Models::Commit.
|
|
27
|
-
snapshots = current_commit&.dependency_snapshots&.
|
|
26
|
+
current_commit = Models::Commit.first(sha: branch.last_analyzed_sha)
|
|
27
|
+
snapshots = current_commit&.dependency_snapshots&.eager(:manifest) || []
|
|
28
28
|
|
|
29
29
|
if @options[:ecosystem]
|
|
30
30
|
snapshots = snapshots.where(ecosystem: @options[:ecosystem])
|
|
@@ -41,7 +41,7 @@ module Git
|
|
|
41
41
|
names = snapshots.map(&:name).uniq
|
|
42
42
|
|
|
43
43
|
all_added_changes = Models::DependencyChange
|
|
44
|
-
.
|
|
44
|
+
.eager(:commit)
|
|
45
45
|
.added
|
|
46
46
|
.where(manifest_id: manifest_ids, name: names)
|
|
47
47
|
.to_a
|
|
@@ -50,7 +50,7 @@ module Git
|
|
|
50
50
|
|
|
51
51
|
error "Branch '#{branch_name}' not found. Check 'git branch -a' for available branches." unless repo.branch_exists?(branch_name)
|
|
52
52
|
|
|
53
|
-
existing = Models::Branch.
|
|
53
|
+
existing = Models::Branch.first(name: branch_name)
|
|
54
54
|
if existing
|
|
55
55
|
info "Branch '#{branch_name}' already tracked (#{existing.commits.count} commits)"
|
|
56
56
|
info "Use 'git pkgs update' to refresh"
|
|
@@ -59,7 +59,7 @@ module Git
|
|
|
59
59
|
|
|
60
60
|
Database.optimize_for_bulk_writes
|
|
61
61
|
|
|
62
|
-
branch = Models::Branch.create
|
|
62
|
+
branch = Models::Branch.create(name: branch_name)
|
|
63
63
|
analyzer = Analyzer.new(repo)
|
|
64
64
|
|
|
65
65
|
info "Analyzing branch: #{branch_name}"
|
|
@@ -100,7 +100,7 @@ module Git
|
|
|
100
100
|
puts "Tracked branches:"
|
|
101
101
|
branches.each do |branch|
|
|
102
102
|
commit_count = branch.commits.count
|
|
103
|
-
dep_commits = branch.
|
|
103
|
+
dep_commits = branch.commits_dataset.where(has_dependency_changes: true).count
|
|
104
104
|
last_sha = branch.last_analyzed_sha&.slice(0, 7) || "none"
|
|
105
105
|
puts " #{branch.name}: #{commit_count} commits (#{dep_commits} with deps), last: #{last_sha}"
|
|
106
106
|
end
|
|
@@ -115,13 +115,13 @@ module Git
|
|
|
115
115
|
|
|
116
116
|
Database.connect(repo.git_dir)
|
|
117
117
|
|
|
118
|
-
branch = Models::Branch.
|
|
118
|
+
branch = Models::Branch.first(name: branch_name)
|
|
119
119
|
error "Branch '#{branch_name}' not tracked. Run 'git pkgs branch list' to see tracked branches." unless branch
|
|
120
120
|
|
|
121
121
|
# Only delete branch_commits, keep shared commits
|
|
122
122
|
count = branch.branch_commits.count
|
|
123
|
-
branch.
|
|
124
|
-
branch.
|
|
123
|
+
branch.branch_commits_dataset.delete
|
|
124
|
+
branch.delete
|
|
125
125
|
|
|
126
126
|
info "Removed branch '#{branch_name}' (#{count} branch-commit links)"
|
|
127
127
|
end
|
|
@@ -143,28 +143,27 @@ module Git
|
|
|
143
143
|
flush = lambda do
|
|
144
144
|
return if pending_commits.empty?
|
|
145
145
|
|
|
146
|
-
|
|
147
|
-
# Use
|
|
146
|
+
Database.db.transaction do
|
|
147
|
+
# Use insert with on_conflict for commits since they may already exist from other branches
|
|
148
148
|
if pending_commits.any?
|
|
149
|
-
Models::Commit.
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
)
|
|
149
|
+
Models::Commit.dataset
|
|
150
|
+
.insert_conflict(target: :sha, update: { has_dependency_changes: Sequel[:excluded][:has_dependency_changes] })
|
|
151
|
+
.multi_insert(pending_commits)
|
|
153
152
|
end
|
|
154
153
|
|
|
155
154
|
commit_ids = Models::Commit
|
|
156
155
|
.where(sha: pending_commits.map { |c| c[:sha] })
|
|
157
|
-
.
|
|
156
|
+
.select_hash(:sha, :id)
|
|
158
157
|
|
|
159
158
|
if pending_branch_commits.any?
|
|
160
159
|
branch_commit_records = pending_branch_commits.map do |bc|
|
|
161
160
|
{ branch_id: bc[:branch_id], commit_id: commit_ids[bc[:sha]], position: bc[:position] }
|
|
162
161
|
end
|
|
163
|
-
Models::BranchCommit.
|
|
162
|
+
Models::BranchCommit.dataset.multi_insert(branch_commit_records)
|
|
164
163
|
end
|
|
165
164
|
|
|
166
165
|
if pending_changes.any?
|
|
167
|
-
manifest_ids = Models::Manifest.
|
|
166
|
+
manifest_ids = Models::Manifest.select_hash(:path, :id)
|
|
168
167
|
change_records = pending_changes.map do |c|
|
|
169
168
|
{
|
|
170
169
|
commit_id: commit_ids[c[:sha]],
|
|
@@ -179,11 +178,11 @@ module Git
|
|
|
179
178
|
updated_at: now
|
|
180
179
|
}
|
|
181
180
|
end
|
|
182
|
-
Models::DependencyChange.
|
|
181
|
+
Models::DependencyChange.dataset.multi_insert(change_records)
|
|
183
182
|
end
|
|
184
183
|
|
|
185
184
|
if pending_snapshots.any?
|
|
186
|
-
manifest_ids ||= Models::Manifest.
|
|
185
|
+
manifest_ids ||= Models::Manifest.select_hash(:path, :id)
|
|
187
186
|
snapshot_records = pending_snapshots.map do |s|
|
|
188
187
|
{
|
|
189
188
|
commit_id: commit_ids[s[:sha]],
|
|
@@ -196,7 +195,7 @@ module Git
|
|
|
196
195
|
updated_at: now
|
|
197
196
|
}
|
|
198
197
|
end
|
|
199
|
-
Models::DependencySnapshot.
|
|
198
|
+
Models::DependencySnapshot.dataset.multi_insert(snapshot_records)
|
|
200
199
|
end
|
|
201
200
|
end
|
|
202
201
|
|
|
@@ -38,22 +38,24 @@ module Git
|
|
|
38
38
|
|
|
39
39
|
# Get all changes between the two commits
|
|
40
40
|
changes = Models::DependencyChange
|
|
41
|
-
.
|
|
42
|
-
.
|
|
43
|
-
.where
|
|
44
|
-
|
|
45
|
-
.order(
|
|
41
|
+
.eager(:commit, :manifest)
|
|
42
|
+
.join(:commits, id: :commit_id)
|
|
43
|
+
.where { Sequel[:commits][:committed_at] > from_commit.committed_at }
|
|
44
|
+
.where { Sequel[:commits][:committed_at] <= to_commit.committed_at }
|
|
45
|
+
.order(Sequel[:commits][:committed_at])
|
|
46
46
|
|
|
47
47
|
if @options[:ecosystem]
|
|
48
|
-
changes = changes.where(ecosystem
|
|
48
|
+
changes = changes.where(Sequel[:dependency_changes][:ecosystem] => @options[:ecosystem])
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
changes_list = changes.all
|
|
52
|
+
|
|
53
|
+
if changes_list.empty?
|
|
52
54
|
empty_result "No dependency changes between #{from_commit.short_sha} and #{to_commit.short_sha}"
|
|
53
55
|
return
|
|
54
56
|
end
|
|
55
57
|
|
|
56
|
-
paginate { output_text(from_commit, to_commit,
|
|
58
|
+
paginate { output_text(from_commit, to_commit, changes_list) }
|
|
57
59
|
end
|
|
58
60
|
|
|
59
61
|
def output_text(from_commit, to_commit, changes)
|
|
@@ -20,8 +20,8 @@ module Git
|
|
|
20
20
|
Database.connect(repo.git_dir)
|
|
21
21
|
|
|
22
22
|
changes = Models::DependencyChange
|
|
23
|
-
.
|
|
24
|
-
.order(
|
|
23
|
+
.eager_graph(:commit, :manifest)
|
|
24
|
+
.order(Sequel[:commit][:committed_at])
|
|
25
25
|
|
|
26
26
|
changes = changes.for_package(package_name) if package_name
|
|
27
27
|
|
|
@@ -31,32 +31,34 @@ module Git
|
|
|
31
31
|
|
|
32
32
|
if @options[:author]
|
|
33
33
|
author = @options[:author]
|
|
34
|
-
changes = changes.
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
changes = changes.where(
|
|
35
|
+
Sequel.like(Sequel[:commit][:author_name], "%#{author}%") |
|
|
36
|
+
Sequel.like(Sequel[:commit][:author_email], "%#{author}%")
|
|
37
37
|
)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
if @options[:since]
|
|
41
41
|
since_time = parse_time(@options[:since])
|
|
42
|
-
changes = changes.
|
|
42
|
+
changes = changes.where { Sequel[:commit][:committed_at] >= since_time }
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
if @options[:until]
|
|
46
46
|
until_time = parse_time(@options[:until])
|
|
47
|
-
changes = changes.
|
|
47
|
+
changes = changes.where { Sequel[:commit][:committed_at] <= until_time }
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
changes_list = changes.all
|
|
51
|
+
|
|
52
|
+
if changes_list.empty?
|
|
51
53
|
msg = package_name ? "No history found for '#{package_name}'" : "No dependency changes found"
|
|
52
54
|
empty_result msg
|
|
53
55
|
return
|
|
54
56
|
end
|
|
55
57
|
|
|
56
58
|
if @options[:format] == "json"
|
|
57
|
-
output_json(
|
|
59
|
+
output_json(changes_list)
|
|
58
60
|
else
|
|
59
|
-
paginate { output_text(
|
|
61
|
+
paginate { output_text(changes_list, package_name) }
|
|
60
62
|
end
|
|
61
63
|
end
|
|
62
64
|
|
|
@@ -31,32 +31,32 @@ module Git
|
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
def install_hooks(repo)
|
|
34
|
+
def install_hooks(repo, quiet: false)
|
|
35
35
|
hooks_dir = File.join(repo.git_dir, "hooks")
|
|
36
|
+
installed = []
|
|
36
37
|
|
|
37
38
|
HOOKS.each do |hook_name|
|
|
38
39
|
hook_path = File.join(hooks_dir, hook_name)
|
|
39
40
|
|
|
40
41
|
if File.exist?(hook_path)
|
|
41
42
|
content = File.read(hook_path)
|
|
42
|
-
if content.include?("git-pkgs")
|
|
43
|
-
info "Hook #{hook_name} already contains git-pkgs"
|
|
44
|
-
next
|
|
45
|
-
end
|
|
43
|
+
next if content.include?("git-pkgs")
|
|
46
44
|
|
|
47
45
|
File.open(hook_path, "a") do |f|
|
|
48
46
|
f.puts "\n# git-pkgs auto-update"
|
|
49
47
|
f.puts "git pkgs update 2>/dev/null || true"
|
|
50
48
|
end
|
|
51
|
-
|
|
49
|
+
installed << hook_name
|
|
52
50
|
else
|
|
53
51
|
File.write(hook_path, HOOK_SCRIPT)
|
|
54
52
|
File.chmod(0o755, hook_path)
|
|
55
|
-
|
|
53
|
+
installed << hook_name
|
|
56
54
|
end
|
|
57
55
|
end
|
|
58
56
|
|
|
59
|
-
|
|
57
|
+
return if quiet || installed.empty?
|
|
58
|
+
|
|
59
|
+
info "Installed hooks: #{installed.join(', ')}"
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
def uninstall_hooks(repo)
|
|
@@ -67,7 +67,7 @@ module Git
|
|
|
67
67
|
puts "-" * 40
|
|
68
68
|
total_dep_commits = Models::Commit.where(has_dependency_changes: true).count
|
|
69
69
|
snapshot_commits = Models::Commit
|
|
70
|
-
.
|
|
70
|
+
.join(:dependency_snapshots, commit_id: :id)
|
|
71
71
|
.distinct
|
|
72
72
|
.count
|
|
73
73
|
puts " Commits with dependency changes: #{total_dep_commits}"
|
|
@@ -102,8 +102,7 @@ module Git
|
|
|
102
102
|
|
|
103
103
|
all_ecosystems.each do |eco|
|
|
104
104
|
if Config.filter_ecosystem?(eco)
|
|
105
|
-
|
|
106
|
-
disabled_ecos << { name: eco, remote: remote }
|
|
105
|
+
disabled_ecos << eco
|
|
107
106
|
else
|
|
108
107
|
enabled_ecos << eco
|
|
109
108
|
end
|
|
@@ -119,10 +118,7 @@ module Git
|
|
|
119
118
|
puts
|
|
120
119
|
puts "Disabled:"
|
|
121
120
|
if disabled_ecos.any?
|
|
122
|
-
disabled_ecos.each
|
|
123
|
-
suffix = eco[:remote] ? " (remote)" : ""
|
|
124
|
-
puts " #{eco[:name]}#{suffix}"
|
|
125
|
-
end
|
|
121
|
+
disabled_ecos.each { |eco| puts " #{eco}" }
|
|
126
122
|
else
|
|
127
123
|
puts " (none)"
|
|
128
124
|
end
|
|
@@ -131,9 +127,8 @@ module Git
|
|
|
131
127
|
if filtering
|
|
132
128
|
puts "Filtering: only #{configured.join(', ')}"
|
|
133
129
|
else
|
|
134
|
-
puts "All
|
|
130
|
+
puts "All ecosystems enabled"
|
|
135
131
|
end
|
|
136
|
-
puts "Remote ecosystems require explicit opt-in"
|
|
137
132
|
puts "Configure with: git config --add pkgs.ecosystems <name>"
|
|
138
133
|
end
|
|
139
134
|
|
|
@@ -41,34 +41,24 @@ module Git
|
|
|
41
41
|
branch = Models::Branch.find_or_create(branch_name)
|
|
42
42
|
analyzer = Analyzer.new(repo)
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
print "Analyzing #{branch_name}..." unless Git::Pkgs.quiet
|
|
45
45
|
|
|
46
|
-
print "Loading commits..." unless Git::Pkgs.quiet
|
|
47
46
|
walker = repo.walk(branch_name, @options[:since])
|
|
48
47
|
commits = walker.to_a
|
|
49
48
|
total = commits.size
|
|
50
|
-
print "\rPrefetching diffs..." unless Git::Pkgs.quiet
|
|
51
49
|
repo.prefetch_blob_paths(commits)
|
|
52
|
-
print "\r#{' ' * 20}\r" unless Git::Pkgs.quiet
|
|
53
50
|
|
|
54
51
|
stats = bulk_process_commits(commits, branch, analyzer, total)
|
|
55
52
|
|
|
56
53
|
branch.update(last_analyzed_sha: repo.branch_target(branch_name))
|
|
57
54
|
|
|
58
|
-
print "\rCreating indexes...#{' ' * 20}" unless Git::Pkgs.quiet
|
|
59
55
|
Database.create_bulk_indexes
|
|
60
56
|
Database.optimize_for_reads
|
|
61
57
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
info "\rDone!#{' ' * 20}"
|
|
65
|
-
info "Analyzed #{total} commits"
|
|
66
|
-
info "Found #{stats[:dependency_commits]} commits with dependency changes"
|
|
67
|
-
info "Stored #{stats[:snapshots_stored]} snapshots (every #{snapshot_interval} changes)"
|
|
68
|
-
info "Blob cache: #{cache_stats[:cached_blobs]} unique blobs, #{cache_stats[:blobs_with_hits]} had cache hits"
|
|
58
|
+
info "\r#{' ' * 40}\rAnalyzed #{branch_name}: #{total} commits (#{stats[:dependency_commits]} with dependency changes)"
|
|
69
59
|
|
|
70
60
|
unless @options[:no_hooks]
|
|
71
|
-
Commands::Hooks.new([
|
|
61
|
+
Commands::Hooks.new([]).install_hooks(repo, quiet: true)
|
|
72
62
|
end
|
|
73
63
|
end
|
|
74
64
|
|
|
@@ -90,22 +80,22 @@ module Git
|
|
|
90
80
|
flush = lambda do
|
|
91
81
|
return if pending_commits.empty?
|
|
92
82
|
|
|
93
|
-
|
|
94
|
-
Models::Commit.
|
|
83
|
+
Database.db.transaction do
|
|
84
|
+
Models::Commit.dataset.multi_insert(pending_commits) if pending_commits.any?
|
|
95
85
|
|
|
96
86
|
commit_ids = Models::Commit
|
|
97
87
|
.where(sha: pending_commits.map { |c| c[:sha] })
|
|
98
|
-
.
|
|
88
|
+
.select_hash(:sha, :id)
|
|
99
89
|
|
|
100
90
|
if pending_branch_commits.any?
|
|
101
91
|
branch_commit_records = pending_branch_commits.map do |bc|
|
|
102
92
|
{ branch_id: bc[:branch_id], commit_id: commit_ids[bc[:sha]], position: bc[:position] }
|
|
103
93
|
end
|
|
104
|
-
Models::BranchCommit.
|
|
94
|
+
Models::BranchCommit.dataset.multi_insert(branch_commit_records)
|
|
105
95
|
end
|
|
106
96
|
|
|
107
97
|
if pending_changes.any?
|
|
108
|
-
manifest_ids = Models::Manifest.
|
|
98
|
+
manifest_ids = Models::Manifest.select_hash(:path, :id)
|
|
109
99
|
change_records = pending_changes.map do |c|
|
|
110
100
|
{
|
|
111
101
|
commit_id: commit_ids[c[:sha]],
|
|
@@ -120,11 +110,11 @@ module Git
|
|
|
120
110
|
updated_at: now
|
|
121
111
|
}
|
|
122
112
|
end
|
|
123
|
-
Models::DependencyChange.
|
|
113
|
+
Models::DependencyChange.dataset.multi_insert(change_records)
|
|
124
114
|
end
|
|
125
115
|
|
|
126
116
|
if pending_snapshots.any?
|
|
127
|
-
manifest_ids ||= Models::Manifest.
|
|
117
|
+
manifest_ids ||= Models::Manifest.select_hash(:path, :id)
|
|
128
118
|
snapshot_records = pending_snapshots.map do |s|
|
|
129
119
|
{
|
|
130
120
|
commit_id: commit_ids[s[:sha]],
|
|
@@ -137,7 +127,7 @@ module Git
|
|
|
137
127
|
updated_at: now
|
|
138
128
|
}
|
|
139
129
|
end
|
|
140
|
-
Models::DependencySnapshot.
|
|
130
|
+
Models::DependencySnapshot.dataset.multi_insert(snapshot_records)
|
|
141
131
|
end
|
|
142
132
|
end
|
|
143
133
|
|
|
@@ -18,7 +18,7 @@ module Git
|
|
|
18
18
|
Database.connect(repo.git_dir)
|
|
19
19
|
|
|
20
20
|
commit_sha = @options[:commit] || repo.head_sha
|
|
21
|
-
target_commit = Models::Commit.
|
|
21
|
+
target_commit = Models::Commit.first(sha: commit_sha)
|
|
22
22
|
|
|
23
23
|
error "Commit #{commit_sha[0, 7]} not in database. Run 'git pkgs update' to index new commits." unless target_commit
|
|
24
24
|
|
|
@@ -61,21 +61,21 @@ module Git
|
|
|
61
61
|
|
|
62
62
|
def compute_dependencies_at_commit(target_commit, repo)
|
|
63
63
|
branch_name = @options[:branch] || repo.default_branch
|
|
64
|
-
branch = Models::Branch.
|
|
64
|
+
branch = Models::Branch.first(name: branch_name)
|
|
65
65
|
return [] unless branch
|
|
66
66
|
|
|
67
67
|
# Find the nearest snapshot commit before or at target
|
|
68
|
-
snapshot_commit = branch.
|
|
69
|
-
.
|
|
70
|
-
.where
|
|
71
|
-
.order(
|
|
68
|
+
snapshot_commit = branch.commits_dataset
|
|
69
|
+
.join(:dependency_snapshots, commit_id: :id)
|
|
70
|
+
.where { Sequel[:commits][:committed_at] <= target_commit.committed_at }
|
|
71
|
+
.order(Sequel.desc(Sequel[:commits][:committed_at]))
|
|
72
72
|
.distinct
|
|
73
73
|
.first
|
|
74
74
|
|
|
75
75
|
# Build initial state from snapshot
|
|
76
76
|
deps = {}
|
|
77
77
|
if snapshot_commit
|
|
78
|
-
snapshot_commit.dependency_snapshots.
|
|
78
|
+
snapshot_commit.dependency_snapshots.each do |s|
|
|
79
79
|
key = [s.manifest.path, s.name]
|
|
80
80
|
deps[key] = {
|
|
81
81
|
manifest_path: s.manifest.path,
|
|
@@ -89,13 +89,15 @@ module Git
|
|
|
89
89
|
|
|
90
90
|
# Replay changes from snapshot to target
|
|
91
91
|
if snapshot_commit && snapshot_commit.id != target_commit.id
|
|
92
|
+
commit_ids = branch.commits_dataset.select_map(:id)
|
|
92
93
|
changes = Models::DependencyChange
|
|
93
|
-
.
|
|
94
|
-
.where(commits:
|
|
95
|
-
.where
|
|
96
|
-
|
|
97
|
-
.order(
|
|
98
|
-
.
|
|
94
|
+
.join(:commits, id: :commit_id)
|
|
95
|
+
.where(Sequel[:commits][:id] => commit_ids)
|
|
96
|
+
.where { Sequel[:commits][:committed_at] > snapshot_commit.committed_at }
|
|
97
|
+
.where { Sequel[:commits][:committed_at] <= target_commit.committed_at }
|
|
98
|
+
.order(Sequel[:commits][:committed_at])
|
|
99
|
+
.eager(:manifest)
|
|
100
|
+
.all
|
|
99
101
|
|
|
100
102
|
changes.each do |change|
|
|
101
103
|
key = [change.manifest.path, change.name]
|
|
@@ -18,16 +18,22 @@ module Git
|
|
|
18
18
|
Database.connect(repo.git_dir)
|
|
19
19
|
|
|
20
20
|
commits = Models::Commit
|
|
21
|
-
.
|
|
21
|
+
.eager(:dependency_changes)
|
|
22
22
|
.where(has_dependency_changes: true)
|
|
23
|
-
.order(committed_at
|
|
23
|
+
.order(Sequel.desc(:committed_at), Sequel.desc(:id))
|
|
24
|
+
|
|
25
|
+
if @options[:author]
|
|
26
|
+
author = @options[:author]
|
|
27
|
+
commits = commits.where(
|
|
28
|
+
Sequel.like(:author_name, "%#{author}%") |
|
|
29
|
+
Sequel.like(:author_email, "%#{author}%")
|
|
30
|
+
)
|
|
31
|
+
end
|
|
24
32
|
|
|
25
|
-
commits = commits.where
|
|
26
|
-
|
|
27
|
-
commits = commits.where("committed_at >= ?", parse_time(@options[:since])) if @options[:since]
|
|
28
|
-
commits = commits.where("committed_at <= ?", parse_time(@options[:until])) if @options[:until]
|
|
33
|
+
commits = commits.where { committed_at >= parse_time(@options[:since]) } if @options[:since]
|
|
34
|
+
commits = commits.where { committed_at <= parse_time(@options[:until]) } if @options[:until]
|
|
29
35
|
|
|
30
|
-
commits = commits.limit(@options[:limit] || 20)
|
|
36
|
+
commits = commits.limit(@options[:limit] || 20).all
|
|
31
37
|
|
|
32
38
|
if commits.empty?
|
|
33
39
|
empty_result "No commits with dependency changes found"
|
|
@@ -33,32 +33,29 @@ module Git
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
def fetch_schema
|
|
36
|
-
|
|
36
|
+
db = Database.db
|
|
37
37
|
tables = {}
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
next if table_name == "schema_migrations"
|
|
42
|
-
|
|
43
|
-
columns = conn.columns(table_name).map do |col|
|
|
39
|
+
db.tables.sort.each do |table_name|
|
|
40
|
+
columns = db.schema(table_name).map do |name, col_info|
|
|
44
41
|
{
|
|
45
|
-
name:
|
|
46
|
-
type:
|
|
47
|
-
sql_type:
|
|
48
|
-
null:
|
|
49
|
-
default:
|
|
42
|
+
name: name.to_s,
|
|
43
|
+
type: col_info[:type],
|
|
44
|
+
sql_type: col_info[:db_type],
|
|
45
|
+
null: col_info[:allow_null],
|
|
46
|
+
default: col_info[:default]
|
|
50
47
|
}
|
|
51
48
|
end
|
|
52
49
|
|
|
53
|
-
indexes =
|
|
50
|
+
indexes = db.indexes(table_name).map do |name, idx_info|
|
|
54
51
|
{
|
|
55
|
-
name:
|
|
56
|
-
columns:
|
|
57
|
-
unique:
|
|
52
|
+
name: name.to_s,
|
|
53
|
+
columns: idx_info[:columns].map(&:to_s),
|
|
54
|
+
unique: idx_info[:unique]
|
|
58
55
|
}
|
|
59
56
|
end
|
|
60
57
|
|
|
61
|
-
tables[table_name] = { columns: columns, indexes: indexes }
|
|
58
|
+
tables[table_name.to_s] = { columns: columns, indexes: indexes }
|
|
62
59
|
end
|
|
63
60
|
|
|
64
61
|
tables
|
|
@@ -72,7 +69,7 @@ module Git
|
|
|
72
69
|
info[:columns].each do |col|
|
|
73
70
|
nullable = col[:null] ? "NULL" : "NOT NULL"
|
|
74
71
|
default = col[:default] ? " DEFAULT #{col[:default]}" : ""
|
|
75
|
-
puts " #{col[:name].ljust(25)} #{col[:sql_type].ljust(15)} #{nullable}#{default}"
|
|
72
|
+
puts " #{col[:name].ljust(25)} #{col[:sql_type].to_s.ljust(15)} #{nullable}#{default}"
|
|
76
73
|
end
|
|
77
74
|
|
|
78
75
|
if info[:indexes].any?
|
|
@@ -89,16 +86,16 @@ module Git
|
|
|
89
86
|
end
|
|
90
87
|
|
|
91
88
|
def output_sql(tables)
|
|
92
|
-
|
|
89
|
+
db = Database.db
|
|
93
90
|
|
|
94
91
|
tables.each do |table_name, info|
|
|
95
|
-
sql =
|
|
96
|
-
puts sql[
|
|
92
|
+
sql = db.fetch("SELECT sql FROM sqlite_master WHERE type='table' AND name=?", table_name).first
|
|
93
|
+
puts sql[:sql] + ";" if sql
|
|
97
94
|
puts
|
|
98
95
|
|
|
99
96
|
info[:indexes].each do |idx|
|
|
100
|
-
idx_sql =
|
|
101
|
-
puts idx_sql[
|
|
97
|
+
idx_sql = db.fetch("SELECT sql FROM sqlite_master WHERE type='index' AND name=?", idx[:name]).first
|
|
98
|
+
puts idx_sql[:sql] + ";" if idx_sql && idx_sql[:sql]
|
|
102
99
|
end
|
|
103
100
|
|
|
104
101
|
puts if info[:indexes].any?
|