git-pkgs 0.4.0 → 0.5.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 +15 -0
- data/README.md +48 -39
- data/lib/git/pkgs/analyzer.rb +35 -12
- data/lib/git/pkgs/cli.rb +83 -29
- data/lib/git/pkgs/commands/blame.rb +25 -7
- data/lib/git/pkgs/commands/branch.rb +27 -15
- data/lib/git/pkgs/commands/completions.rb +234 -0
- data/lib/git/pkgs/commands/diff.rb +34 -31
- data/lib/git/pkgs/commands/diff_driver.rb +9 -7
- data/lib/git/pkgs/commands/history.rb +0 -7
- data/lib/git/pkgs/commands/hooks.rb +8 -8
- data/lib/git/pkgs/commands/info.rb +65 -1
- data/lib/git/pkgs/commands/init.rb +31 -17
- data/lib/git/pkgs/commands/list.rb +2 -2
- data/lib/git/pkgs/commands/log.rb +5 -12
- data/lib/git/pkgs/commands/show.rb +3 -23
- data/lib/git/pkgs/commands/stale.rb +26 -7
- data/lib/git/pkgs/commands/stats.rb +9 -12
- data/lib/git/pkgs/commands/tree.rb +1 -1
- data/lib/git/pkgs/commands/update.rb +9 -7
- data/lib/git/pkgs/commands/upgrade.rb +4 -4
- data/lib/git/pkgs/config.rb +73 -0
- data/lib/git/pkgs/database.rb +7 -7
- data/lib/git/pkgs/models/commit.rb +19 -0
- data/lib/git/pkgs/output.rb +13 -0
- data/lib/git/pkgs/repository.rb +35 -1
- data/lib/git/pkgs/version.rb +1 -1
- data/lib/git/pkgs.rb +34 -0
- metadata +3 -10
- data/CODE_OF_CONDUCT.md +0 -10
- data/CONTRIBUTING.md +0 -27
- data/Rakefile +0 -8
- data/SECURITY.md +0 -7
- data/benchmark_bulk.rb +0 -167
- data/benchmark_db.rb +0 -138
- data/benchmark_detailed.rb +0 -151
- data/benchmark_full.rb +0 -131
- data/docs/schema.md +0 -129
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Git
|
|
4
|
+
module Pkgs
|
|
5
|
+
module Commands
|
|
6
|
+
class Completions
|
|
7
|
+
COMMANDS = CLI::COMMANDS
|
|
8
|
+
SUBCOMMAND_OPTIONS = {
|
|
9
|
+
"hooks" => %w[--install --uninstall],
|
|
10
|
+
"branch" => %w[--add --remove --list],
|
|
11
|
+
"diff" => %w[--format],
|
|
12
|
+
"list" => %w[--format --type],
|
|
13
|
+
"tree" => %w[--format],
|
|
14
|
+
"history" => %w[--format --limit],
|
|
15
|
+
"search" => %w[--format --limit],
|
|
16
|
+
"blame" => %w[--format],
|
|
17
|
+
"stale" => %w[--days --format],
|
|
18
|
+
"stats" => %w[--format],
|
|
19
|
+
"log" => %w[--limit --format],
|
|
20
|
+
"show" => %w[--format],
|
|
21
|
+
"where" => %w[--format],
|
|
22
|
+
"why" => %w[--format]
|
|
23
|
+
}.freeze
|
|
24
|
+
|
|
25
|
+
BASH_SCRIPT = <<~'BASH'
|
|
26
|
+
_git_pkgs() {
|
|
27
|
+
local cur prev commands
|
|
28
|
+
COMPREPLY=()
|
|
29
|
+
cur="${COMP_WORDS[COMP_CWORD]}"
|
|
30
|
+
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
|
31
|
+
|
|
32
|
+
commands="init update hooks info list tree history search where why blame stale stats diff branch show log upgrade schema completions"
|
|
33
|
+
|
|
34
|
+
if [[ ${COMP_CWORD} -eq 2 && ${COMP_WORDS[1]} == "pkgs" ]]; then
|
|
35
|
+
COMPREPLY=( $(compgen -W "${commands}" -- ${cur}) )
|
|
36
|
+
return 0
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
if [[ ${COMP_CWORD} -eq 1 ]]; then
|
|
40
|
+
COMPREPLY=( $(compgen -W "${commands}" -- ${cur}) )
|
|
41
|
+
return 0
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
case "${prev}" in
|
|
45
|
+
hooks)
|
|
46
|
+
COMPREPLY=( $(compgen -W "--install --uninstall --help" -- ${cur}) )
|
|
47
|
+
;;
|
|
48
|
+
branch)
|
|
49
|
+
COMPREPLY=( $(compgen -W "--add --remove --list --help" -- ${cur}) )
|
|
50
|
+
;;
|
|
51
|
+
completions)
|
|
52
|
+
COMPREPLY=( $(compgen -W "bash zsh install --help" -- ${cur}) )
|
|
53
|
+
;;
|
|
54
|
+
diff|list|tree|history|search|blame|stale|stats|log|show|where|why)
|
|
55
|
+
COMPREPLY=( $(compgen -W "--format --help" -- ${cur}) )
|
|
56
|
+
;;
|
|
57
|
+
esac
|
|
58
|
+
|
|
59
|
+
return 0
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# Support both 'git pkgs' and 'git-pkgs' invocations
|
|
63
|
+
complete -F _git_pkgs git-pkgs
|
|
64
|
+
|
|
65
|
+
# For 'git pkgs' subcommand completion
|
|
66
|
+
if declare -F _git >/dev/null 2>&1; then
|
|
67
|
+
_git_pkgs_git_wrapper() {
|
|
68
|
+
if [[ ${COMP_WORDS[1]} == "pkgs" ]]; then
|
|
69
|
+
_git_pkgs
|
|
70
|
+
fi
|
|
71
|
+
}
|
|
72
|
+
fi
|
|
73
|
+
BASH
|
|
74
|
+
|
|
75
|
+
ZSH_SCRIPT = <<~'ZSH'
|
|
76
|
+
#compdef git-pkgs
|
|
77
|
+
|
|
78
|
+
_git-pkgs() {
|
|
79
|
+
local -a commands
|
|
80
|
+
commands=(
|
|
81
|
+
'init:Initialize the package database'
|
|
82
|
+
'update:Update the database with new commits'
|
|
83
|
+
'hooks:Manage git hooks for auto-updating'
|
|
84
|
+
'info:Show database size and row counts'
|
|
85
|
+
'branch:Manage tracked branches'
|
|
86
|
+
'list:List dependencies at a commit'
|
|
87
|
+
'tree:Show dependency tree grouped by type'
|
|
88
|
+
'history:Show the history of a package'
|
|
89
|
+
'search:Find a dependency across all history'
|
|
90
|
+
'where:Show where a package appears in manifest files'
|
|
91
|
+
'why:Explain why a dependency exists'
|
|
92
|
+
'blame:Show who added each dependency'
|
|
93
|
+
'stale:Show dependencies that have not been updated'
|
|
94
|
+
'stats:Show dependency statistics'
|
|
95
|
+
'diff:Show dependency changes between commits'
|
|
96
|
+
'show:Show dependency changes in a commit'
|
|
97
|
+
'log:List commits with dependency changes'
|
|
98
|
+
'upgrade:Upgrade database after git-pkgs update'
|
|
99
|
+
'schema:Show database schema'
|
|
100
|
+
'completions:Generate shell completions'
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
_arguments -C \
|
|
104
|
+
'1: :->command' \
|
|
105
|
+
'*:: :->args'
|
|
106
|
+
|
|
107
|
+
case $state in
|
|
108
|
+
command)
|
|
109
|
+
_describe -t commands 'git-pkgs commands' commands
|
|
110
|
+
;;
|
|
111
|
+
args)
|
|
112
|
+
case $words[1] in
|
|
113
|
+
hooks)
|
|
114
|
+
_arguments \
|
|
115
|
+
'--install[Install git hooks]' \
|
|
116
|
+
'--uninstall[Remove git hooks]' \
|
|
117
|
+
'--help[Show help]'
|
|
118
|
+
;;
|
|
119
|
+
branch)
|
|
120
|
+
_arguments \
|
|
121
|
+
'--add[Add a branch to track]' \
|
|
122
|
+
'--remove[Remove a tracked branch]' \
|
|
123
|
+
'--list[List tracked branches]' \
|
|
124
|
+
'--help[Show help]'
|
|
125
|
+
;;
|
|
126
|
+
completions)
|
|
127
|
+
_arguments '1:shell:(bash zsh install)'
|
|
128
|
+
;;
|
|
129
|
+
diff|list|tree|history|search|blame|stale|stats|log|show|where|why)
|
|
130
|
+
_arguments \
|
|
131
|
+
'--format[Output format]:format:(table json csv)' \
|
|
132
|
+
'--help[Show help]'
|
|
133
|
+
;;
|
|
134
|
+
esac
|
|
135
|
+
;;
|
|
136
|
+
esac
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
_git-pkgs "$@"
|
|
140
|
+
ZSH
|
|
141
|
+
|
|
142
|
+
def initialize(args)
|
|
143
|
+
@args = args
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def run
|
|
147
|
+
shell = @args.first
|
|
148
|
+
|
|
149
|
+
case shell
|
|
150
|
+
when "bash"
|
|
151
|
+
puts BASH_SCRIPT
|
|
152
|
+
when "zsh"
|
|
153
|
+
puts ZSH_SCRIPT
|
|
154
|
+
when "install"
|
|
155
|
+
install_completions
|
|
156
|
+
when "-h", "--help", nil
|
|
157
|
+
print_help
|
|
158
|
+
else
|
|
159
|
+
$stderr.puts "Unknown shell: #{shell}"
|
|
160
|
+
$stderr.puts "Supported: bash, zsh, install"
|
|
161
|
+
exit 1
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def install_completions
|
|
166
|
+
shell = detect_shell
|
|
167
|
+
|
|
168
|
+
case shell
|
|
169
|
+
when "zsh"
|
|
170
|
+
install_zsh_completions
|
|
171
|
+
when "bash"
|
|
172
|
+
install_bash_completions
|
|
173
|
+
else
|
|
174
|
+
$stderr.puts "Could not detect shell. Please run one of:"
|
|
175
|
+
$stderr.puts " eval \"$(git pkgs completions bash)\""
|
|
176
|
+
$stderr.puts " eval \"$(git pkgs completions zsh)\""
|
|
177
|
+
exit 1
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def detect_shell
|
|
182
|
+
shell_env = ENV["SHELL"] || ""
|
|
183
|
+
if shell_env.include?("zsh")
|
|
184
|
+
"zsh"
|
|
185
|
+
elsif shell_env.include?("bash")
|
|
186
|
+
"bash"
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def install_bash_completions
|
|
191
|
+
dir = File.expand_path("~/.local/share/bash-completion/completions")
|
|
192
|
+
FileUtils.mkdir_p(dir)
|
|
193
|
+
path = File.join(dir, "git-pkgs")
|
|
194
|
+
File.write(path, BASH_SCRIPT)
|
|
195
|
+
puts "Installed bash completions to #{path}"
|
|
196
|
+
puts "Restart your shell or run: source #{path}"
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def install_zsh_completions
|
|
200
|
+
dir = File.expand_path("~/.zsh/completions")
|
|
201
|
+
FileUtils.mkdir_p(dir)
|
|
202
|
+
path = File.join(dir, "_git-pkgs")
|
|
203
|
+
File.write(path, ZSH_SCRIPT)
|
|
204
|
+
puts "Installed zsh completions to #{path}"
|
|
205
|
+
puts ""
|
|
206
|
+
puts "Add to your ~/.zshrc if not already present:"
|
|
207
|
+
puts " fpath=(~/.zsh/completions $fpath)"
|
|
208
|
+
puts " autoload -Uz compinit && compinit"
|
|
209
|
+
puts ""
|
|
210
|
+
puts "Then restart your shell or run: source ~/.zshrc"
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def print_help
|
|
214
|
+
puts <<~HELP
|
|
215
|
+
Usage: git pkgs completions <shell>
|
|
216
|
+
|
|
217
|
+
Generate shell completion scripts.
|
|
218
|
+
|
|
219
|
+
Shells:
|
|
220
|
+
bash Output bash completion script
|
|
221
|
+
zsh Output zsh completion script
|
|
222
|
+
install Auto-install completions for your shell
|
|
223
|
+
|
|
224
|
+
Examples:
|
|
225
|
+
git pkgs completions bash > ~/.local/share/bash-completion/completions/git-pkgs
|
|
226
|
+
git pkgs completions zsh > ~/.zsh/completions/_git-pkgs
|
|
227
|
+
eval "$(git pkgs completions bash)"
|
|
228
|
+
git pkgs completions install
|
|
229
|
+
HELP
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
@@ -17,23 +17,24 @@ module Git
|
|
|
17
17
|
|
|
18
18
|
Database.connect(repo.git_dir)
|
|
19
19
|
|
|
20
|
-
from_ref =
|
|
21
|
-
|
|
20
|
+
from_ref, to_ref = parse_range_argument
|
|
21
|
+
from_ref ||= @options[:from]
|
|
22
|
+
to_ref ||= @options[:to] || "HEAD"
|
|
22
23
|
|
|
23
|
-
error "Usage: git pkgs diff --from=REF [--to=REF]" unless from_ref
|
|
24
|
+
error "Usage: git pkgs diff <commit>..<commit> or git pkgs diff --from=REF [--to=REF]" unless from_ref
|
|
24
25
|
|
|
25
26
|
# Resolve git refs (like HEAD~10) to SHAs
|
|
26
27
|
from_sha = repo.rev_parse(from_ref)
|
|
27
28
|
to_sha = repo.rev_parse(to_ref)
|
|
28
29
|
|
|
29
|
-
error "Could not resolve '#{from_ref}'" unless from_sha
|
|
30
|
-
error "Could not resolve '#{to_ref}'" unless to_sha
|
|
30
|
+
error "Could not resolve '#{from_ref}'. Check that the ref exists." unless from_sha
|
|
31
|
+
error "Could not resolve '#{to_ref}'. Check that the ref exists." unless to_sha
|
|
31
32
|
|
|
32
|
-
from_commit =
|
|
33
|
-
to_commit =
|
|
33
|
+
from_commit = Models::Commit.find_or_create_from_repo(repo, from_sha)
|
|
34
|
+
to_commit = Models::Commit.find_or_create_from_repo(repo, to_sha)
|
|
34
35
|
|
|
35
|
-
error "Commit '#{from_sha[0..7]}' not
|
|
36
|
-
error "Commit '#{to_sha[0..7]}' not
|
|
36
|
+
error "Commit '#{from_sha[0..7]}' not in database. Run 'git pkgs update' to index new commits." unless from_commit
|
|
37
|
+
error "Commit '#{to_sha[0..7]}' not in database. Run 'git pkgs update' to index new commits." unless to_commit
|
|
37
38
|
|
|
38
39
|
# Get all changes between the two commits
|
|
39
40
|
changes = Models::DependencyChange
|
|
@@ -98,34 +99,36 @@ module Git
|
|
|
98
99
|
puts "Summary: #{added_count} #{removed_count} #{modified_count}"
|
|
99
100
|
end
|
|
100
101
|
|
|
101
|
-
def
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
has_dependency_changes: false
|
|
117
|
-
)
|
|
118
|
-
rescue Rugged::OdbError
|
|
119
|
-
nil
|
|
102
|
+
def parse_range_argument
|
|
103
|
+
return [nil, nil] if @args.empty?
|
|
104
|
+
|
|
105
|
+
arg = @args.first
|
|
106
|
+
return [nil, nil] if arg.start_with?("-")
|
|
107
|
+
|
|
108
|
+
if arg.include?("..")
|
|
109
|
+
@args.shift
|
|
110
|
+
parts = arg.split("..", 2)
|
|
111
|
+
[parts[0], parts[1].empty? ? "HEAD" : parts[1]]
|
|
112
|
+
else
|
|
113
|
+
# Single ref means "from that ref to HEAD"
|
|
114
|
+
@args.shift
|
|
115
|
+
[arg, "HEAD"]
|
|
116
|
+
end
|
|
120
117
|
end
|
|
121
118
|
|
|
122
119
|
def parse_options
|
|
123
120
|
options = {}
|
|
124
121
|
|
|
125
122
|
parser = OptionParser.new do |opts|
|
|
126
|
-
opts.banner = "Usage: git pkgs diff
|
|
127
|
-
|
|
128
|
-
opts.
|
|
123
|
+
opts.banner = "Usage: git pkgs diff [<from>..<to>] [options]"
|
|
124
|
+
opts.separator ""
|
|
125
|
+
opts.separator "Examples:"
|
|
126
|
+
opts.separator " git pkgs diff main..feature"
|
|
127
|
+
opts.separator " git pkgs diff HEAD~10"
|
|
128
|
+
opts.separator " git pkgs diff --from=v1.0 --to=v2.0"
|
|
129
|
+
opts.separator ""
|
|
130
|
+
|
|
131
|
+
opts.on("-f", "--from=REF", "Start commit") do |v|
|
|
129
132
|
options[:from] = v
|
|
130
133
|
end
|
|
131
134
|
|
|
@@ -42,6 +42,7 @@ module Git
|
|
|
42
42
|
def initialize(args)
|
|
43
43
|
@args = args
|
|
44
44
|
@options = parse_options
|
|
45
|
+
Config.configure_bibliothecary
|
|
45
46
|
end
|
|
46
47
|
|
|
47
48
|
def run
|
|
@@ -99,12 +100,12 @@ module Git
|
|
|
99
100
|
end
|
|
100
101
|
end
|
|
101
102
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
103
|
+
info "Installed textconv driver for lockfiles."
|
|
104
|
+
info " git config: diff.pkgs.textconv = git-pkgs diff-driver"
|
|
105
|
+
info " .gitattributes: #{new_entries.count} lockfile patterns added"
|
|
106
|
+
info ""
|
|
107
|
+
info "Now 'git diff' on lockfiles shows dependency changes."
|
|
108
|
+
info "Use 'git diff --no-textconv' to see raw diff."
|
|
108
109
|
end
|
|
109
110
|
|
|
110
111
|
def uninstall_driver
|
|
@@ -117,7 +118,7 @@ module Git
|
|
|
117
118
|
File.write(gitattributes_path, lines.join)
|
|
118
119
|
end
|
|
119
120
|
|
|
120
|
-
|
|
121
|
+
info "Uninstalled diff driver."
|
|
121
122
|
end
|
|
122
123
|
|
|
123
124
|
def read_file(path)
|
|
@@ -132,6 +133,7 @@ module Git
|
|
|
132
133
|
|
|
133
134
|
result = Bibliothecary.analyse_file(path, content).first
|
|
134
135
|
return {} unless result
|
|
136
|
+
return {} if Config.filter_ecosystem?(result[:platform])
|
|
135
137
|
|
|
136
138
|
result[:dependencies].map { |d| [d[:name], d] }.to_h
|
|
137
139
|
rescue StandardError
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "time"
|
|
4
|
-
|
|
5
3
|
module Git
|
|
6
4
|
module Pkgs
|
|
7
5
|
module Commands
|
|
@@ -159,11 +157,6 @@ module Git
|
|
|
159
157
|
options
|
|
160
158
|
end
|
|
161
159
|
|
|
162
|
-
def parse_time(str)
|
|
163
|
-
Time.parse(str)
|
|
164
|
-
rescue ArgumentError
|
|
165
|
-
error "Invalid date format: #{str}"
|
|
166
|
-
end
|
|
167
160
|
end
|
|
168
161
|
end
|
|
169
162
|
end
|
|
@@ -40,7 +40,7 @@ module Git
|
|
|
40
40
|
if File.exist?(hook_path)
|
|
41
41
|
content = File.read(hook_path)
|
|
42
42
|
if content.include?("git-pkgs")
|
|
43
|
-
|
|
43
|
+
info "Hook #{hook_name} already contains git-pkgs"
|
|
44
44
|
next
|
|
45
45
|
end
|
|
46
46
|
|
|
@@ -48,15 +48,15 @@ module Git
|
|
|
48
48
|
f.puts "\n# git-pkgs auto-update"
|
|
49
49
|
f.puts "git pkgs update 2>/dev/null || true"
|
|
50
50
|
end
|
|
51
|
-
|
|
51
|
+
info "Appended git-pkgs to existing #{hook_name} hook"
|
|
52
52
|
else
|
|
53
53
|
File.write(hook_path, HOOK_SCRIPT)
|
|
54
54
|
File.chmod(0o755, hook_path)
|
|
55
|
-
|
|
55
|
+
info "Created #{hook_name} hook"
|
|
56
56
|
end
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
info "Hooks installed successfully"
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
def uninstall_hooks(repo)
|
|
@@ -70,7 +70,7 @@ module Git
|
|
|
70
70
|
|
|
71
71
|
if content.strip == HOOK_SCRIPT.strip
|
|
72
72
|
File.delete(hook_path)
|
|
73
|
-
|
|
73
|
+
info "Removed #{hook_name} hook"
|
|
74
74
|
elsif content.include?("git-pkgs")
|
|
75
75
|
new_content = content.lines.reject { |line|
|
|
76
76
|
line.include?("git-pkgs") || line.include?("git pkgs")
|
|
@@ -79,15 +79,15 @@ module Git
|
|
|
79
79
|
|
|
80
80
|
if new_content.strip.empty? || new_content.strip == "#!/bin/sh"
|
|
81
81
|
File.delete(hook_path)
|
|
82
|
-
|
|
82
|
+
info "Removed #{hook_name} hook"
|
|
83
83
|
else
|
|
84
84
|
File.write(hook_path, new_content)
|
|
85
|
-
|
|
85
|
+
info "Removed git-pkgs from #{hook_name} hook"
|
|
86
86
|
end
|
|
87
87
|
end
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
info "Hooks uninstalled successfully"
|
|
91
91
|
end
|
|
92
92
|
|
|
93
93
|
def show_status(repo)
|
|
@@ -12,6 +12,11 @@ module Git
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def run
|
|
15
|
+
if @options[:ecosystems]
|
|
16
|
+
output_ecosystems
|
|
17
|
+
return
|
|
18
|
+
end
|
|
19
|
+
|
|
15
20
|
repo = Repository.new
|
|
16
21
|
require_database(repo)
|
|
17
22
|
|
|
@@ -77,6 +82,61 @@ module Git
|
|
|
77
82
|
end
|
|
78
83
|
end
|
|
79
84
|
|
|
85
|
+
def output_ecosystems
|
|
86
|
+
require "bibliothecary"
|
|
87
|
+
|
|
88
|
+
all_ecosystems = Bibliothecary::Parsers.constants.map do |c|
|
|
89
|
+
parser = Bibliothecary::Parsers.const_get(c)
|
|
90
|
+
parser.platform_name if parser.respond_to?(:platform_name)
|
|
91
|
+
end.compact.sort
|
|
92
|
+
|
|
93
|
+
configured = Config.ecosystems
|
|
94
|
+
filtering = configured.any?
|
|
95
|
+
|
|
96
|
+
puts "Available Ecosystems"
|
|
97
|
+
puts "=" * 40
|
|
98
|
+
puts
|
|
99
|
+
|
|
100
|
+
enabled_ecos = []
|
|
101
|
+
disabled_ecos = []
|
|
102
|
+
|
|
103
|
+
all_ecosystems.each do |eco|
|
|
104
|
+
if Config.filter_ecosystem?(eco)
|
|
105
|
+
remote = Config.remote_ecosystem?(eco)
|
|
106
|
+
disabled_ecos << { name: eco, remote: remote }
|
|
107
|
+
else
|
|
108
|
+
enabled_ecos << eco
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
puts "Enabled:"
|
|
113
|
+
if enabled_ecos.any?
|
|
114
|
+
enabled_ecos.each { |eco| puts " #{Color.green(eco)}" }
|
|
115
|
+
else
|
|
116
|
+
puts " (none)"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
puts
|
|
120
|
+
puts "Disabled:"
|
|
121
|
+
if disabled_ecos.any?
|
|
122
|
+
disabled_ecos.each do |eco|
|
|
123
|
+
suffix = eco[:remote] ? " (remote)" : ""
|
|
124
|
+
puts " #{eco[:name]}#{suffix}"
|
|
125
|
+
end
|
|
126
|
+
else
|
|
127
|
+
puts " (none)"
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
puts
|
|
131
|
+
if filtering
|
|
132
|
+
puts "Filtering: only #{configured.join(', ')}"
|
|
133
|
+
else
|
|
134
|
+
puts "All local ecosystems enabled"
|
|
135
|
+
end
|
|
136
|
+
puts "Remote ecosystems require explicit opt-in"
|
|
137
|
+
puts "Configure with: git config --add pkgs.ecosystems <name>"
|
|
138
|
+
end
|
|
139
|
+
|
|
80
140
|
def format_size(bytes)
|
|
81
141
|
units = %w[B KB MB GB]
|
|
82
142
|
unit_index = 0
|
|
@@ -94,7 +154,11 @@ module Git
|
|
|
94
154
|
options = {}
|
|
95
155
|
|
|
96
156
|
parser = OptionParser.new do |opts|
|
|
97
|
-
opts.banner = "Usage: git pkgs info"
|
|
157
|
+
opts.banner = "Usage: git pkgs info [options]"
|
|
158
|
+
|
|
159
|
+
opts.on("--ecosystems", "Show available ecosystems and filter status") do
|
|
160
|
+
options[:ecosystems] = true
|
|
161
|
+
end
|
|
98
162
|
|
|
99
163
|
opts.on("-h", "--help", "Show this help") do
|
|
100
164
|
puts opts
|
|
@@ -6,8 +6,16 @@ module Git
|
|
|
6
6
|
class Init
|
|
7
7
|
include Output
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
DEFAULT_BATCH_SIZE = 500
|
|
10
|
+
DEFAULT_SNAPSHOT_INTERVAL = 50
|
|
11
|
+
|
|
12
|
+
def batch_size
|
|
13
|
+
Git::Pkgs.batch_size || DEFAULT_BATCH_SIZE
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def snapshot_interval
|
|
17
|
+
Git::Pkgs.snapshot_interval || DEFAULT_SNAPSHOT_INTERVAL
|
|
18
|
+
end
|
|
11
19
|
|
|
12
20
|
def initialize(args)
|
|
13
21
|
@args = args
|
|
@@ -18,10 +26,10 @@ module Git
|
|
|
18
26
|
repo = Repository.new
|
|
19
27
|
|
|
20
28
|
branch_name = @options[:branch] || repo.default_branch
|
|
21
|
-
error "Branch '#{branch_name}' not found" unless repo.branch_exists?(branch_name)
|
|
29
|
+
error "Branch '#{branch_name}' not found. Check 'git branch -a' for available branches." unless repo.branch_exists?(branch_name)
|
|
22
30
|
|
|
23
31
|
if Database.exists?(repo.git_dir) && !@options[:force]
|
|
24
|
-
|
|
32
|
+
info "Database already exists. Use --force to rebuild."
|
|
25
33
|
return
|
|
26
34
|
end
|
|
27
35
|
|
|
@@ -33,29 +41,33 @@ module Git
|
|
|
33
41
|
branch = Models::Branch.find_or_create(branch_name)
|
|
34
42
|
analyzer = Analyzer.new(repo)
|
|
35
43
|
|
|
36
|
-
|
|
44
|
+
info "Analyzing branch: #{branch_name}"
|
|
37
45
|
|
|
46
|
+
print "Loading commits..." unless Git::Pkgs.quiet
|
|
38
47
|
walker = repo.walk(branch_name, @options[:since])
|
|
39
48
|
commits = walker.to_a
|
|
40
49
|
total = commits.size
|
|
50
|
+
print "\rPrefetching diffs..." unless Git::Pkgs.quiet
|
|
51
|
+
repo.prefetch_blob_paths(commits)
|
|
52
|
+
print "\r#{' ' * 20}\r" unless Git::Pkgs.quiet
|
|
41
53
|
|
|
42
54
|
stats = bulk_process_commits(commits, branch, analyzer, total)
|
|
43
55
|
|
|
44
56
|
branch.update(last_analyzed_sha: repo.branch_target(branch_name))
|
|
45
57
|
|
|
46
|
-
print "\rCreating indexes
|
|
58
|
+
print "\rCreating indexes...#{' ' * 20}" unless Git::Pkgs.quiet
|
|
47
59
|
Database.create_bulk_indexes
|
|
48
60
|
Database.optimize_for_reads
|
|
49
61
|
|
|
50
62
|
cache_stats = analyzer.cache_stats
|
|
51
63
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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"
|
|
57
69
|
|
|
58
|
-
|
|
70
|
+
unless @options[:no_hooks]
|
|
59
71
|
Commands::Hooks.new(["--install"]).run
|
|
60
72
|
end
|
|
61
73
|
end
|
|
@@ -135,9 +147,11 @@ module Git
|
|
|
135
147
|
pending_snapshots.clear
|
|
136
148
|
end
|
|
137
149
|
|
|
150
|
+
progress_interval = [total / 100, 10].max
|
|
151
|
+
|
|
138
152
|
commits.each do |rugged_commit|
|
|
139
153
|
processed += 1
|
|
140
|
-
print "\rProcessing commit #{processed}/#{total}..." if processed %
|
|
154
|
+
print "\rProcessing commit #{processed}/#{total}..." if !Git::Pkgs.quiet && (processed % progress_interval == 0 || processed == total)
|
|
141
155
|
|
|
142
156
|
next if rugged_commit.parents.length > 1 # skip merge commits
|
|
143
157
|
|
|
@@ -191,7 +205,7 @@ module Git
|
|
|
191
205
|
snapshot = result[:snapshot]
|
|
192
206
|
|
|
193
207
|
# Store snapshot at intervals
|
|
194
|
-
if dependency_commit_count %
|
|
208
|
+
if dependency_commit_count % snapshot_interval == 0
|
|
195
209
|
snapshot.each do |(manifest_path, name), dep_info|
|
|
196
210
|
pending_snapshots << {
|
|
197
211
|
sha: rugged_commit.oid,
|
|
@@ -206,7 +220,7 @@ module Git
|
|
|
206
220
|
end
|
|
207
221
|
end
|
|
208
222
|
|
|
209
|
-
flush.call if pending_commits.size >=
|
|
223
|
+
flush.call if pending_commits.size >= batch_size
|
|
210
224
|
end
|
|
211
225
|
|
|
212
226
|
# Always store final snapshot for the last processed commit
|
|
@@ -249,8 +263,8 @@ module Git
|
|
|
249
263
|
options[:force] = true
|
|
250
264
|
end
|
|
251
265
|
|
|
252
|
-
opts.on("--hooks", "
|
|
253
|
-
options[:
|
|
266
|
+
opts.on("--no-hooks", "Skip installing git hooks") do
|
|
267
|
+
options[:no_hooks] = true
|
|
254
268
|
end
|
|
255
269
|
|
|
256
270
|
opts.on("-h", "--help", "Show this help") do
|
|
@@ -20,7 +20,7 @@ module Git
|
|
|
20
20
|
commit_sha = @options[:commit] || repo.head_sha
|
|
21
21
|
target_commit = Models::Commit.find_by(sha: commit_sha)
|
|
22
22
|
|
|
23
|
-
error "Commit #{commit_sha[0, 7]} not
|
|
23
|
+
error "Commit #{commit_sha[0, 7]} not in database. Run 'git pkgs update' to index new commits." unless target_commit
|
|
24
24
|
|
|
25
25
|
deps = compute_dependencies_at_commit(target_commit, repo)
|
|
26
26
|
|
|
@@ -90,7 +90,7 @@ module Git
|
|
|
90
90
|
# Replay changes from snapshot to target
|
|
91
91
|
if snapshot_commit && snapshot_commit.id != target_commit.id
|
|
92
92
|
changes = Models::DependencyChange
|
|
93
|
-
.joins(:commit
|
|
93
|
+
.joins(:commit)
|
|
94
94
|
.where(commits: { id: branch.commit_ids })
|
|
95
95
|
.where("commits.committed_at > ? AND commits.committed_at <= ?",
|
|
96
96
|
snapshot_commit.committed_at, target_commit.committed_at)
|