Skip to content

Commit

Permalink
Shell out only once to list git remotes and their URLs
Browse files Browse the repository at this point in the history
Each hub invocation would previously shell out twice to `git remote`:
1. Once to get the list of available remote names
2. A second time to obtain remote URLs with `remote -v`

With the new approach, we get the list of available remotes and collect
their URLs in the same pass. This is part of an effort to reduce the
number of shelling out to git if not absolutely necessary.
  • Loading branch information
mislav committed Dec 25, 2013
1 parent bb73ac1 commit 76afe6a
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 20 deletions.
41 changes: 23 additions & 18 deletions lib/hub/context.rb
Expand Up @@ -194,12 +194,17 @@ def master_branch

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

Expand Down Expand Up @@ -376,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
Expand All @@ -393,21 +398,21 @@ 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
Expand Down
3 changes: 1 addition & 2 deletions test/hub_test.rb
Expand Up @@ -99,7 +99,6 @@ def setup
end

@git_reader.stub! \
'remote' => "mislav\norigin",
'remote -v' => "origin\tgit://github.com/defunkt/hub.git (fetch)\nmislav\tgit://github.com/mislav/hub.git (fetch)",
'rev-parse --symbolic-full-name master@{upstream}' => 'refs/remotes/origin/master',
'config --get --bool hub.http-clone' => 'false',
Expand Down Expand Up @@ -520,7 +519,7 @@ def stub_remotes_group(name, value)
end

def stub_no_remotes
stub_command_output 'remote', nil
stub_command_output 'remote -v', nil
end

def stub_no_git_repo
Expand Down

0 comments on commit 76afe6a

Please sign in to comment.