hub 1.7.0 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of hub might be problematic. Click here for more details.

data/README.md CHANGED
@@ -365,7 +365,6 @@ You will need the following libraries for development:
365
365
 
366
366
  * [ronn](https://github.com/rtomayko/ronn) (building man pages)
367
367
  * [webmock](https://github.com/bblimke/webmock)
368
- * [json](http://flori.github.com/json/) (ruby 1.8 only)
369
368
 
370
369
  Meta
371
370
  ----
data/lib/hub.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'hub/version'
2
2
  require 'hub/args'
3
3
  require 'hub/context'
4
+ require 'hub/json'
4
5
  require 'hub/commands'
5
6
  require 'hub/runner'
data/lib/hub/commands.rb CHANGED
@@ -34,13 +34,9 @@ module Hub
34
34
  # provides git interrogation methods
35
35
  extend Context
36
36
 
37
- API_REPO = 'http://github.com/api/v2/yaml/repos/show/%s/%s'
38
- API_FORK = 'https://github.com/api/v2/yaml/repos/fork/%s/%s'
39
- API_CREATE = 'https://github.com/api/v2/yaml/repos/create'
40
- API_PULL = 'http://github.com/api/v2/json/pulls/%s'
41
- API_PULLREQUEST = 'https://github.com/api/v2/yaml/pulls/%s/%s'
42
-
43
- NAME_WITH_OWNER_RE = /^([\w-]+)(?:\/([\w-]+))?$/
37
+ NAME_RE = /[\w.-]+/
38
+ OWNER_RE = /[a-zA-Z0-9-]+/
39
+ NAME_WITH_OWNER_RE = /^(?:#{NAME_RE}|#{OWNER_RE}\/#{NAME_RE})$/
44
40
 
45
41
  def run(args)
46
42
  slurp_global_flags(args)
@@ -97,11 +93,11 @@ module Hub
97
93
  head_project, options[:head] = from_github_ref.call(head, head_project)
98
94
  when '-i'
99
95
  options[:issue] = args.shift
100
- when %r{^https?://github.com/([^/]+/[^/]+)/issues/(\d+)}
101
- options[:issue] = $2
102
- base_project = github_project($1)
103
96
  else
104
- if !options[:title] then options[:title] = arg
97
+ if url = resolve_github_url(arg) and url.project_path =~ /^issues\/(\d+)/
98
+ options[:issue] = $1
99
+ base_project = url.project
100
+ elsif !options[:title] then options[:title] = arg
105
101
  else
106
102
  abort "invalid argument: #{arg}"
107
103
  end
@@ -120,8 +116,10 @@ module Hub
120
116
  end
121
117
  options[:head] ||= (tracked_branch || current_branch).short_name
122
118
 
123
- if head_project.owner != github_user and !tracked_branch and !explicit_owner
124
- head_project = github_project(head_project.name, github_user)
119
+ # when no tracking, assume remote branch is published under active user's fork
120
+ user = github_user(true, head_project.host)
121
+ if head_project.owner != user and !tracked_branch and !explicit_owner
122
+ head_project = head_project.owned_by(user)
125
123
  end
126
124
 
127
125
  remote_branch = "#{head_project.remote}/#{options[:head]}"
@@ -184,8 +182,12 @@ module Hub
184
182
  # $ hub clone rtomayko/tilt
185
183
  # $ hub clone tilt
186
184
  if arg =~ NAME_WITH_OWNER_RE
187
- project = github_project(arg)
188
- ssh ||= args[0] != 'submodule' && project.owner == github_user(false)
185
+ # FIXME: this logic shouldn't be duplicated here!
186
+ name, owner = arg, nil
187
+ owner, name = name.split('/', 2) if name.index('/')
188
+ host = ENV['GITHUB_HOST']
189
+ project = Context::GithubProject.new(nil, owner || github_user(true, host), name, host || 'github.com')
190
+ ssh ||= args[0] != 'submodule' && project.owner == github_user(false, host) || host
189
191
  args[idx] = project.git_url(:private => ssh, :https => https_protocol?)
190
192
  end
191
193
  break
@@ -229,11 +231,13 @@ module Hub
229
231
  # $ hub remote add origin
230
232
  # > git remote add origin git://github.com/YOUR_LOGIN/THIS_REPO.git
231
233
  def remote(args)
232
- if %w[add set-url].include?(args[1]) && args.last =~ NAME_WITH_OWNER_RE
233
- user, repo = $1, $2 || repo_name
234
- else
235
- return # do not touch arguments
234
+ if %w[add set-url].include?(args[1])
235
+ name = args.last
236
+ if name =~ /^(#{OWNER_RE})$/ || name =~ /^(#{OWNER_RE})\/(#{NAME_RE})$/
237
+ user, repo = $1, $2 || repo_name
238
+ end
236
239
  end
240
+ return unless user # do not touch arguments
237
241
 
238
242
  ssh = args.delete('-p')
239
243
 
@@ -283,14 +287,16 @@ module Hub
283
287
  names = []
284
288
  end
285
289
 
286
- names.reject! { |name|
287
- name =~ /\W/ or remotes.include?(name) or
288
- remotes_group(name) or not repo_exists?(name)
289
- }
290
+ projects = names.map { |name|
291
+ unless name =~ /\W/ or remotes.include?(name) or remotes_group(name)
292
+ project = github_project(nil, name)
293
+ project if repo_exists?(project)
294
+ end
295
+ }.compact
290
296
 
291
- if names.any?
292
- names.each do |name|
293
- args.before ['remote', 'add', name, git_url(name)]
297
+ if projects.any?
298
+ projects.each do |project|
299
+ args.before ['remote', 'add', project.owner, project.git_url(:https => https_protocol?)]
294
300
  end
295
301
  end
296
302
  end
@@ -299,20 +305,23 @@ module Hub
299
305
  # > git remote add -f -t feature git://github:com/mislav/hub.git
300
306
  # > git checkout -b mislav-feature mislav/feature
301
307
  def checkout(args)
302
- if (2..3) === args.length and args[1] =~ %r{https?://github.com/(.+?)/(.+?)/pull/(\d+)}
303
- owner, repo, pull_id = $1, $2, $3
308
+ if (2..3) === args.length and url = resolve_github_url(args[1]) and url.project_path =~ /^pull\/(\d+)/
309
+ pull_id = $1
304
310
 
305
311
  load_net_http
306
- pull_body = Net::HTTP.get URI(API_PULL % File.join(owner, repo, pull_id))
312
+ response = http_request(url.project.api_pullrequest_url(pull_id, 'json'))
313
+ pull_data = JSON.parse(response.body)['pull']
307
314
 
308
- user, branch = pull_body.match(/"label":\s*"(.+?)"/)[1].split(':', 2)
315
+ user, branch = pull_data['head']['label'].split(':', 2)
309
316
  new_branch_name = args[2] || "#{user}-#{branch}"
310
317
 
311
318
  if remotes.include? user
312
319
  args.before ['remote', 'set-branches', '--add', user, branch]
313
320
  args.before ['fetch', user, "+refs/heads/#{branch}:refs/remotes/#{user}/#{branch}"]
314
321
  else
315
- args.before ['remote', 'add', '-f', '-t', branch, user, github_project(repo, user).git_url]
322
+ url = github_project(url.project_name, user).git_url(:private => pull_data['head']['repository']['private'],
323
+ :https => https_protocol?)
324
+ args.before ['remote', 'add', '-f', '-t', branch, user, url]
316
325
  end
317
326
  args[1..-1] = ['-b', new_branch_name, "#{user}/#{branch}"]
318
327
  end
@@ -331,25 +340,22 @@ module Hub
331
340
  # > git cherry-pick SHA
332
341
  def cherry_pick(args)
333
342
  unless args.include?('-m') or args.include?('--mainline')
334
- case ref = args.words.last
335
- when %r{^(?:https?:)//github.com/(.+?)/(.+?)/commit/([a-f0-9]{7,40})}
336
- user, repo, sha = $1, $2, $3
337
- args[args.index(ref)] = sha
338
- when /^(\w+)@([a-f0-9]{7,40})$/
339
- user, repo, sha = $1, nil, $2
340
- args[args.index(ref)] = sha
341
- else
342
- user = nil
343
+ ref = args.words.last
344
+ if url = resolve_github_url(ref) and url.project_path =~ /^commit\/([a-f0-9]{7,40})/
345
+ sha = $1
346
+ project = url.project
347
+ elsif ref =~ /^(#{OWNER_RE})@([a-f0-9]{7,40})$/
348
+ owner, sha = $1, $2
349
+ project = local_repo.main_project.owned_by(owner)
343
350
  end
344
351
 
345
- if user
346
- if user == repo_owner
347
- # fetch from origin if the repo belongs to the user
348
- args.before ['fetch', origin_remote]
349
- elsif remotes.include?(user)
350
- args.before ['fetch', user]
352
+ if project
353
+ args[args.index(ref)] = sha
354
+
355
+ if remote = project.remote and remotes.include? remote
356
+ args.before ['fetch', remote]
351
357
  else
352
- args.before ['remote', 'add', '-f', user, git_url(user, repo)]
358
+ args.before ['remote', 'add', '-f', project.owner, project.git_url(:https => https_protocol?)]
353
359
  end
354
360
  end
355
361
  end
@@ -366,7 +372,7 @@ module Hub
366
372
  url = url.sub(%r{(/pull/\d+)/\w*$}, '\1') unless gist
367
373
  ext = gist ? '.txt' : '.patch'
368
374
  url += ext unless File.extname(url) == ext
369
- patch_file = File.join(ENV['TMPDIR'], "#{gist ? 'gist-' : ''}#{File.basename(url)}")
375
+ patch_file = File.join(ENV['TMPDIR'] || '/tmp', "#{gist ? 'gist-' : ''}#{File.basename(url)}")
370
376
  args.before 'curl', ['-#LA', "hub #{Hub::Version}", url, '-o', patch_file]
371
377
  args[idx] = patch_file
372
378
  end
@@ -382,7 +388,11 @@ module Hub
382
388
  # > git remote add origin git@github.com:USER/REPO.git
383
389
  def init(args)
384
390
  if args.delete('-g')
385
- url = git_url(github_user, repo_name, :private => true)
391
+ # can't use default_host because there is no local_repo yet
392
+ # FIXME: this shouldn't be here!
393
+ host = ENV['GITHUB_HOST']
394
+ project = Context::GithubProject.new(nil, github_user(true, host), File.basename(current_dir), host || 'github.com')
395
+ url = project.git_url(:private => true, :https => https_protocol?)
386
396
  args.after ['remote', 'add', 'origin', url]
387
397
  end
388
398
  end
@@ -391,21 +401,22 @@ module Hub
391
401
  # ... hardcore forking action ...
392
402
  # > git remote add -f YOUR_USER git@github.com:YOUR_USER/CURRENT_REPO.git
393
403
  def fork(args)
394
- # can't do anything without token and original owner name
395
- if github_user && github_token && repo_owner
396
- if repo_exists?(github_user)
397
- warn "#{github_user}/#{repo_name} already exists on GitHub"
398
- else
399
- fork_repo unless args.noop?
400
- end
404
+ unless project = local_repo.main_project
405
+ abort "Error: repository under 'origin' remote is not a GitHub project"
406
+ end
407
+ forked_project = project.owned_by(github_user(true, project.host))
408
+ if repo_exists?(forked_project)
409
+ warn "#{forked_project.name_with_owner} already exists on #{forked_project.host}"
410
+ else
411
+ fork_repo(project) unless args.noop?
412
+ end
401
413
 
402
- if args.include?('--no-remote')
403
- exit
404
- else
405
- url = git_url(github_user, repo_name, :private => true)
406
- args.replace %W"remote add -f #{github_user} #{url}"
407
- args.after 'echo', ['new remote:', github_user]
408
- end
414
+ if args.include?('--no-remote')
415
+ exit
416
+ else
417
+ url = forked_project.git_url(:private => true, :https => https_protocol?)
418
+ args.replace %W"remote add -f #{forked_project.owner} #{url}"
419
+ args.after 'echo', ['new remote:', forked_project.owner]
409
420
  end
410
421
  rescue HTTPExceptions
411
422
  display_http_exception("creating fork", $!.response)
@@ -440,17 +451,17 @@ module Hub
440
451
  end
441
452
  end
442
453
  new_repo_name ||= repo_name
443
- repo_with_owner = "#{owner}/#{new_repo_name}"
454
+ new_project = github_project(new_repo_name, owner)
444
455
 
445
- if repo_exists?(owner, new_repo_name)
446
- warn "#{repo_with_owner} already exists on GitHub"
456
+ if repo_exists?(new_project)
457
+ warn "#{new_project.name_with_owner} already exists on #{new_project.host}"
447
458
  action = "set remote origin"
448
459
  else
449
460
  action = "created repository"
450
- create_repo(repo_with_owner, options) unless args.noop?
461
+ create_repo(new_project, options) unless args.noop?
451
462
  end
452
463
 
453
- url = git_url(owner, new_repo_name, :private => true)
464
+ url = new_project.git_url(:private => true, :https => https_protocol?)
454
465
 
455
466
  if remotes.first != 'origin'
456
467
  args.replace %W"remote add -f origin #{url}"
@@ -458,7 +469,7 @@ module Hub
458
469
  args.replace %W"remote -v"
459
470
  end
460
471
 
461
- args.after 'echo', ["#{action}:", repo_with_owner]
472
+ args.after 'echo', ["#{action}:", new_project.name_with_owner]
462
473
  end
463
474
  rescue HTTPExceptions
464
475
  display_http_exception("creating repository", $!.response)
@@ -831,32 +842,32 @@ help
831
842
  end
832
843
 
833
844
  # Determines whether a user has a fork of the current repo on GitHub.
834
- def repo_exists?(user, repo = repo_name)
845
+ def repo_exists?(project)
835
846
  load_net_http
836
- url = API_REPO % [user, repo]
837
- Net::HTTPSuccess === Net::HTTP.get_response(URI(url))
847
+ Net::HTTPSuccess === http_request(project.api_show_url('yaml'))
838
848
  end
839
849
 
840
850
  # Forks the current repo using the GitHub API.
841
851
  #
842
852
  # Returns nothing.
843
- def fork_repo
853
+ def fork_repo(project)
844
854
  load_net_http
845
- response = http_post API_FORK % [repo_owner, repo_name]
855
+ response = http_post project.api_fork_url('yaml')
846
856
  response.error! unless Net::HTTPSuccess === response
847
857
  end
848
858
 
849
859
  # Creates a new repo using the GitHub API.
850
860
  #
851
861
  # Returns nothing.
852
- def create_repo(name, options = {})
853
- params = {'name' => name.sub(/^#{github_user}\//, '')}
862
+ def create_repo(project, options = {})
863
+ is_org = project.owner != github_user(true, project.host)
864
+ params = {'name' => is_org ? project.name_with_owner : project.name}
854
865
  params['public'] = '0' if options[:private]
855
866
  params['description'] = options[:description] if options[:description]
856
867
  params['homepage'] = options[:homepage] if options[:homepage]
857
868
 
858
869
  load_net_http
859
- response = http_post(API_CREATE, params)
870
+ response = http_post(project.api_create_url('yaml'), params)
860
871
  response.error! unless Net::HTTPSuccess === response
861
872
  end
862
873
 
@@ -872,15 +883,9 @@ help
872
883
  params['pull[body]'] = options[:body] if options[:body]
873
884
 
874
885
  load_net_http
875
- response = http_post(API_PULLREQUEST % [project.owner, project.name], params)
886
+ response = http_post(project.api_create_pullrequest_url('json'), params)
876
887
  response.error! unless Net::HTTPSuccess === response
877
- # GitHub bug: although we request YAML, it returns JSON
878
- if response['Content-type'].to_s.include? 'application/json'
879
- { "html_url" => response.body.match(/"html_url":\s*"(.+?)"/)[1] }
880
- else
881
- require 'yaml'
882
- YAML.load(response.body)['pull']
883
- end
888
+ JSON.parse(response.body)['pull']
884
889
  end
885
890
 
886
891
  def pullrequest_editmsg(changes)
@@ -925,11 +930,12 @@ help
925
930
  end
926
931
  end
927
932
 
928
- def http_post(url, params = nil)
929
- url = URI(url)
930
- post = Net::HTTP::Post.new(url.request_uri)
931
- post.basic_auth "#{github_user}/token", github_token
932
- post.set_form_data params if params
933
+ def http_request(url, type = :Get)
934
+ url = URI(url) unless url.respond_to? :host
935
+ user, token = github_user(type != :Get, url.host), github_token(type != :Get, url.host)
936
+
937
+ req = Net::HTTP.const_get(type).new(url.request_uri)
938
+ req.basic_auth "#{user}/token", token if user and token
933
939
 
934
940
  port = url.port
935
941
  if use_ssl = 'https' == url.scheme and not use_ssl?
@@ -943,7 +949,16 @@ help
943
949
  # TODO: SSL peer verification
944
950
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
945
951
  end
946
- http.start { http.request(post) }
952
+
953
+ yield req if block_given?
954
+ http.start { http.request(req) }
955
+ end
956
+
957
+ def http_post(url, params = nil)
958
+ http_request(url, :Post) do |req|
959
+ req.set_form_data params if params
960
+ req['Content-Length'] = req.body ? req.body.length : 0
961
+ end
947
962
  end
948
963
 
949
964
  def load_net_http
@@ -966,7 +981,13 @@ help
966
981
 
967
982
  def display_http_exception(action, response)
968
983
  $stderr.puts "Error #{action}: #{response.message} (HTTP #{response.code})"
969
- warn "Check your token configuration (`git config github.token`)" if response.code.to_i == 401
984
+ case response.code.to_i
985
+ when 401 then warn "Check your token configuration (`git config github.token`)"
986
+ when 422
987
+ if response.content_type =~ /\bjson\b/ and data = JSON.parse(response.body) and data["error"]
988
+ $stderr.puts data["error"]
989
+ end
990
+ end
970
991
  end
971
992
 
972
993
  end
data/lib/hub/context.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'shellwords'
2
2
  require 'forwardable'
3
+ require 'uri'
3
4
 
4
5
  module Hub
5
6
  # Provides methods for inspecting the environment, such as GitHub user/token
@@ -83,16 +84,20 @@ module Hub
83
84
  include GitReaderMethods
84
85
  private :git_config, :git_command
85
86
 
86
- def local_repo
87
+ def local_repo(fatal = true)
87
88
  @local_repo ||= begin
88
- LocalRepo.new git_reader, current_dir if is_repo?
89
+ if is_repo?
90
+ LocalRepo.new git_reader, current_dir
91
+ elsif fatal
92
+ abort "fatal: Not a git repository"
93
+ end
89
94
  end
90
95
  end
91
96
 
92
97
  repo_methods = [
93
98
  :current_branch, :master_branch,
94
99
  :current_project, :upstream_project,
95
- :repo_owner,
100
+ :repo_owner, :repo_host,
96
101
  :remotes, :remotes_group, :origin_remote
97
102
  ]
98
103
  def_delegator :local_repo, :name, :repo_name
@@ -116,6 +121,10 @@ module Hub
116
121
  end
117
122
  end
118
123
 
124
+ def repo_host
125
+ project = main_project and project.host
126
+ end
127
+
119
128
  def main_project
120
129
  remote = origin_remote and remote.project
121
130
  end
@@ -162,9 +171,43 @@ module Hub
162
171
  def remote_by_name(remote_name)
163
172
  remotes.find {|r| r.name == remote_name }
164
173
  end
174
+
175
+ def known_hosts
176
+ git_config('hub.host', :all).to_s.split("\n") + [default_host]
177
+ end
178
+
179
+ def default_host
180
+ ENV['GITHUB_HOST'] || main_host
181
+ end
182
+
183
+ def main_host
184
+ 'github.com'
185
+ end
165
186
  end
166
187
 
167
- class GithubProject < Struct.new(:local_repo, :owner, :name)
188
+ class GithubProject < Struct.new(:local_repo, :owner, :name, :host)
189
+ def self.from_url(url, local_repo)
190
+ if local_repo.known_hosts.include? url.host
191
+ _, owner, name = url.path.split('/', 4)
192
+ GithubProject.new(local_repo, owner, name.sub(/\.git$/, ''), url.host)
193
+ end
194
+ end
195
+
196
+ def initialize(*args)
197
+ super
198
+ self.host ||= local_repo.default_host
199
+ end
200
+
201
+ def private?
202
+ local_repo and host != local_repo.main_host
203
+ end
204
+
205
+ def owned_by(new_owner)
206
+ new_project = dup
207
+ new_project.owner = new_owner
208
+ new_project
209
+ end
210
+
168
211
  def name_with_owner
169
212
  "#{owner}/#{name}"
170
213
  end
@@ -187,22 +230,74 @@ module Hub
187
230
  path = '/wiki' + path
188
231
  end
189
232
  end
190
- 'https://github.com/' + project_name + path.to_s
233
+ "https://#{host}/" + project_name + path.to_s
191
234
  end
192
235
 
193
236
  def git_url(options = {})
194
- if options[:https] then 'https://github.com/'
195
- elsif options[:private] then 'git@github.com:'
196
- else 'git://github.com/'
237
+ if options[:https] then "https://#{host}/"
238
+ elsif options[:private] or private? then "git@#{host}:"
239
+ else "git://#{host}/"
197
240
  end + name_with_owner + '.git'
198
241
  end
242
+
243
+ def api_url(type, resource, action)
244
+ URI("https://#{host}/api/v2/#{type}/#{resource}/#{action}")
245
+ end
246
+
247
+ def api_show_url(type)
248
+ api_url(type, 'repos', "show/#{owner}/#{name}")
249
+ end
250
+
251
+ def api_fork_url(type)
252
+ api_url(type, 'repos', "fork/#{owner}/#{name}")
253
+ end
254
+
255
+ def api_create_url(type)
256
+ api_url(type, 'repos', 'create')
257
+ end
258
+
259
+ def api_pullrequest_url(id, type)
260
+ api_url(type, 'pulls', "#{owner}/#{name}/#{id}")
261
+ end
262
+
263
+ def api_create_pullrequest_url(type)
264
+ api_url(type, 'pulls', "#{owner}/#{name}")
265
+ end
266
+ end
267
+
268
+ class GithubURL < URI::HTTPS
269
+ extend Forwardable
270
+
271
+ attr_reader :project
272
+ def_delegator :project, :name, :project_name
273
+ def_delegator :project, :owner, :project_owner
274
+
275
+ def self.resolve(url, local_repo)
276
+ u = URI(url)
277
+ if %[http https].include? u.scheme and project = GithubProject.from_url(u, local_repo)
278
+ self.new(u.scheme, u.userinfo, u.host, u.port, u.registry,
279
+ u.path, u.opaque, u.query, u.fragment, project)
280
+ end
281
+ rescue URI::InvalidURIError
282
+ nil
283
+ end
284
+
285
+ def initialize(*args)
286
+ @project = args.pop
287
+ super(*args)
288
+ end
289
+
290
+ # segment of path after the project owner and name
291
+ def project_path
292
+ path.split('/', 4)[3]
293
+ end
199
294
  end
200
295
 
201
296
  class Branch < Struct.new(:local_repo, :name)
202
297
  alias to_s name
203
298
 
204
299
  def short_name
205
- name.split('/').last
300
+ name.sub(%r{^refs/(remotes/)?.+?/}, '')
206
301
  end
207
302
 
208
303
  def master?
@@ -233,13 +328,24 @@ module Hub
233
328
  end
234
329
 
235
330
  def project
236
- if urls.find { |u| u =~ %r{\bgithub\.com[:/](.+)/(.+).git$} }
237
- GithubProject.new local_repo, $1, $2
238
- end
331
+ urls.each { |url|
332
+ if valid = GithubProject.from_url(url, local_repo)
333
+ return valid
334
+ end
335
+ }
336
+ nil
239
337
  end
240
338
 
241
339
  def urls
242
- @urls ||= local_repo.git_config("remote.#{name}.url", :all).to_s.split("\n")
340
+ @urls ||= local_repo.git_config("remote.#{name}.url", :all).to_s.split("\n").map { |uri|
341
+ begin
342
+ if uri =~ %r{^[\w-]+://} then URI(uri)
343
+ elsif uri =~ %r{^([^/]+?):} then URI("ssh://#{$1}/#{$'}") # scp-like syntax
344
+ end
345
+ rescue URI::InvalidURIError
346
+ nil
347
+ end
348
+ }.compact
243
349
  end
244
350
  end
245
351
 
@@ -255,7 +361,14 @@ module Hub
255
361
  owner ||= github_user
256
362
  end
257
363
 
258
- GithubProject.new local_repo, owner, name
364
+ if local_repo(false) and main_project = local_repo.main_project
365
+ project = main_project.dup
366
+ project.owner = owner
367
+ project.name = name
368
+ project
369
+ else
370
+ GithubProject.new(local_repo, owner, name)
371
+ end
259
372
  end
260
373
 
261
374
  def git_url(owner = nil, name = nil, options = {})
@@ -263,23 +376,45 @@ module Hub
263
376
  project.git_url({:https => https_protocol?}.update(options))
264
377
  end
265
378
 
379
+ def resolve_github_url(url)
380
+ GithubURL.resolve(url, local_repo) if url =~ /^https?:/
381
+ end
382
+
266
383
  LGHCONF = "http://help.github.com/set-your-user-name-email-and-github-token/"
267
384
 
268
385
  # Either returns the GitHub user as set by git-config(1) or aborts
269
386
  # with an error message.
270
- def github_user(fatal = true)
271
- if user = ENV['GITHUB_USER'] || git_config('github.user')
387
+ def github_user(fatal = true, host = nil)
388
+ if local = local_repo(false)
389
+ host ||= local.default_host
390
+ host = nil if host == local.main_host
391
+ end
392
+ host = %(."#{host}") if host
393
+ if user = ENV['GITHUB_USER'] || git_config("github#{host}.user")
272
394
  user
273
395
  elsif fatal
274
- abort("** No GitHub user set. See #{LGHCONF}")
396
+ if host.nil?
397
+ abort("** No GitHub user set. See #{LGHCONF}")
398
+ else
399
+ abort("** No user set for github#{host}")
400
+ end
275
401
  end
276
402
  end
277
403
 
278
- def github_token(fatal = true)
279
- if token = ENV['GITHUB_TOKEN'] || git_config('github.token')
404
+ def github_token(fatal = true, host = nil)
405
+ if local = local_repo(false)
406
+ host ||= local.default_host
407
+ host = nil if host == local.main_host
408
+ end
409
+ host = %(."#{host}") if host
410
+ if token = ENV['GITHUB_TOKEN'] || git_config("github#{host}.token")
280
411
  token
281
412
  elsif fatal
282
- abort("** No GitHub token set. See #{LGHCONF}")
413
+ if host.nil?
414
+ abort("** No GitHub token set. See #{LGHCONF}")
415
+ else
416
+ abort("** No token set for github#{host}")
417
+ end
283
418
  end
284
419
  end
285
420