git-pkgs 0.3.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 +27 -0
- data/LICENSE +189 -189
- data/README.md +89 -39
- data/lib/git/pkgs/analyzer.rb +36 -13
- data/lib/git/pkgs/cli.rb +86 -29
- data/lib/git/pkgs/color.rb +1 -0
- data/lib/git/pkgs/commands/blame.rb +26 -8
- 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 +171 -0
- 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 +70 -2
- data/lib/git/pkgs/commands/init.rb +40 -24
- 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/commands/where.rb +166 -0
- 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 +36 -0
- metadata +6 -8
- data/CODE_OF_CONDUCT.md +0 -10
- data/Rakefile +0 -8
- 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
|
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bibliothecary"
|
|
4
|
+
|
|
5
|
+
module Git
|
|
6
|
+
module Pkgs
|
|
7
|
+
module Commands
|
|
8
|
+
class DiffDriver
|
|
9
|
+
include Output
|
|
10
|
+
|
|
11
|
+
# Only lockfiles - manifests are human-readable and diff fine normally
|
|
12
|
+
LOCKFILE_PATTERNS = %w[
|
|
13
|
+
Brewfile.lock.json
|
|
14
|
+
Cargo.lock
|
|
15
|
+
Cartfile.resolved
|
|
16
|
+
Gemfile.lock
|
|
17
|
+
Gopkg.lock
|
|
18
|
+
Package.resolved
|
|
19
|
+
Pipfile.lock
|
|
20
|
+
Podfile.lock
|
|
21
|
+
Project.lock.json
|
|
22
|
+
bun.lock
|
|
23
|
+
composer.lock
|
|
24
|
+
gems.locked
|
|
25
|
+
glide.lock
|
|
26
|
+
go.sum
|
|
27
|
+
mix.lock
|
|
28
|
+
npm-shrinkwrap.json
|
|
29
|
+
package-lock.json
|
|
30
|
+
packages.lock.json
|
|
31
|
+
paket.lock
|
|
32
|
+
pnpm-lock.yaml
|
|
33
|
+
poetry.lock
|
|
34
|
+
project.assets.json
|
|
35
|
+
pubspec.lock
|
|
36
|
+
pylock.toml
|
|
37
|
+
shard.lock
|
|
38
|
+
uv.lock
|
|
39
|
+
yarn.lock
|
|
40
|
+
].freeze
|
|
41
|
+
|
|
42
|
+
def initialize(args)
|
|
43
|
+
@args = args
|
|
44
|
+
@options = parse_options
|
|
45
|
+
Config.configure_bibliothecary
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def run
|
|
49
|
+
if @options[:install]
|
|
50
|
+
install_driver
|
|
51
|
+
return
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
if @options[:uninstall]
|
|
55
|
+
uninstall_driver
|
|
56
|
+
return
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# textconv mode: single file argument, output dependency list
|
|
60
|
+
if @args.length == 1
|
|
61
|
+
output_textconv(@args[0])
|
|
62
|
+
return
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
error "Usage: git pkgs diff-driver <file>"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def output_textconv(file_path)
|
|
69
|
+
content = read_file(file_path)
|
|
70
|
+
deps = parse_deps(file_path, content)
|
|
71
|
+
|
|
72
|
+
# Output sorted dependency list for git to diff
|
|
73
|
+
deps.keys.sort.each do |name|
|
|
74
|
+
dep = deps[name]
|
|
75
|
+
# Only show type if it's not runtime (the default)
|
|
76
|
+
type_suffix = dep[:type] && dep[:type] != "runtime" ? " [#{dep[:type]}]" : ""
|
|
77
|
+
puts "#{name} #{dep[:requirement]}#{type_suffix}"
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def install_driver
|
|
82
|
+
# Set up git config for textconv
|
|
83
|
+
system("git", "config", "diff.pkgs.textconv", "git-pkgs diff-driver")
|
|
84
|
+
|
|
85
|
+
# Add to .gitattributes
|
|
86
|
+
gitattributes_path = File.join(Dir.pwd, ".gitattributes")
|
|
87
|
+
existing = File.exist?(gitattributes_path) ? File.read(gitattributes_path) : ""
|
|
88
|
+
|
|
89
|
+
new_entries = []
|
|
90
|
+
LOCKFILE_PATTERNS.each do |pattern|
|
|
91
|
+
entry = "#{pattern} diff=pkgs"
|
|
92
|
+
new_entries << entry unless existing.include?(entry)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
if new_entries.any?
|
|
96
|
+
File.open(gitattributes_path, "a") do |f|
|
|
97
|
+
f.puts unless existing.end_with?("\n") || existing.empty?
|
|
98
|
+
f.puts "# git-pkgs textconv for lockfiles"
|
|
99
|
+
new_entries.each { |entry| f.puts entry }
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
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."
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def uninstall_driver
|
|
112
|
+
system("git", "config", "--unset", "diff.pkgs.textconv")
|
|
113
|
+
|
|
114
|
+
gitattributes_path = File.join(Dir.pwd, ".gitattributes")
|
|
115
|
+
if File.exist?(gitattributes_path)
|
|
116
|
+
lines = File.readlines(gitattributes_path)
|
|
117
|
+
lines.reject! { |line| line.include?("diff=pkgs") || line.include?("# git-pkgs") }
|
|
118
|
+
File.write(gitattributes_path, lines.join)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
info "Uninstalled diff driver."
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def read_file(path)
|
|
125
|
+
return "" if path == "/dev/null"
|
|
126
|
+
return "" unless File.exist?(path)
|
|
127
|
+
|
|
128
|
+
File.read(path)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def parse_deps(path, content)
|
|
132
|
+
return {} if content.empty?
|
|
133
|
+
|
|
134
|
+
result = Bibliothecary.analyse_file(path, content).first
|
|
135
|
+
return {} unless result
|
|
136
|
+
return {} if Config.filter_ecosystem?(result[:platform])
|
|
137
|
+
|
|
138
|
+
result[:dependencies].map { |d| [d[:name], d] }.to_h
|
|
139
|
+
rescue StandardError
|
|
140
|
+
{}
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def parse_options
|
|
144
|
+
options = {}
|
|
145
|
+
|
|
146
|
+
parser = OptionParser.new do |opts|
|
|
147
|
+
opts.banner = "Usage: git pkgs diff-driver <file>"
|
|
148
|
+
opts.separator ""
|
|
149
|
+
opts.separator "Outputs dependency list for git textconv diffing."
|
|
150
|
+
|
|
151
|
+
opts.on("--install", "Install textconv driver for lockfiles") do
|
|
152
|
+
options[:install] = true
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
opts.on("--uninstall", "Uninstall textconv driver") do
|
|
156
|
+
options[:uninstall] = true
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
opts.on("-h", "--help", "Show this help") do
|
|
160
|
+
puts opts
|
|
161
|
+
exit
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
parser.parse!(@args)
|
|
166
|
+
options
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
@@ -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
|
|
|
@@ -69,10 +74,69 @@ module Git
|
|
|
69
74
|
puts " Commits with snapshots: #{snapshot_commits}"
|
|
70
75
|
if total_dep_commits > 0
|
|
71
76
|
ratio = (snapshot_commits.to_f / total_dep_commits * 100).round(1)
|
|
72
|
-
|
|
77
|
+
if snapshot_commits > 0
|
|
78
|
+
puts " Coverage: #{ratio}% (1 snapshot per ~#{(total_dep_commits / snapshot_commits)} changes)"
|
|
79
|
+
else
|
|
80
|
+
puts " Coverage: #{ratio}%"
|
|
81
|
+
end
|
|
73
82
|
end
|
|
74
83
|
end
|
|
75
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
|
+
|
|
76
140
|
def format_size(bytes)
|
|
77
141
|
units = %w[B KB MB GB]
|
|
78
142
|
unit_index = 0
|
|
@@ -90,7 +154,11 @@ module Git
|
|
|
90
154
|
options = {}
|
|
91
155
|
|
|
92
156
|
parser = OptionParser.new do |opts|
|
|
93
|
-
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
|
|
94
162
|
|
|
95
163
|
opts.on("-h", "--help", "Show this help") do
|
|
96
164
|
puts opts
|