yamine 0.18.0 → 0.19.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 +11 -0
- data/lib/yamine/cli/worktree.rb +55 -1
- data/lib/yamine/version.rb +1 -1
- data/lib/yamine/worktrees.rb +55 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4f7d162f5c1ad28d48339119a7a5f6c15e1d1b269a8e33a0a6f21b51d24be07f
|
|
4
|
+
data.tar.gz: 795408acb99c284062307bd7e08217af3b1806ccb0d4b6b47c2b0841e0439a83
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 05f6fc345aa7a2f2d92194d1c3095b41b16c15b3be3bfc9d5e030eb84b8b589de349129da286242abf41af3a956999946a9f6b88edc34ab87dd8424e17ae06fd
|
|
7
|
+
data.tar.gz: c33864d1addccc46714f86556310fcd19cad835525d1b3eb800d7badc5987177406f924dcc27e77b04d4363e014042f997b0b04c5a020931e8e0d0d187687b94
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.19.0] — 2026-09-27
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **`yamine worktree info <name> [--json]`** — detailed view of a single
|
|
10
|
+
worktree: branch, HEAD (short SHA + subject), author, commit date,
|
|
11
|
+
dirty/merged status, and any database claims. `--json` emits the same
|
|
12
|
+
data as a machine-readable hash. Follows the same safety rails as
|
|
13
|
+
`list`: main checkout is always clean, detached HEADs report n/a for
|
|
14
|
+
merged status, and the path is resolved to its real location.
|
|
15
|
+
|
|
5
16
|
## [0.18.0] — 2026-09-23
|
|
6
17
|
|
|
7
18
|
### Changed
|
data/lib/yamine/cli/worktree.rb
CHANGED
|
@@ -52,12 +52,13 @@ module Yamine
|
|
|
52
52
|
sub = args.first
|
|
53
53
|
case sub
|
|
54
54
|
when "list", nil then list(ctx, args[1..] || [])
|
|
55
|
+
when "info" then info(ctx, args[1..] || [])
|
|
55
56
|
when "add" then add(ctx, args[1..] || [])
|
|
56
57
|
when "remove" then remove(ctx, args[1..] || [])
|
|
57
58
|
when "clean" then clean(ctx, args[1..] || [])
|
|
58
59
|
else
|
|
59
60
|
raise Error,
|
|
60
|
-
"Usage: yamine worktree list | add <name> [--dir <path>] [--no-install] | " \
|
|
61
|
+
"Usage: yamine worktree list | info <name> [--json] | add <name> [--dir <path>] [--no-install] | " \
|
|
61
62
|
"remove <name> [--force] | clean [--all] [--dry-run]"
|
|
62
63
|
end
|
|
63
64
|
end
|
|
@@ -110,6 +111,59 @@ module Yamine
|
|
|
110
111
|
line
|
|
111
112
|
end
|
|
112
113
|
|
|
114
|
+
# yamine worktree info <name> [--json]
|
|
115
|
+
#
|
|
116
|
+
# Detailed view of a single worktree: branch, HEAD, author, date,
|
|
117
|
+
# dirty/merged status, and any database claims.
|
|
118
|
+
def info(ctx, args)
|
|
119
|
+
opts, rest = take_flags(args, value_flags: [], flags: %w[--json])
|
|
120
|
+
name = rest.first
|
|
121
|
+
raise Error, "Usage: yamine worktree info <name> [--json]" unless name
|
|
122
|
+
|
|
123
|
+
repo = repo_top || abort_not_in_repo
|
|
124
|
+
entry = Worktrees.find(repo, name)
|
|
125
|
+
unless entry
|
|
126
|
+
raise Error, "no worktree named #{name.inspect} here — `yamine worktree list` shows what exists"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
map = Database.load_map(ctx.store.dir)
|
|
130
|
+
claim = claim_for(map, entry.path)
|
|
131
|
+
default_branch = Worktrees.default_branch(repo)
|
|
132
|
+
|
|
133
|
+
result = Worktrees.info(repo, entry, default_branch: default_branch)
|
|
134
|
+
result[:databases] = database_names(claim)
|
|
135
|
+
|
|
136
|
+
if opts[:json]
|
|
137
|
+
require "json"
|
|
138
|
+
puts JSON.pretty_generate(result)
|
|
139
|
+
else
|
|
140
|
+
print_info(result)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def database_names(claim)
|
|
145
|
+
return [] unless claim
|
|
146
|
+
|
|
147
|
+
info = claim[1]
|
|
148
|
+
names = info.is_a?(Hash) ? info["names"] : nil
|
|
149
|
+
return [claim[0]] unless names.is_a?(Hash) && !names.empty?
|
|
150
|
+
|
|
151
|
+
names.values
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def print_info(result)
|
|
155
|
+
puts " branch #{result[:branch]}"
|
|
156
|
+
puts " path #{result[:path]}"
|
|
157
|
+
puts " HEAD #{result[:head]}"
|
|
158
|
+
puts " author #{result[:author]}" if result[:author]
|
|
159
|
+
puts " date #{result[:date]}" if result[:date]
|
|
160
|
+
puts " dirty #{result[:dirty] ? "yes" : "no"}"
|
|
161
|
+
puts " merged #{result[:merged].nil? ? "n/a" : (result[:merged] ? "yes" : "no")}"
|
|
162
|
+
if result[:databases] && !result[:databases].empty?
|
|
163
|
+
result[:databases].each { |db| puts " database #{db}" }
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
113
167
|
# Every database a claim stands for, one per line under the row —
|
|
114
168
|
# five names inline would unreadable, and hiding them would
|
|
115
169
|
# re-create the "which database is this worktree even using"
|
data/lib/yamine/version.rb
CHANGED
data/lib/yamine/worktrees.rb
CHANGED
|
@@ -52,6 +52,36 @@ module Yamine
|
|
|
52
52
|
entries
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
+
# Detailed information about a single worktree. Returns a Hash with
|
|
56
|
+
# keys: :branch, :path, :head, :author, :date, :dirty, :merged.
|
|
57
|
+
def info(dir, entry, default_branch: nil)
|
|
58
|
+
repo = dir
|
|
59
|
+
branch_name = entry.main? ? "main" : (entry.branch || "(detached)")
|
|
60
|
+
|
|
61
|
+
head, subject, author_name, author_email, date = commit_info(entry.path)
|
|
62
|
+
|
|
63
|
+
dirty = entry.exists? &&
|
|
64
|
+
!entry.main? &&
|
|
65
|
+
dirty?(entry.path, ignore: [])
|
|
66
|
+
|
|
67
|
+
merged = if entry.detached || entry.main?
|
|
68
|
+
nil
|
|
69
|
+
else
|
|
70
|
+
db = default_branch || default_branch_for(repo)
|
|
71
|
+
merged?(repo, entry.branch, into: db)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
{
|
|
75
|
+
branch: branch_name,
|
|
76
|
+
path: File.expand_path(entry.path),
|
|
77
|
+
head: head,
|
|
78
|
+
author: author_name && author_email ? "#{author_name} <#{author_email}>" : nil,
|
|
79
|
+
date: date,
|
|
80
|
+
dirty: dirty,
|
|
81
|
+
merged: merged
|
|
82
|
+
}
|
|
83
|
+
end
|
|
84
|
+
|
|
55
85
|
# The worktree whose branch or directory basename is `name`.
|
|
56
86
|
def find(dir, name)
|
|
57
87
|
list(dir).find do |entry|
|
|
@@ -143,5 +173,30 @@ module Yamine
|
|
|
143
173
|
rescue SystemCallError, ArgumentError
|
|
144
174
|
["", nil]
|
|
145
175
|
end
|
|
176
|
+
|
|
177
|
+
# Return [short_sha, subject, author_name, author_email, iso_date]
|
|
178
|
+
# for the HEAD commit of `path`. Any field may be nil when git fails.
|
|
179
|
+
def commit_info(path)
|
|
180
|
+
out, status = git(path,
|
|
181
|
+
"log", "-1", "--format=%h\t%s\t%an\t%ae\t%aI")
|
|
182
|
+
return [nil, nil, nil, nil, nil] unless status&.success?
|
|
183
|
+
|
|
184
|
+
parts = out.strip.split("\t", 5)
|
|
185
|
+
[parts[0], parts[1], parts[2], parts[3], parts[4]]
|
|
186
|
+
rescue SystemCallError, ArgumentError
|
|
187
|
+
[nil, nil, nil, nil, nil]
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Resolve the default branch for merging comparisons.
|
|
191
|
+
def default_branch_for(repo)
|
|
192
|
+
Variant::DEFAULT_BRANCHES.each do |candidate|
|
|
193
|
+
return candidate if branch_exists?(repo, candidate)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
out, status = git(repo, "symbolic-ref", "--quiet", "refs/remotes/origin/HEAD")
|
|
197
|
+
return out.strip.sub(%r{\Arefs/remotes/origin/}, "") if status&.success? && !out.strip.empty?
|
|
198
|
+
|
|
199
|
+
"master"
|
|
200
|
+
end
|
|
146
201
|
end
|
|
147
202
|
end
|