Showing with 130 additions and 126 deletions.
  1. +1 −1 .travis.yml
  2. +6 −2 Gemfile
  3. +5 −9 Gemfile.lock
  4. +1 −1 Rakefile
  5. +1 −0 features/pull_request.feature
  6. +6 −5 hub.gemspec
  7. +72 −44 lib/hub/context.rb
  8. +1 −0 lib/hub/runner.rb
  9. +12 −57 lib/hub/ssh_config.rb
  10. +1 −1 lib/hub/version.rb
  11. +24 −6 test/hub_test.rb
@@ -1,7 +1,7 @@
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq tmux zsh git
install: script/cached-bundle install --deployment
install: script/cached-bundle install --deployment --without development
script: script/test
language: ruby
rvm:
@@ -1,11 +1,15 @@
source 'https://rubygems.org'

gem 'rake', '~> 10.1.1'
gem 'minitest'
gem 'ronn', :platform => :mri
gem 'aruba', '~> 0.5.3'
gem 'cucumber', '~> 1.3.9'
gem 'sinatra'
gem 'json'
gem 'jruby-openssl', :platform => :jruby
gem 'webmock', '~> 1.9.0'

gemspec
group :development do
gem 'ronn', :platform => :mri
gem 'ruby-prof'
end
@@ -1,8 +1,3 @@
PATH
remote: .
specs:
hub (1.11.0)

GEM
remote: https://rubygems.org/
specs:
@@ -41,14 +36,15 @@ GEM
rack (1.4.1)
rack-protection (1.2.0)
rack
rake (0.9.2.2)
rake (10.1.1)
rdiscount (1.6.8)
ronn (0.7.3)
hpricot (>= 0.8.2)
mustache (>= 0.7.0)
rdiscount (>= 1.5.8)
rspec-expectations (2.14.4)
diff-lcs (>= 1.1.3, < 2.0)
ruby-prof (0.13.1)
sinatra (1.3.2)
rack (~> 1.3, >= 1.3.6)
rack-protection (~> 1.2)
@@ -65,11 +61,11 @@ PLATFORMS
DEPENDENCIES
aruba (~> 0.5.3)
cucumber (~> 1.3.9)
hub!
jruby-openssl
json
minitest
rake
rake (~> 10.1.1)
ronn
ruby-prof
sinatra
webmock
webmock (~> 1.9.0)
@@ -173,7 +173,7 @@ task :homebrew do
sh "git checkout -q -B #{branch}"
sh "git commit -m 'hub v#{Hub::VERSION}' -- #{formula_file}"
sh "git push -u mislav #{branch}"
sh "hub pull-request 'upgrade hub to v#{Hub::VERSION}'"
sh "hub pull-request -m 'upgrade hub to v#{Hub::VERSION}'"

sh "git checkout -q master"
end
@@ -57,6 +57,7 @@ Feature: hub pull-request
hub: Specifying pull request title without a flag is deprecated.
Please use one of `-m' or `-F' options.\n
"""
And the stdout should contain exactly "the://url\n"

Scenario: Non-existing base
Given the GitHub API server:
@@ -10,15 +10,16 @@ Gem::Specification.new do |s|
s.authors = [ "Chris Wanstrath", "Mislav Marohnić" ]
s.license = "MIT"

s.add_development_dependency 'rake'
s.add_development_dependency 'webmock'

s.files = %w( README.md Rakefile LICENSE )
s.files += Dir.glob("lib/**/*")
s.files += Dir.glob("bin/**/*")
s.files += Dir.glob("man/**/*")
s.files += Dir.glob("script/**/*")
s.files += Dir.glob("test/**/*")

# include only files in version control
git_dir = File.expand_path('../.git', __FILE__)
if File.directory?(git_dir)
s.files &= `git --git-dir='#{git_dir}' ls-files -z`.split("\0")
end

s.executables = %w( hub )
s.description = <<desc
@@ -18,7 +18,8 @@ def initialize(executable = nil, &read_proc)
@executable = executable || 'git'
# caches output when shelling out to git
read_proc ||= lambda { |cache, cmd|
result = %x{#{command_to_string(cmd)} 2>#{NULL}}.chomp
str = command_to_string(cmd)
result = silence_stderr { %x{#{str}}.chomp }
cache[cmd] = $?.success? && !result.empty? ? result : nil
}
@cache = Hash.new(&read_proc)
@@ -61,6 +62,14 @@ def command_to_string(cmd)
full_cmd = to_exec(cmd)
full_cmd.respond_to?(:shelljoin) ? full_cmd.shelljoin : full_cmd.join(' ')
end

def silence_stderr
oldio = STDERR.dup
STDERR.reopen(NULL)
yield
ensure
STDERR.reopen(oldio)
end
end

module GitReaderMethods
@@ -88,17 +97,19 @@ def git_reader
private :git_config, :git_command

def local_repo(fatal = true)
@local_repo ||= begin
if is_repo?
LocalRepo.new git_reader, current_dir
return nil if defined?(@local_repo) && @local_repo == false
@local_repo =
if git_dir = git_command('rev-parse -q --git-dir')
LocalRepo.new(git_reader, current_dir, git_dir)
elsif fatal
raise FatalError, "Not a git repository"
else
false
end
end
end

repo_methods = [
:current_branch,
:current_branch, :git_dir,
:remote_branch_and_project,
:repo_owner, :repo_host,
:remotes, :remotes_group, :origin_remote
@@ -116,7 +127,7 @@ def master_branch
end
end

class LocalRepo < Struct.new(:git_reader, :dir)
class LocalRepo < Struct.new(:git_reader, :dir, :git_dir)
include GitReaderMethods

def name
@@ -151,28 +162,49 @@ def remote_branch_and_project(username_fetcher)
end

def current_branch
if branch = git_command('symbolic-ref -q HEAD')
Branch.new self, branch
@current_branch ||= branch_at_ref('HEAD')
end

def branch_at_ref(*parts)
begin
head = file_read(*parts)
rescue Errno::ENOENT
return nil
else
Branch.new(self, head.rstrip) if head.sub!('ref: ', '')
end
end

def file_read(*parts)
File.read(File.join(git_dir, *parts))
end

def file_exist?(*parts)
File.exist?(File.join(git_dir, *parts))
end

def master_branch
if remote = origin_remote
default_branch = git_command("rev-parse --symbolic-full-name #{remote}")
default_branch = branch_at_ref("refs/remotes/#{remote}/HEAD")
end
Branch.new(self, default_branch || 'refs/heads/master')
default_branch || Branch.new(self, 'refs/heads/master')
end

ORIGIN_NAMES = %w[ upstream github origin ]

def remotes
@remotes ||= begin
# TODO: is there a plumbing command to get a list of remotes?
list = git_command('remote').to_s.split("\n")
list = ORIGIN_NAMES.inject([]) { |sorted, name|
sorted << list.delete(name)
}.compact.concat(list)
list.map { |name| Remote.new self, name }
names = []
url_memo = Hash.new {|h,k| names << k; h[k]=[] }
git_command('remote -v').to_s.split("\n").map do |line|
next if line !~ /^(.+?)\t(.+) \(/
name, url = $1, $2
url_memo[name] << url
end
((ORIGIN_NAMES + names) & names).map do |name|
urls = url_memo[name].uniq
Remote.new(self, name, urls)
end
end
end

@@ -194,12 +226,10 @@ def remote_by_name(remote_name)
remotes.find {|r| r.name == remote_name }
end

def known_hosts
hosts = git_config('hub.host', :all).to_s.split("\n")
hosts << default_host
# support ssh.github.com
# https://help.github.com/articles/using-ssh-over-the-https-port
hosts << "ssh.#{default_host}"
def known_host?(host)
default = default_host
default == host || "ssh.#{default}" == host ||
git_config('hub.host', :all).to_s.split("\n").include?(host)
end

def self.default_host
@@ -220,7 +250,7 @@ def ssh_config

class GithubProject < Struct.new(:local_repo, :owner, :name, :host)
def self.from_url(url, local_repo)
if local_repo.known_hosts.include? url.host
if local_repo.known_host?(url.host)
_, owner, name = url.path.split('/', 4)
GithubProject.new(local_repo, owner, name.sub(/\.git$/, ''), url.host)
end
@@ -335,7 +365,7 @@ def push_target(owner_name)
refs = local_repo.remotes_for_publish(owner_name).map { |remote|
"refs/remotes/#{remote}/#{short}"
}
if branch = refs.detect {|ref| local_repo.git_command("rev-parse -q --verify #{ref}") }
if branch = refs.detect {|ref| local_repo.file_exist?(ref) }
Branch.new(local_repo, branch)
end
end
@@ -351,15 +381,15 @@ def remote_name
end
end

class Remote < Struct.new(:local_repo, :name)
class Remote < Struct.new(:local_repo, :name, :raw_urls)
alias to_s name

def ==(other)
other.respond_to?(:to_str) ? name == other.to_str : super
end

def project
urls.each_value { |url|
urls.each { |url|
if valid = GithubProject.from_url(url, local_repo)
return valid
end
@@ -368,27 +398,29 @@ def project
end

def urls
return @urls if defined? @urls
@urls = {}
local_repo.git_command('remote -v').to_s.split("\n").map do |line|
next if line !~ /^(.+?)\t(.+) \((.+)\)$/
remote, uri, type = $1, $2, $3
next if remote != self.name
if uri =~ %r{^[\w-]+://} or uri =~ %r{^([^/]+?):}
uri = "ssh://#{$1}/#{$'}" if $1
@urls ||= raw_urls.map do |url|
with_normalized_url(url) do |normalized|
begin
@urls[type] = uri_parse(uri)
uri_parse(normalized)
rescue URI::InvalidURIError
end
end
end
@urls
end

def with_normalized_url(url)
if url =~ %r{^[\w-]+://} || url =~ %r{^([^/]+?):}
url = "ssh://#{$1}/#{$'}" if $1
yield url
end
end

def uri_parse uri
uri = URI.parse uri
uri.host = local_repo.ssh_config.get_value(uri.host, 'hostname') { uri.host }
uri.user = local_repo.ssh_config.get_value(uri.host, 'user') { uri.user }
if uri.host != local_repo.default_host
ssh = local_repo.ssh_config
uri.host = ssh.get_value(uri.host, :HostName) { uri.host }
end
uri
end
end
@@ -447,12 +479,8 @@ def current_dir
PWD
end

def git_dir
git_command 'rev-parse -q --git-dir'
end

def is_repo?
!!git_dir
!!local_repo(false)
end

def git_editor
@@ -58,6 +58,7 @@ def execute_command_chain commands
if cmd.respond_to?(:call) then cmd.call
elsif i == commands.length - 1
# last command in chain
STDOUT.flush; STDERR.flush
exec(*cmd)
else
exit($?.exitstatus) unless system(*cmd)