Module: Git::Parsers::Branch Private

Defined in:
lib/git/parsers/branch.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.

Parser for git branch command output

Handles parsing of git branch --list and git branch --delete output into structured data objects.

Design Note: Namespace Organization

This parser creates and returns BranchInfo and BranchDeleteResult 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/Result classes are public API - returned by commands and used throughout the codebase
  • Info classes are domain entities - represent core git concepts (branches as data)
  • Result classes are operation outcomes - represent command results, not parsing details

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

Constant Summary collapse

FORMAT_STRING =

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.

Format string for git branch --format

Fields (pipe-delimited):

  1. refname - full ref name (e.g., refs/heads/main, refs/remotes/origin/main)
  2. objectname - full SHA of the commit the branch points to
  3. HEAD - '*' if current branch, empty otherwise
  4. worktreepath - path if checked out in another worktree, empty otherwise
  5. symref - target ref if symbolic reference, empty otherwise
  6. upstream - full upstream ref (e.g., refs/remotes/origin/main), empty if none
'%(refname)|%(objectname)|%(HEAD)|%(worktreepath)|%(symref)|%(upstream)'
FIELD_DELIMITER =

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.

Delimiter used in format output

'|'
DELETED_BRANCH_REGEX =

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.

Regex to parse successful deletion lines from stdout Matches: Deleted branch branchname (was abc123). Matches: Deleted remote-tracking branch origin/branchname (was abc123). Uses non-greedy match to capture branch names containing spaces

/^Deleted (?:remote-tracking )?branch (.+?) \(was/
ERROR_BRANCH_REGEX =

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.

Regex to parse error messages from stderr Matches: error: branch 'branchname' not found.

/^error: branch '([^']+)'(.*)$/

Class Method Summary collapse

Class Method Details

.build_branch_info(fields) ⇒ Git::BranchInfo

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.

Build a BranchInfo from parsed fields

Parameters:

  • fields (Array<String>)

    the parsed fields: [refname, objectname, head, worktreepath, symref, upstream]

Returns:



94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/git/parsers/branch.rb', line 94

def build_branch_info(fields)
  raw_refname, objectname, head, worktreepath, symref, upstream = fields
  current = head == '*'

  Git::BranchInfo.new(
    refname: normalize_refname(raw_refname),
    target_oid: presence(objectname),
    current: current,
    worktree: in_other_worktree?(worktreepath, current),
    symref: presence(symref),
    upstream: build_upstream_info(upstream)
  )
end

.build_delete_result(requested_names, existing_branches, deleted_names, error_map) ⇒ Git::BranchDeleteResult

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.

Build the BranchDeleteResult from parsed data

Parameters:

  • requested_names (Array<String>)

    originally requested branch names

  • existing_branches (Hash<String, Git::BranchInfo>)

    branches that existed before delete

  • deleted_names (Array<String>)

    names confirmed deleted in stdout

  • error_map (Hash<String, String>)

    map of branch name to error message

Returns:



212
213
214
215
216
217
218
219
220
221
# File 'lib/git/parsers/branch.rb', line 212

def build_delete_result(requested_names, existing_branches, deleted_names, error_map)
  deleted = deleted_names.filter_map { |name| existing_branches[name] }

  not_deleted = (requested_names - deleted_names).map do |name|
    error_message = error_map[name] || "branch '#{name}' could not be deleted"
    Git::BranchDeleteFailure.new(name: name, error_message: error_message)
  end

  Git::BranchDeleteResult.new(deleted: deleted, not_deleted: not_deleted)
end

.build_upstream_info(upstream_ref) ⇒ Git::BranchInfo?

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.

Build upstream BranchInfo from upstream refname

Parameters:

  • upstream_ref (String, nil)

    the upstream ref (e.g., 'refs/remotes/origin/main')

Returns:



153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/git/parsers/branch.rb', line 153

def build_upstream_info(upstream_ref)
  return nil if upstream_ref.nil? || upstream_ref.empty?

  Git::BranchInfo.new(
    refname: normalize_refname(upstream_ref),
    target_oid: nil, # We don't have upstream's OID from this format
    current: false,
    worktree: false,
    symref: nil,
    upstream: nil # Upstream branches don't have their own upstream in this context
  )
end

.in_other_worktree?(worktreepath, current) ⇒ Boolean

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.

Check if the branch is checked out in another worktree

worktree is true when the branch is checked out in ANOTHER worktree (worktreepath is non-empty AND it's not the current branch)

Parameters:

  • worktreepath (String, nil)

    the worktree path from git output

  • current (Boolean)

    whether this is the current branch

Returns:

  • (Boolean)

    true if checked out in another worktree



143
144
145
146
# File 'lib/git/parsers/branch.rb', line 143

def in_other_worktree?(worktreepath, current)
  has_worktree = !worktreepath.nil? && !worktreepath.empty?
  has_worktree && !current
end

.non_branch_entry?(refname) ⇒ Boolean

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.

Check if the refname represents a detached HEAD state or non-branch entry

Git outputs special entries for detached HEAD and non-branch states:

  • "(HEAD detached at )" when in detached HEAD state
  • "(not a branch)" for non-branch entries

Parameters:

  • refname (String)

    the refname to check

Returns:

  • (Boolean)

    true if this is a non-branch entry



117
118
119
# File 'lib/git/parsers/branch.rb', line 117

def non_branch_entry?(refname)
  refname.match?(/^\(HEAD detached/) || refname.match?(/^\(not a branch\)/)
end

.normalize_refname(refname) ⇒ 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.

Normalize a full refname to the expected format

Converts:

  • refs/heads/main -> main
  • refs/remotes/origin/main -> remotes/origin/main

Parameters:

  • refname (String)

    the full refname from git

Returns:

  • (String)

    normalized refname



130
131
132
# File 'lib/git/parsers/branch.rb', line 130

def normalize_refname(refname)
  refname.sub(%r{^refs/heads/}, '').sub(%r{^refs/}, '')
end

.parse_branch_line(line) ⇒ Git::BranchInfo?

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 a single formatted branch line

Parameters:

  • line (String)

    the line to parse (pipe-delimited fields)

Returns:

  • (Git::BranchInfo, nil)

    branch info object, or nil if line should be skipped



81
82
83
84
85
86
87
# File 'lib/git/parsers/branch.rb', line 81

def parse_branch_line(line)
  fields = line.split(FIELD_DELIMITER, 6)

  return nil if non_branch_entry?(fields[0])

  build_branch_info(fields)
end

.parse_deleted_branches(stdout) ⇒ 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.

Parse deleted branch names from stdout

Examples:

BranchParser.parse_deleted_branches("Deleted branch feature (was abc123).\n")
# => ["feature"]

Parameters:

  • stdout (String)

    command stdout

Returns:

  • (Array<String>)

    names of successfully deleted branches



184
185
186
# File 'lib/git/parsers/branch.rb', line 184

def parse_deleted_branches(stdout)
  stdout.scan(DELETED_BRANCH_REGEX).flatten
end

.parse_error_messages(stderr) ⇒ Hash<String, 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.

Parse error messages from stderr into a map

Examples:

BranchParser.parse_error_messages("error: branch 'missing' not found.\n")
# => {"missing" => "error: branch 'missing' not found."}

Parameters:

  • stderr (String)

    command stderr

Returns:

  • (Hash<String, String>)

    map of branch name to error message



197
198
199
200
201
202
# File 'lib/git/parsers/branch.rb', line 197

def parse_error_messages(stderr)
  stderr.each_line.with_object({}) do |line, hash|
    match = line.match(ERROR_BRANCH_REGEX)
    hash[match[1]] = line.strip if match
  end
end

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

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 branch --list output into BranchInfo objects

Examples:

Git::Parsers::Branch.parse_list("refs/heads/main|abc1234|*|||\nrefs/heads/feature|def5678||||\n")
# => [#<data Git::BranchInfo refname="main", ...>, #<data Git::BranchInfo refname="feature", ...>]

Parameters:

  • stdout (String)

    output from git branch --list --format=...

Returns:



72
73
74
# File 'lib/git/parsers/branch.rb', line 72

def parse_list(stdout)
  stdout.split("\n").filter_map { |line| parse_branch_line(line) }
end

.presence(value) ⇒ 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.

Return value if non-empty, nil otherwise

Parameters:

  • value (String, nil)

    the value to check

Returns:

  • (String, nil)

    the value or nil



171
172
173
# File 'lib/git/parsers/branch.rb', line 171

def presence(value)
  value.nil? || value.empty? ? nil : value
end