Repository: ruby-git/ruby-git (Ruby)

Issue:
First remote branch "HEAD -> origin/master" is a special case and fails gcommit.sha
### Subject of the issue
First remote branch `HEAD -> origin/master` is a special case and fails `gcommit.sha`
So something like this fails on first iteration
```
g.branches.remote.each do |branch|
  branch.gcommit.sha
end
```

### Your environment
* git version 2.17.1
* ruby-git - git (1.7.0)
* ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-linux]

### Steps to reproduce
```
require 'git'

g = Git.clone("https://github.com/ruby-git/ruby-git","/tmp/github.com/ruby-git/ruby-git")
g.branches.remote.first.gcommit.sha
```

### Expected behaviour
Return the sha of the commit from branch master

### Actual behaviour
```
Git::GitExecuteError (git '--git-dir=/tmp/github.com/ruby-git/ruby-git/.git' '--work-tree=/tmp/github.com/ruby-git/ruby-git' '-c' 'color.ui=false' rev-parse 'remotes/origin/HEAD -> origin/master'  2>&1:fatal: ambiguous argument 'remotes/origin/HEAD -> origin/master': unknown revision or path not in the working tree.)
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'
remotes/origin/HEAD -> origin/master
```


Discussion:
A friendly reminder that this issue had no activity for 60 days.




Patch:
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:
+        (?:
+          (?<current>\*[[:blank:]]) |  # Current branch (checked out in the current worktree)
+          (?<worktree>\+[[:blank:]]) | # Branch checked out in a different worktree
+          [[:blank:]]{2}               # Branch not checked out
+        )
+
+        # The branch's full refname
+        (?<refname>[^[[:blank:]]]+)
+
+        # Optional symref
+        # If this ref is a symbolic reference, this is the ref referenced
+        (?:
+          [[:blank:]]->[[:blank:]](?<symref>.*)
+        )?
+      $
+    /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


Test patch:
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