diff --git a/lib/git/lib.rb b/lib/git/lib.rb index 741083df..108b1035 100644 --- a/lib/git/lib.rb +++ b/lib/git/lib.rb @@ -347,13 +347,37 @@ def change_head_branch(branch_name) command('symbolic-ref', 'HEAD', "refs/heads/#{branch_name}") end + BRANCH_LINE_REGEXP = / + ^ + # Prefix indicates if this branch is checked out. The prefix is one of: + (?: + (?\*[[:blank:]]) | # Current branch (checked out in the current worktree) + (?\+[[:blank:]]) | # Branch checked out in a different worktree + [[:blank:]]{2} # Branch not checked out + ) + + # The branch's full refname + (?[^[[:blank:]]]+) + + # Optional symref + # If this ref is a symbolic reference, this is the ref referenced + (?: + [[:blank:]]->[[:blank:]](?.*) + )? + $ + /x + def branches_all - arr = [] - command_lines('branch', '-a').each do |b| - current = (b[0, 2] == '* ') - arr << [b.gsub('* ', '').strip, current] + command_lines('branch', '-a').map do |line| + match_data = line.match(BRANCH_LINE_REGEXP) + raise GitExecuteError, 'Unexpected branch line format' unless match_data + [ + match_data[:refname], + !match_data[:current].nil?, + !match_data[:worktree].nil?, + match_data[:symref] + ] end - arr end def worktrees_all diff --git a/tests/units/test_branch.rb b/tests/units/test_branch.rb index 96585bdc..c7a12aee 100644 --- a/tests/units/test_branch.rb +++ b/tests/units/test_branch.rb @@ -26,9 +26,28 @@ def setup end end - def test_branches_all - assert(@git.branches[:master].is_a?(Git::Branch)) - assert(@git.branches.size > 5) + test 'Git::Base#branches' do + in_temp_dir do + remote_git = Git.init('remote_git', initial_branch: 'master') + File.write('remote_git/file.txt', 'hello world') + remote_git.add('file.txt') + remote_git.commit('Initial commit') + remote_branches = remote_git.branches + assert_equal(1, remote_branches.size) + assert(remote_branches.first.current) + assert_equal('master', remote_branches.first.name) + + # Test that remote tracking branches are handled correctly + # + local_git = Git.clone('remote_git/.git', 'local_git') + local_branches = assert_nothing_raised { local_git.branches } + assert_equal(3, local_branches.size) + assert(remote_branches.first.current) + local_branch_refs = local_branches.map(&:full) + assert_include(local_branch_refs, 'master') + assert_include(local_branch_refs, 'remotes/origin/master') + assert_include(local_branch_refs, 'remotes/origin/HEAD') + end end def test_branches_local