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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 43b8c059b0635ce4272b806fd2b8f70ae42ed6d591ed87536de9c86ed0419864
4
- data.tar.gz: c34fb3a40d5c16442accee4dcbdb32b1b2b5013de5c3a823bade745b6ac65e88
3
+ metadata.gz: 4f7d162f5c1ad28d48339119a7a5f6c15e1d1b269a8e33a0a6f21b51d24be07f
4
+ data.tar.gz: 795408acb99c284062307bd7e08217af3b1806ccb0d4b6b47c2b0841e0439a83
5
5
  SHA512:
6
- metadata.gz: 70495a26f226c3b0dac853c5a4ce1694207f4a9bbb73af92b44b37cc17ff3c13430af7440252102b8acd40f22ee7efdae5dfec26155678a5839e30b10c5c0d1a
7
- data.tar.gz: e450abc81d00dad86bf03e540304ad43683e99329a60eb24ddea0122760048c88729e7fe33c9b93056a1840ec871e488ea458d1afdbd655b0dccd477369b8cb5
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
@@ -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"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Yamine
4
- VERSION = "0.18.0"
4
+ VERSION = "0.19.0"
5
5
  end
@@ -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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yamine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.18.0
4
+ version: 0.19.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kaka Ruto