Class: Ace::Git::Worktree::Models::WorktreeInfo

Inherits:
Object
  • Object
show all
Defined in:
lib/ace/git/worktree/models/worktree_info.rb

Overview

Worktree information model

Represents information about a git worktree, including its path, branch, commit, and associated task metadata.

Examples:

Create from git worktree list output

info = WorktreeInfo.from_git_output("/path/to/worktree abc123 [branch-name]")

Create manually

info = WorktreeInfo.new(
  path: "/project/.ace-wt/task.081",
  branch: "081-fix-auth-bug",
  commit: "abc123",
  task_id: "081"
)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path:, commit:, branch: nil, task_id: nil, bare: false, detached: false, created_at: nil) ⇒ WorktreeInfo

Initialize a new WorktreeInfo

Parameters:

  • path (String)

    Path to the worktree directory

  • branch (String, nil) (defaults to: nil)

    Branch name (nil if detached HEAD)

  • commit (String)

    Commit hash

  • task_id (String, nil) (defaults to: nil)

    Associated task ID

  • bare (Boolean) (defaults to: false)

    Whether this is a bare worktree

  • detached (Boolean) (defaults to: false)

    Whether worktree is in detached HEAD state

  • created_at (Time, nil) (defaults to: nil)

    When the worktree was created



36
37
38
39
40
41
42
43
44
# File 'lib/ace/git/worktree/models/worktree_info.rb', line 36

def initialize(path:, commit:, branch: nil, task_id: nil, bare: false, detached: false, created_at: nil)
  @path = path
  @branch = branch
  @commit = commit
  @task_id = task_id
  @bare = bare
  @detached = detached
  @created_at = created_at
end

Instance Attribute Details

#bareObject (readonly)

Returns the value of attribute bare.



25
26
27
# File 'lib/ace/git/worktree/models/worktree_info.rb', line 25

def bare
  @bare
end

#branchObject (readonly)

Returns the value of attribute branch.



25
26
27
# File 'lib/ace/git/worktree/models/worktree_info.rb', line 25

def branch
  @branch
end

#commitObject (readonly)

Returns the value of attribute commit.



25
26
27
# File 'lib/ace/git/worktree/models/worktree_info.rb', line 25

def commit
  @commit
end

#created_atObject (readonly)

Returns the value of attribute created_at.



25
26
27
# File 'lib/ace/git/worktree/models/worktree_info.rb', line 25

def created_at
  @created_at
end

#detachedObject (readonly)

Returns the value of attribute detached.



25
26
27
# File 'lib/ace/git/worktree/models/worktree_info.rb', line 25

def detached
  @detached
end

#pathObject (readonly)

Returns the value of attribute path.



25
26
27
# File 'lib/ace/git/worktree/models/worktree_info.rb', line 25

def path
  @path
end

#task_idObject (readonly)

Returns the value of attribute task_id.



25
26
27
# File 'lib/ace/git/worktree/models/worktree_info.rb', line 25

def task_id
  @task_id
end

Class Method Details

.extract_task_id(path, branch) ⇒ String?

Extract task ID from path or branch name

Parameters:

  • path (String)

    Worktree path

  • branch (String, nil)

    Branch name

Returns:

  • (String, nil)

    Extracted task ID or nil



280
281
282
283
284
285
286
287
# File 'lib/ace/git/worktree/models/worktree_info.rb', line 280

def self.extract_task_id(path, branch)
  # Try to extract from path first (e.g., task.081, 081-work)
  path_task_id = extract_task_id_from_string(File.basename(path))
  return path_task_id if path_task_id

  # Try to extract from branch name (e.g., 081-fix-something)
  extract_task_id_from_string(branch)
end

.extract_task_id_from_string(string) ⇒ String?

Extract task ID from a string using common patterns

Parameters:

  • string (String, nil)

    String to search

Returns:

  • (String, nil)

    Extracted task ID or nil (preserves subtask IDs like “121.01”)



293
294
295
296
297
298
# File 'lib/ace/git/worktree/models/worktree_info.rb', line 293

def self.extract_task_id_from_string(string)
  return nil if string.nil? || string.empty?

  # Use shared extractor that preserves subtask IDs (e.g., "121.01")
  Atoms::TaskIDExtractor.normalize(string)
end

.find_by_branch(worktrees, branch) ⇒ WorktreeInfo?

Find worktree info by branch name

Parameters:

  • worktrees (Array<WorktreeInfo>)

    List of worktree info

  • branch (String)

    Branch name to search for

Returns:



221
222
223
# File 'lib/ace/git/worktree/models/worktree_info.rb', line 221

def self.find_by_branch(worktrees, branch)
  worktrees.find { |worktree| worktree.branch == branch.to_s }
end

.find_by_directory(worktrees, directory) ⇒ WorktreeInfo?

Find worktree info by directory name

Parameters:

  • worktrees (Array<WorktreeInfo>)

    List of worktree info

  • directory (String)

    Directory name to search for

Returns:



212
213
214
# File 'lib/ace/git/worktree/models/worktree_info.rb', line 212

def self.find_by_directory(worktrees, directory)
  worktrees.find { |worktree| worktree.directory_name == directory.to_s }
end

.find_by_task_id(worktrees, task_id) ⇒ WorktreeInfo?

Find worktree info by task ID

Parameters:

  • worktrees (Array<WorktreeInfo>)

    List of worktree info

  • task_id (String)

    Task ID to search for

Returns:



203
204
205
# File 'lib/ace/git/worktree/models/worktree_info.rb', line 203

def self.find_by_task_id(worktrees, task_id)
  worktrees.find { |worktree| worktree.task_id == task_id.to_s }
end

.from_git_output(line) ⇒ WorktreeInfo?

Parse git worktree list output line

Examples:

WorktreeInfo.from_git_output("/path/to/worktree abc123 [branch-name]")
WorktreeInfo.from_git_output("/path/to/worktree abc123 (detached HEAD)")

Parameters:

  • line (String)

    Output line from ‘git worktree list`

Returns:

  • (WorktreeInfo, nil)

    Parsed worktree info or nil if parsing failed



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/ace/git/worktree/models/worktree_info.rb', line 102

def self.from_git_output(line)
  return nil if line.nil? || line.strip.empty?

  # Git worktree list format:
  # /path/to/worktree abc123 [branch-name]
  # /path/to/worktree abc123 (detached HEAD)
  # /path/to/worktree abc123 + [branch-name]  (worktree has changes)

  parts = line.strip.split(/\s+/, 3)
  return nil if parts.length < 2

  path = parts[0]
  commit = parts[1]
  branch = nil
  bare = false
  detached = false

  # Parse the third part if present
  if parts.length >= 3
    third_part = parts[2]

    if third_part.include?("[") && third_part.include?("]")
      # Branch worktree: [branch-name]
      branch_match = third_part.match(/\[([^\]]+)\]/)
      branch = branch_match[1] if branch_match
      bare = third_part.include?("bare")
    elsif third_part.include?("detached HEAD")
      # Detached HEAD worktree
      detached = true
    end
  end

  # Try to extract task ID from path or branch
  task_id = extract_task_id(path, branch)

  new(
    path: path,
    branch: branch,
    commit: commit,
    task_id: task_id,
    bare: bare,
    detached: detached
  )
end

.from_git_output_list(output) ⇒ Array<WorktreeInfo>

Parse multiple lines from git worktree list output

Examples:

worktrees = WorktreeInfo.from_git_output_list(`git worktree list --porcelain`)

Parameters:

  • output (String)

    Full output from ‘git worktree list –porcelain`

Returns:



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/ace/git/worktree/models/worktree_info.rb', line 154

def self.from_git_output_list(output)
  return [] if output.nil? || output.empty?

  # Split by blank lines to get per-worktree blocks
  blocks = output.strip.split(/\n\n+/)
  worktrees = []

  blocks.each do |block|
    lines = block.strip.split("\n").map(&:strip)
    next unless lines.first&.start_with?("worktree ")

    path = lines.first.sub(/^worktree\s+/, "")
    commit = nil
    branch = nil
    detached = false
    bare = false

    lines[1..].each do |line|
      if line.start_with?("HEAD ")
        commit = line.sub(/^HEAD\s+/, "")
      elsif line.start_with?("branch ")
        branch_ref = line.sub(/^branch\s+/, "")
        if branch_ref.start_with?("refs/heads/")
          branch = branch_ref.sub(/^refs\/heads\//, "")
        end
      elsif line == "detached"
        detached = true
      elsif line == "bare"
        bare = true
      end
      # skip: locked, prunable, empty lines
    end

    # Detached if no branch line was found and not bare
    detached = true if branch.nil? && !bare && commit

    task_id = extract_task_id(path, branch)
    worktrees << new(path: path, branch: branch, commit: commit,
      task_id: task_id, bare: bare, detached: detached)
  end

  worktrees
end

Instance Method Details

#==(other) ⇒ Boolean Also known as: eql?

Equality comparison

Parameters:

Returns:

  • (Boolean)

    true if equal



255
256
257
258
259
260
261
262
# File 'lib/ace/git/worktree/models/worktree_info.rb', line 255

def ==(other)
  return false unless other.is_a?(WorktreeInfo)

  @path == other.path &&
    @branch == other.branch &&
    @commit == other.commit &&
    @task_id == other.task_id
end

#descriptionString

Get a human-readable description

Returns:

  • (String)

    Human-readable description



86
87
88
89
90
91
92
# File 'lib/ace/git/worktree/models/worktree_info.rb', line 86

def description
  if task_associated?
    "Task #{@task_id}: #{@branch} at #{@path}"
  else
    "#{@branch || @commit[0, 8]} at #{@path}"
  end
end

#directory_nameString

Get the worktree directory name

Returns:

  • (String)

    Directory name (basename of path)



63
64
65
# File 'lib/ace/git/worktree/models/worktree_info.rb', line 63

def directory_name
  File.basename(@path)
end

#empty?Boolean

Check if the worktree directory is empty

Returns:

  • (Boolean)

    true if directory is empty



77
78
79
80
81
# File 'lib/ace/git/worktree/models/worktree_info.rb', line 77

def empty?
  return true unless exists?

  Dir.empty?(@path)
end

#exists?Boolean

Check if the worktree directory exists

Returns:

  • (Boolean)

    true if directory exists



70
71
72
# File 'lib/ace/git/worktree/models/worktree_info.rb', line 70

def exists?
  File.directory?(@path)
end

#hashInteger

Hash for using as hash keys

Returns:

  • (Integer)

    Hash value



269
270
271
# File 'lib/ace/git/worktree/models/worktree_info.rb', line 269

def hash
  [@path, @branch, @commit, @task_id].hash
end

#task_associated?Boolean

Check if the worktree is associated with a task

Returns:

  • (Boolean)

    true if task_id is present



49
50
51
# File 'lib/ace/git/worktree/models/worktree_info.rb', line 49

def task_associated?
  !@task_id.nil? && !@task_id.empty?
end

#to_hHash

Convert to hash

Returns:

  • (Hash)

    Worktree info as hash



228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/ace/git/worktree/models/worktree_info.rb', line 228

def to_h
  {
    path: @path,
    branch: @branch,
    commit: @commit,
    task_id: @task_id,
    bare: @bare,
    detached: @detached,
    created_at: @created_at,
    usable: usable?,
    task_associated: task_associated?,
    exists: exists?,
    empty: empty?
  }
end

#to_json(*args) ⇒ String

Convert to JSON

Returns:

  • (String)

    JSON representation



247
248
249
# File 'lib/ace/git/worktree/models/worktree_info.rb', line 247

def to_json(*args)
  to_h.to_json(*args)
end

#usable?Boolean

Check if the worktree is in a usable state

Returns:

  • (Boolean)

    true if worktree is not bare or detached



56
57
58
# File 'lib/ace/git/worktree/models/worktree_info.rb', line 56

def usable?
  !@bare && !@detached
end