Skip to content
This repository was archived by the owner on Jan 22, 2026. It is now read-only.

Commit bc8370d

Browse files
authored
Merge pull request #8 from andrew/performance
Performance improvements
2 parents 8ca2850 + 81edf63 commit bc8370d

File tree

7 files changed

+66
-22
lines changed

7 files changed

+66
-22
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
## [Unreleased]
22

33
- `git pkgs init` now installs git hooks by default (use `--no-hooks` to skip)
4+
- Fix N+1 queries in `blame`, `stale`, `stats`, and `log` commands
45

56
## [0.4.0] - 2026-01-04
67

lib/git/pkgs/analyzer.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ def parse_manifest_before_commit(rugged_commit, manifest_path)
251251
end
252252

253253
def parse_manifest_by_oid(blob_oid, manifest_path)
254-
cache_key = "#{blob_oid}:#{manifest_path}"
254+
cache_key = [blob_oid, manifest_path]
255255

256256
if @blob_cache.key?(cache_key)
257257
@blob_cache[cache_key][:hits] += 1

lib/git/pkgs/commands/blame.rb

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,34 @@ def run
3535
return
3636
end
3737

38+
# Batch fetch all "added" changes for current dependencies
39+
snapshot_keys = snapshots.map { |s| [s.name, s.manifest_id] }.to_set
40+
manifest_ids = snapshots.map(&:manifest_id).uniq
41+
names = snapshots.map(&:name).uniq
42+
43+
all_added_changes = Models::DependencyChange
44+
.includes(:commit)
45+
.added
46+
.where(manifest_id: manifest_ids, name: names)
47+
.to_a
48+
49+
# Group by (name, manifest_id) and find earliest by committed_at
50+
added_by_key = {}
51+
all_added_changes.each do |change|
52+
key = [change.name, change.manifest_id]
53+
next unless snapshot_keys.include?(key)
54+
55+
existing = added_by_key[key]
56+
if existing.nil? || change.commit.committed_at < existing.commit.committed_at
57+
added_by_key[key] = change
58+
end
59+
end
60+
3861
# For each current dependency, find who added it
3962
blame_data = []
4063

4164
snapshots.each do |snapshot|
42-
added_change = Models::DependencyChange
43-
.includes(:commit)
44-
.where(name: snapshot.name, manifest: snapshot.manifest)
45-
.added
46-
.order("commits.committed_at ASC")
47-
.first
65+
added_change = added_by_key[[snapshot.name, snapshot.manifest_id]]
4866

4967
next unless added_change
5068

lib/git/pkgs/commands/list.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def compute_dependencies_at_commit(target_commit, repo)
9090
# Replay changes from snapshot to target
9191
if snapshot_commit && snapshot_commit.id != target_commit.id
9292
changes = Models::DependencyChange
93-
.joins(:commit, :manifest)
93+
.joins(:commit)
9494
.where(commits: { id: branch.commit_ids })
9595
.where("commits.committed_at > ? AND commits.committed_at <= ?",
9696
snapshot_commit.committed_at, target_commit.committed_at)

lib/git/pkgs/commands/log.rb

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ def run
1818
Database.connect(repo.git_dir)
1919

2020
commits = Models::Commit
21+
.includes(:dependency_changes)
2122
.where(has_dependency_changes: true)
2223
.order(committed_at: :desc)
2324

@@ -42,8 +43,8 @@ def run
4243

4344
def output_text(commits)
4445
commits.each do |commit|
45-
changes = commit.dependency_changes
46-
changes = changes.where(ecosystem: @options[:ecosystem]) if @options[:ecosystem]
46+
changes = commit.dependency_changes.to_a
47+
changes = changes.select { |c| c.ecosystem == @options[:ecosystem] } if @options[:ecosystem]
4748
next if changes.empty?
4849

4950
puts "#{commit.short_sha} #{commit.message&.lines&.first&.strip}"
@@ -75,8 +76,8 @@ def output_json(commits)
7576
require "json"
7677

7778
data = commits.map do |commit|
78-
changes = commit.dependency_changes
79-
changes = changes.where(ecosystem: @options[:ecosystem]) if @options[:ecosystem]
79+
changes = commit.dependency_changes.to_a
80+
changes = changes.select { |c| c.ecosystem == @options[:ecosystem] } if @options[:ecosystem]
8081

8182
{
8283
sha: commit.sha,

lib/git/pkgs/commands/stale.rb

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,38 @@ def run
3434
return
3535
end
3636

37+
# Batch fetch all changes for current dependencies
38+
snapshot_keys = snapshots.map { |s| [s.name, s.manifest_id] }.to_set
39+
manifest_ids = snapshots.map(&:manifest_id).uniq
40+
names = snapshots.map(&:name).uniq
41+
42+
all_changes = Models::DependencyChange
43+
.includes(:commit)
44+
.where(manifest_id: manifest_ids, name: names)
45+
.to_a
46+
47+
# Group by (name, manifest_id) and find latest by committed_at
48+
latest_by_key = {}
49+
all_changes.each do |change|
50+
key = [change.name, change.manifest_id]
51+
next unless snapshot_keys.include?(key)
52+
53+
existing = latest_by_key[key]
54+
if existing.nil? || change.commit.committed_at > existing.commit.committed_at
55+
latest_by_key[key] = change
56+
end
57+
end
58+
3759
# Find last update for each dependency
3860
outdated_data = []
61+
now = Time.now
3962

4063
snapshots.each do |snapshot|
41-
last_change = Models::DependencyChange
42-
.includes(:commit)
43-
.where(name: snapshot.name, manifest: snapshot.manifest)
44-
.order("commits.committed_at DESC")
45-
.first
64+
last_change = latest_by_key[[snapshot.name, snapshot.manifest_id]]
4665

4766
next unless last_change
4867

49-
days_since_update = ((Time.now - last_change.commit.committed_at) / 86400).to_i
68+
days_since_update = ((now - last_change.commit.committed_at) / 86400).to_i
5069

5170
outdated_data << {
5271
name: snapshot.name,

lib/git/pkgs/commands/stats.rb

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,16 @@ def collect_stats(branch, branch_name)
9191
manifests = Models::Manifest.all
9292
manifests = manifests.where(ecosystem: ecosystem) if ecosystem
9393

94+
manifest_ids = manifests.pluck(:id)
95+
change_counts_query = Models::DependencyChange
96+
.joins(:commit)
97+
.where(manifest_id: manifest_ids)
98+
change_counts_query = change_counts_query.where("commits.committed_at >= ?", since_time) if since_time
99+
change_counts_query = change_counts_query.where("commits.committed_at <= ?", until_time) if until_time
100+
change_counts = change_counts_query.group(:manifest_id).count
101+
94102
data[:manifests] = manifests.map do |manifest|
95-
manifest_changes = manifest.dependency_changes.joins(:commit)
96-
manifest_changes = manifest_changes.where("commits.committed_at >= ?", since_time) if since_time
97-
manifest_changes = manifest_changes.where("commits.committed_at <= ?", until_time) if until_time
98-
{ path: manifest.path, ecosystem: manifest.ecosystem, changes: manifest_changes.count }
103+
{ path: manifest.path, ecosystem: manifest.ecosystem, changes: change_counts[manifest.id] || 0 }
99104
end
100105

101106
data

0 commit comments

Comments
 (0)