Module: Git::Parsers::Worktree Private

Defined in:
lib/git/parsers/worktree.rb

Overview

This module is part of a private API. You should avoid using this module if possible, as it may be removed or be changed in the future.

Note:

Known limitation: git C-quotes a lock or prune reason that contains unusual characters such as a newline or a non-ASCII byte (see the --porcelain description in the git-worktree documentation). The reason is returned as git prints it, quotes and escapes included; it is not unquoted.

Parser for git worktree command output

Handles parsing of git worktree list --porcelain output into structured data objects.

Design Note: Namespace Organization

This parser creates and returns WorktreeInfo objects, which live at the top-level Git:: namespace rather than within Git::Parsers::. This is intentional:

  • Parsers are infrastructure - marked @api private, users shouldn't interact with them directly
  • Info classes are public API - returned by commands and used throughout the codebase
  • Info classes are domain entities - represent core git concepts (worktrees as data)

Keeping Info classes at Git:: improves discoverability and correctly reflects their role as public types rather than parser internals.

Constant Summary collapse

LINE_PATTERN =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Pattern splitting a porcelain line into its key and optional value

The key is everything before the first space and the value is everything after it, so a path or reason that contains spaces is kept intact. The pattern matches every line; a line with no space has a nil value.

/\A(?<key>[^ ]*)(?: (?<value>.*))?\z/
DEFAULT_ATTRS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Attribute values for a worktree with no flags set

Returns:

{
  head: nil, branch: nil, bare: false, detached: false,
  locked: false, lock_reason: nil, prunable: false, prune_reason: nil
}.freeze

Class Method Summary collapse

Class Method Details

.line_attrs(line, stdout) ⇒ Hash{Symbol => Object}

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Map one porcelain line to the WorktreeInfo attributes it sets

Parameters:

  • line (String)

    a chomped line of porcelain output

  • stdout (String)

    the full output (for error messages)

Returns:

  • (Hash{Symbol => Object})

    the attributes set by the line

Raises:



136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/git/parsers/worktree.rb', line 136

def line_attrs(line, stdout)
  key, value = split_line(line)

  case key
  when 'HEAD' then { head: value }
  when 'branch' then { branch: value }
  when 'bare' then { bare: true }
  when 'detached' then { detached: true }
  when 'locked' then { locked: true, lock_reason: value }
  when 'prunable' then { prunable: true, prune_reason: value }
  else raise Git::UnexpectedResultError, unexpected_line_error(stdout, line, 'unrecognized key')
  end
end

.parse_list(stdout) ⇒ Array<Git::WorktreeInfo>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parse git worktree list --porcelain output into WorktreeInfo objects

Records are separated by a blank line. Each record starts with a worktree <path> line followed by any of HEAD <sha>, branch <ref>, bare, detached, locked [<reason>], and prunable <reason>.

Examples:

Git::Parsers::Worktree.parse_list(
  "worktree /tmp/wt/main\nHEAD f3e2c1f...\nbranch refs/heads/main\n"
)
# => [#<data Git::WorktreeInfo path="/tmp/wt/main", ...>]

Parameters:

  • stdout (String)

    output from git worktree list --porcelain

Returns:

  • (Array<Git::WorktreeInfo>)

    one entry per worktree, in the order git listed them (the main worktree first)

Raises:



74
75
76
# File 'lib/git/parsers/worktree.rb', line 74

def parse_list(stdout)
  records(stdout).map { |lines| parse_record(lines, stdout) }
end

.parse_record(lines, stdout) ⇒ Git::WorktreeInfo

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parse one record into a WorktreeInfo

Parameters:

  • lines (Array<String>)

    the lines of the record

  • stdout (String)

    the full output (for error messages)

Returns:

Raises:



102
103
104
105
106
107
108
109
110
# File 'lib/git/parsers/worktree.rb', line 102

def parse_record(lines, stdout)
  key, path = split_line(lines.first)
  unless key == 'worktree' && path
    raise Git::UnexpectedResultError,
          unexpected_line_error(stdout, lines.first, 'expected a record to start with "worktree <path>"')
  end

  Git::WorktreeInfo.new(path: path, **DEFAULT_ATTRS, **record_attrs(lines.drop(1), stdout))
end

.record_attrs(lines, stdout) ⇒ Hash{Symbol => Object}

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Collect the attributes set by the lines that follow the worktree line

Parameters:

  • lines (Array<String>)

    the record's lines after the first

  • stdout (String)

    the full output (for error messages)

Returns:

  • (Hash{Symbol => Object})

    the attributes to override in DEFAULT_ATTRS

Raises:



122
123
124
# File 'lib/git/parsers/worktree.rb', line 122

def record_attrs(lines, stdout)
  lines.each_with_object({}) { |line, attrs| attrs.merge!(line_attrs(line, stdout)) }
end

.records(stdout) ⇒ Array<Array<String>>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Split the output into records, each an array of chomped non-blank lines

Blank lines separate records. chunk drops every run of lines whose block value is :_separator, so only the non-blank runs are returned.

Parameters:

  • stdout (String)

    output from git worktree list --porcelain

Returns:

  • (Array<Array<String>>)

    the lines of each record



87
88
89
# File 'lib/git/parsers/worktree.rb', line 87

def records(stdout)
  stdout.each_line(chomp: true).chunk { |line| line.empty? ? :_separator : true }.map { |_, lines| lines }
end

.split_line(line) ⇒ Array(String, String), Array(String, nil)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Split a porcelain line into its key and optional value

Parameters:

  • line (String)

    a chomped line of porcelain output

Returns:

  • (Array(String, String), Array(String, nil))

    the key and the value, or nil when the line has no value



157
158
159
160
# File 'lib/git/parsers/worktree.rb', line 157

def split_line(line)
  match = LINE_PATTERN.match(line)
  [match[:key], match[:value]]
end

.unexpected_line_error(stdout, line, reason) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Generate the error message for a line the parser cannot handle

Parameters:

  • stdout (String)

    the full output

  • line (String)

    the problematic line

  • reason (String)

    why the line is unexpected

Returns:

  • (String)

    the formatted error message



172
173
174
175
176
177
178
179
180
181
182
# File 'lib/git/parsers/worktree.rb', line 172

def unexpected_line_error(stdout, line, reason)
  <<~ERROR
    Unexpected line in output from `git worktree list --porcelain`: #{reason}

    Line:
      "#{line}"

    Full output:
      #{stdout.gsub("\n", "\n  ")}
  ERROR
end