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

Issue:
Git::Branch function update_ref doesn't work as expected
### Subject of the issue
Git::Branch#update_ref creates the wrong ref name, resulting in "refname is ambiguous" warnings.

### Your environment
* git 2.16.2
* ruby 2.3.1
* ruby-git 1.12.0

### Steps to reproduce
```ruby
require 'git'
git = Git.init
File.write('foo','rev 1')
git.add('foo')
git.commit('rev 1')
git.branch('testing').create
puts "HEAD", git.revparse('HEAD')
puts "testing", git.revparse('testing')
puts "refs/heads/testing", git.revparse('refs/heads/testing')
puts "\n\n"
File.write('foo','rev 2')
git.add('foo')
git.commit('rev 2')
git.branch('testing').update_ref(git.revparse('HEAD'))
puts "HEAD", git.revparse('HEAD')
puts "testing", git.revparse('testing')
puts "refs/heads/testing", git.revparse('refs/heads/testing')
```

### Expected behaviour
I'd expect `git.branch('testing').update_ref(<HASH>)` to behave the same as `git branch -f testing <HASH>` from the command line.

If that were the case, the code above should print the following (sha values may vary):
```
HEAD
2a0b5b7b...
testing
2a0b5b7b...
refs/heads/testing
2a0b5b7b...

HEAD
decf91cb...
testing
decf91cb...
refs/heads/testing
decf91cb...
```

### Actual behaviour
The code above prints this:
```
HEAD
2a0b5b7b...
testing
2a0b5b7b...
refs/heads/testing
2a0b5b7b...

HEAD
decf91cb...
testing
warning: refname 'testing' is ambiguous.
decf91cb...
refs/heads/testing
2a0b5b7b...
```

It looks like instead of updating the file `.git/refs/heads/testing` with the new hash, instead the call to `update_ref()` creates a new file `.git/testing` with the new hash.

### Discussion
For reference, this is the current implementation of Git::Branch#update_ref:

```ruby
def update_ref(commit)
  @base.lib.update_ref(@full, commit)
end
```

And this is the implementation of Git::Lib#update_ref that it's calling:

```ruby
def update_ref(branch, commit)
  command('update-ref', [branch, commit])
end
```

I think the `branch` naming of that function argument is problematic, since it just passes through to [the `git update-ref` command](https://git-scm.com/docs/git-update-ref), which accepts a 'ref' as an argument, not a branch name.  If we really intend for that function to accept a branch name, then the implementation should probably change to prepend `refs/heads/` before calling the command.  However, this makes the behavior of the function diverge from the behavior of the command, which is probably undesirable.

With that in mind, I think the fix needs to happen in Git::Branch#update_ref to call Git::Lib#update_ref with the right ref name instead of just using the 'full branch' name.

### Recommendation

Git::Branch#update_ref:
```ruby
def update_ref(commit)
  if @remote
    @base.lib.update_ref("refs/remotes/#{@remote.name}/#{@name}", commit)
  else
    @base.lib.update_ref("refs/heads/#{@name}", commit)
  end
end
```

Git::Lib#update_ref:
```ruby
def update_ref(ref, commit)
  command('update-ref', [ref, commit])
end
```
Git::Branch private function parse_name doesn't work as documented
### Subject of the issue
The code comments right before `parse_name` say one thing, but the code doesn't do it.

### Your environment
* git 2.16.2
* ruby 2.3.1
* ruby-git 1.12.0

### Steps to reproduce
```ruby
require 'git'
git = Git.open(Dir.pwd)
['origin','origin/master','remotes/origin/master','origin/master/v2'].each do |name|
  puts "Testing #{name}"
  puts git.branch(name).remote
  puts git.branch(name).name
  puts "\n"
end
```

### Expected behaviour
Based on this comment in `branch.rb`:
```
      # Example:
      #   parse_name('master') #=> [nil, 'master']
      #   parse_name('origin/master') #=> ['origin', 'master']
      #   parse_name('remotes/origin/master') #=> ['origin', 'master']
      #   parse_name('origin/master/v2') #=> ['origin', 'master/v2'] 
```
I'd expect the above code to print this:
```
Testing origin
nil
master

Testing origin/master
origin
master

Testing remotes/origin/master
origin
master

Testing origin/master/v2
origin
master/v2
```

### Actual behaviour
The above code prints this:
```
Testing origin
nil
master

Testing origin/master
nil
origin/master

Testing remotes/origin/master
origin
master

Testing origin/master/v2
nil
origin/master/v2
```

### Recommendation
It looks like the regexp in `parse_name` is wrong.  It should include the slash after 'remotes' in the non-capturing group:
```ruby
        if name.match(/^(?:remotes\/)?([^\/]+)\/(.+)/)
```


Discussion:

Patch:
diff --git a/lib/git/branch.rb b/lib/git/branch.rb
index c38c9d4a..31c482e8 100644
--- a/lib/git/branch.rb
+++ b/lib/git/branch.rb
@@ -78,7 +78,11 @@ def merge(branch = nil, message = nil)
     end
     
     def update_ref(commit)
-      @base.lib.update_ref(@full, commit)
+      if @remote
+        @base.lib.update_ref("refs/remotes/#{@remote.name}/#{@name}", commit)
+      else
+        @base.lib.update_ref("refs/heads/#{@name}", commit)
+      end
     end
     
     def to_a
@@ -114,7 +118,7 @@ def determine_current
       # param [String] name branch full name.
       # return [<Git::Remote,NilClass,String>] an Array containing the remote and branch names. 
       def parse_name(name)
-        if name.match(/^(?:remotes)?\/([^\/]+)\/(.+)/)
+        if name.match(/^(?:remotes\/)?([^\/]+)\/(.+)/)
           return [Git::Remote.new(@base, $1), $2]
         end
 
diff --git a/lib/git/lib.rb b/lib/git/lib.rb
index 82156eda..67130306 100644
--- a/lib/git/lib.rb
+++ b/lib/git/lib.rb
@@ -974,8 +974,8 @@ def commit_tree(tree, opts = {})
       command('commit-tree', *arr_opts, redirect: "< #{escape t.path}")
     end
 
-    def update_ref(branch, commit)
-      command('update-ref', branch, commit)
+    def update_ref(ref, commit)
+      command('update-ref', ref, commit)
     end
 
     def checkout_index(opts = {})


Test patch:
diff --git a/tests/units/test_branch.rb b/tests/units/test_branch.rb
index 8fdc1db7..e3acd0d2 100644
--- a/tests/units/test_branch.rb
+++ b/tests/units/test_branch.rb
@@ -98,4 +98,22 @@ def test_branch_create_and_switch
       assert(git.branch('other_branch').current)
     end
   end
+
+  def test_branch_update_ref
+    in_temp_dir do |path|
+      git = Git.init
+      File.write('foo','rev 1')
+      git.add('foo')
+      git.commit('rev 1')
+      git.branch('testing').create
+      File.write('foo','rev 2')
+      git.add('foo')
+      git.commit('rev 2')
+      git.branch('testing').update_ref(git.revparse('HEAD'))
+
+      # Expect the call to Branch#update_ref to pass the full ref name for the
+      # of the testing branch to Lib#update_ref
+      assert_equal(git.revparse('HEAD'), git.revparse('refs/heads/testing'))
+    end
+  end
 end