Module: Hub::Commands

Extended by:
Commands, Context
Included in:
Commands
Defined in:
lib/hub/commands.rb

Overview

The Commands module houses the git commands that hub lovingly wraps. If a method exists here, it is expected to have a corresponding git command which either gets run before or after the method executes.

The typical flow is as follows:

  1. hub is invoked from the command line: $ hub clone rtomayko/tilt

  2. The Hub class is initialized: >> hub = Hub.new(‘clone’, ‘rtomayko/tilt’)

  3. The method representing the git subcommand is executed with the full args: >> Commands.clone(‘clone’, ‘rtomayko/tilt’)

  4. That method rewrites the args as it sees fit: >> args = “git://github.com/” + args + “.git”

    > “git://github.com/rtomayko/tilt.git”

  5. The new args are used to run git: >> exec “git”, “clone”, “git://github.com/rtomayko/tilt.git”

An optional after callback can be set. If so, it is run after step 5 (which then performs a system call rather than an exec). See Hub::Args for more information on the after callback.

Constant Summary collapse

API_REPO =
'http://github.com/api/v2/yaml/repos/show/%s/%s'
API_FORK =
'http://github.com/api/v2/yaml/repos/fork/%s/%s'
API_CREATE =
'http://github.com/api/v2/yaml/repos/create'

Instance Method Summary collapse

Instance Method Details

#alias(args) ⇒ Object



438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
# File 'lib/hub/commands.rb', line 438

def alias(args)
  shells = {
    'sh'   => 'alias git=hub',
    'bash' => 'alias git=hub',
    'zsh'  => 'function git(){hub "$@"}',
    'csh'  => 'alias git hub',
    'fish' => 'alias git hub'
  }

  silent = args.delete('-s')

  if shell = args[1]
    if silent.nil?
      puts "Run this in your shell to start using `hub` as `git`:"
      print "  "
    end
  else
    puts "usage: hub alias [-s] SHELL", ""
    puts "You already have hub installed and available in your PATH,"
    puts "but to get the full experience you'll want to alias it to"
    puts "`git`.", ""
    puts "To see how to accomplish this for your shell, run the alias"
    puts "command again with the name of your shell.", ""
    puts "Known shells:"
    shells.map { |key, _| key }.sort.each do |key|
      puts "  " + key
    end
    puts "", "Options:"
    puts "  -s   Silent. Useful when using the output with eval, e.g."
    puts "       $ eval `hub alias -s bash`"

    exit
  end

  if shells[shell]
    puts shells[shell]
  else
    abort "fatal: never heard of `#{shell}'"
  end

  exit
end

#am(args) ⇒ Object

$ hub am github.com/defunkt/hub/pull/55 > curl github.com/defunkt/hub/pull/55.patch -o /tmp/55.patch > git am /tmp/55.patch



235
236
237
238
239
240
241
242
243
244
245
# File 'lib/hub/commands.rb', line 235

def am(args)
  if url = args.find { |a| a =~ %r{^https?://(gist\.)?github\.com/} }
    idx = args.index(url)
    gist = $1 == 'gist.'
    ext = gist ? '.txt' : '.patch'
    url += ext unless File.extname(url) == ext
    patch_file = File.join(ENV['TMPDIR'], "#{gist ? 'gist-' : ''}#{File.basename(url)}")
    args.before 'curl', ['-#LA', "hub #{Hub::Version}", url, '-o', patch_file]
    args[idx] = patch_file
  end
end

#browse(args) ⇒ Object

$ hub browse > open github.com/CURRENT_REPO

$ hub browse – issues > open github.com/CURRENT_REPO/issues

$ hub browse pjhyett/github-services > open github.com/pjhyett/github-services

$ hub browse github-services > open github.com/YOUR_LOGIN/github-services

$ hub browse github-services wiki > open github.com/YOUR_LOGIN/github-services/wiki



360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
# File 'lib/hub/commands.rb', line 360

def browse(args)
  args.shift
  browse_command(args) do
    user = repo = nil
    dest = args.shift
    dest = nil if dest == '--'

    if dest
      # $ hub browse pjhyett/github-services
      # $ hub browse github-services
      repo = dest
    elsif repo_user
      # $ hub browse
      user = repo_user
    else
      abort "Usage: hub browse [<USER>/]<REPOSITORY>"
    end

    params = { :user => user, :repo => repo }

    # $ hub browse -- wiki
    case subpage = args.shift
    when 'commits'
      branch = (!dest && tracked_branch) || 'master'
      params[:web] = "/commits/#{branch}"
    when 'tree', NilClass
      branch = !dest && tracked_branch
      params[:web] = "/tree/#{branch}" if branch && branch != 'master'
    else
      params[:web] = "/#{subpage}"
    end

    params
  end
end

#cherry_pick(args) ⇒ Object

$ git cherry-pick github.com/mislav/hub/commit/a319d88#comments > git remote add -f mislav git://github.com/mislav/hub.git > git cherry-pick a319d88

$ git cherry-pick mislav@a319d88 > git remote add -f mislav git://github.com/mislav/hub.git > git cherry-pick a319d88

$ git cherry-pick mislav@SHA > git fetch mislav > git cherry-pick SHA



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/hub/commands.rb', line 205

def cherry_pick(args)
  unless args.include?('-m') or args.include?('--mainline')
    case ref = args.words.last
    when %r{^(?:https?:)//github.com/(.+?)/(.+?)/commit/([a-f0-9]{7,40})}
      user, repo, sha = $1, $2, $3
      args[args.index(ref)] = sha
    when /^(\w+)@([a-f1-9]{7,40})$/
      user, repo, sha = $1, nil, $2
      args[args.index(ref)] = sha
    else
      user = nil
    end

    if user
      if user == repo_owner
        # fetch from origin if the repo belongs to the user
        args.before ['fetch', default_remote]
      elsif remotes.include?(user)
        args.before ['fetch', user]
      else
        remote_url = github_url(:user => user, :repo => repo, :private => false)
        args.before ['remote', 'add', '-f', user, remote_url]
      end
    end
  end
end

#clone(args) ⇒ Object

$ hub clone rtomayko/tilt > git clone git://github.com/rtomayko/tilt.

$ hub clone -p kneath/hemingway > git clone [email protected]:kneath/hemingway.git

$ hub clone tilt > git clone git://github.com/YOUR_LOGIN/tilt.

$ hub clone -p github > git clone [email protected]:YOUR_LOGIN/hemingway.git



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/hub/commands.rb', line 71

def clone(args)
  ssh = args.delete('-p')
  has_values = /^(--(upload-pack|template|depth|origin|branch|reference)|-[ubo])$/
  
  idx = 1
  while idx < args.length
    arg = args[idx]
    if arg.index('-') == 0
      idx += 1 if arg =~ has_values
    elsif arg.index('://') or arg.index('@') or File.directory?(arg)
      # Bail out early for URLs and local paths.
      break
    elsif arg.scan('/').size <= 1 && !arg.include?(':')
      # $ hub clone rtomayko/tilt
      # $ hub clone tilt
      args[args.index(arg)] = github_url(:repo => arg, :private => ssh)
      break
    end
    idx += 1
  end
end

#compare(args) ⇒ Object

$ hub compare 1.0…fix > open github.com/CURRENT_REPO/compare/1.0…fix $ hub compare refactor > open github.com/CURRENT_REPO/compare/refactor $ hub compare myfork feature > open github.com/myfork/REPO/compare/feature $ hub compare -u 1.0…2.0 “github.com/CURRENT_REPO/compare/1.0…2.0



404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/hub/commands.rb', line 404

def compare(args)
  args.shift
  browse_command(args) do
    if args.empty?
      branch = tracked_branch
      if branch && branch != 'master'
        range, user = branch, repo_user
      else
        abort "Usage: hub compare [USER] [<START>...]<END>"
      end
    else
      range = args.pop
      user = args.pop || repo_user
    end
    { :user => user, :web => "/compare/#{range}" }
  end
end

#create(args) ⇒ Object

$ hub create … create repo on github … > git remote add -f origin [email protected]:YOUR_USER/CURRENT_REPO.git



286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
# File 'lib/hub/commands.rb', line 286

def create(args)
  if !is_repo?
    puts "'create' must be run from inside a git repository"
    args.skip!
  elsif github_user && github_token
    args.shift
    options = {}
    options[:private] = true if args.delete('-p')

    until args.empty?
      case arg = args.shift
      when '-d'
        options[:description] = args.shift
      when '-h'
        options[:homepage] = args.shift
      else
        puts "unexpected argument: #{arg}"
        return
      end
    end

    if repo_exists?(github_user)
      puts "#{github_user}/#{repo_name} already exists on GitHub"
      action = "set remote origin"
    else
      action = "created repository"
      create_repo(options)
    end

    url = github_url(:private => true)

    if remotes.first != 'origin'
      args.replace %W"remote add -f origin #{url}"
    else
      args.replace %W"remote -v"
    end

    args.after { puts "#{action}: #{github_user}/#{repo_name}" }
  end
rescue Net::HTTPExceptions
  response = $!.response
  warn "error creating repository: #{response.message} (HTTP #{response.code})"
  exit 1
end

#fetch(args) ⇒ Object

$ hub fetch mislav > git remote add mislav git://github.com/mislav/REPO.git > git fetch mislav

$ hub fetch –multiple mislav xoebus > git remote add mislav … > git remote add xoebus … > git fetch –multiple mislav xoebus



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/hub/commands.rb', line 162

def fetch(args)
  # $ hub fetch --multiple <name1>, <name2>, ...
  if args.include?('--multiple')
    names = args.words[1..-1]
  # $ hub fetch <name>
  elsif remote_name = args.words[1]
    # $ hub fetch <name1>,<name2>,...
    if remote_name =~ /^\w+(,\w+)+$/
      index = args.index(remote_name)
      args.delete(remote_name)
      names = remote_name.split(',')
      args.insert(index, *names)
      args.insert(index, '--multiple')
    else
      names = [remote_name]
    end
  else
    names = []
  end

  names.reject! { |name|
    name =~ /\W/ or remotes.include?(name) or
      remotes_group(name) or not repo_exists?(name)
  }

  if names.any?
    names.each do |name|
      args.before ['remote', 'add', name, github_url(:user => name)]
    end
  end
end

#fork(args) ⇒ Object

$ hub fork … hardcore forking action … > git remote add -f YOUR_USER [email protected]:YOUR_USER/CURRENT_REPO.git



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/hub/commands.rb', line 260

def fork(args)
  # can't do anything without token and original owner name
  if github_user && github_token && repo_owner
    if repo_exists?(github_user)
      puts "#{github_user}/#{repo_name} already exists on GitHub"
    else
      fork_repo
    end

    if args.include?('--no-remote')
      exit
    else
      url = github_url(:private => true)
      args.replace %W"remote add -f #{github_user} #{url}"
      args.after { puts "new remote: #{github_user}" }
    end
  end
rescue Net::HTTPExceptions
  response = $!.response
  warn "error creating fork: #{response.message} (HTTP #{response.code})"
  exit 1
end

#help(args) ⇒ Object Also known as: --help

$ hub help (print improved help text)



493
494
495
496
497
498
499
500
501
502
503
504
# File 'lib/hub/commands.rb', line 493

def help(args)
  command = args.grep(/^[^-]/)[1]

  if command == 'hub'
    puts hub_manpage
    exit
  elsif command.nil? && args.grep(/^--?a/).empty?
    ENV['GIT_PAGER'] = '' if args.grep(/^-{1,2}p/).empty? # Use `cat`.
    puts improved_help_text
    exit
  end
end

#hub(args) ⇒ Object

$ hub hub standalone Prints the “standalone” version of hub for an easy, memorable installation sequence:

$ gem install git-hub $ hub hub standalone > ~/bin/hub && chmod 755 ~/bin/hub $ gem uninstall git-hub



429
430
431
432
433
434
435
436
# File 'lib/hub/commands.rb', line 429

def hub(args)
  return help(args) unless args[1] == 'standalone'
  require 'hub/standalone'
  puts Hub::Standalone.build
  exit
rescue LoadError
  abort "hub is running in standalone mode."
end

#improved_help_textObject

The text print when ‘hub help` is run, kept in its own method for the convenience of the author.



509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
# File 'lib/hub/commands.rb', line 509

def improved_help_text
  "usage: git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]\n[-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR]\n[--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]\n\nBasic Commands:\n   init       Create an empty git repository or reinitialize an existing one\n   add        Add new or modified files to the staging area\n   rm         Remove files from the working directory and staging area\n   mv         Move or rename a file, a directory, or a symlink\n   status     Show the status of the working directory and staging area\n   commit     Record changes to the repository\n\nHistory Commands:\n   log        Show the commit history log\n   diff       Show changes between commits, commit and working tree, etc\n   show       Show information about commits, tags or files\n\nBranching Commands:\n   branch     List, create, or delete branches\n   checkout   Switch the active branch to another branch\n   merge      Join two or more development histories (branches) together\n   tag        Create, list, delete, sign or verify a tag object\n\nRemote Commands:\n   clone      Clone a remote repository into a new directory\n   fetch      Download data, tags and branches from a remote repository\n   pull       Fetch from and merge with another repository or a local branch\n   push       Upload data, tags and branches to a remote repository\n   remote     View and manage a set of remote repositories\n\nAdvanced commands:\n   reset      Reset your staging area or working directory to another point\n   rebase     Re-apply a series of patches in one branch onto another\n   bisect     Find by binary search the change that introduced a bug\n   grep       Print files with lines matching a pattern in your codebase\n\nSee 'git help COMMAND' for more information on a specific command.\n"
end

#init(args) ⇒ Object

$ hub init -g > git init > git remote add origin [email protected]:USER/REPO.git



250
251
252
253
254
255
# File 'lib/hub/commands.rb', line 250

def init(args)
  if args.delete('-g')
    url = github_url(:private => true, :repo => current_dirname)
    args.after "git remote add origin #{url}"
  end
end

#push(args) ⇒ Object

$ hub push origin,staging cool-feature > git push origin cool-feature > git push staging cool-feature



334
335
336
337
338
339
340
341
342
343
344
# File 'lib/hub/commands.rb', line 334

def push(args)
  return unless args[1] =~ /,/

  branch  = args[2]
  remotes = args[1].split(',')
  args[1] = remotes.shift

  remotes.each do |name|
    args.after ['push', name, branch]
  end
end

#remote(args) ⇒ Object

$ hub remote add pjhyett > git remote add pjhyett git://github.com/pjhyett/THIS_REPO.git

$ hub remote add -p mojombo > git remote add mojombo [email protected]:mojombo/THIS_REPO.git

$ hub remote add origin > git remote add origin git://github.com/YOUR_LOGIN/THIS_REPO.git



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/hub/commands.rb', line 127

def remote(args)
  return unless ['add','set-url'].include?(args[1]) && args.last !~ %r{.+?://|.+?@|^[./]}

  ssh = args.delete('-p')

  # user/repo
  args.last =~ /\b(.+?)(?:\/(.+))?$/
  user, repo = $1, $2

  if args.words[2] == 'origin' && args.words[3].nil?
    # Origin special case triggers default user/repo
    user = repo = nil
  elsif args.words[-2] == args.words[1]
    # rtomayko/tilt => rtomayko
    # Make sure you dance around flags.
    idx = args.index( args.words[-1] )
    args[idx] = user
  else
    # They're specifying the remote name manually (e.g.
    # git remote add blah rtomayko/tilt), so just drop the last
    # argument.
    args.replace args[0...-1]
  end

  args << github_url(:user => user, :repo => repo, :private => ssh)
end

#run(args) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/hub/commands.rb', line 44

def run(args)
  # Hack to emulate git-style
  args.unshift 'help' if args.grep(/^[^-]|version|exec-path$|html-path/).empty?

  cmd = args[0]
  expanded_args = expand_alias(cmd)
  cmd = expanded_args[0] if expanded_args

  # git commands can have dashes
  cmd = cmd.sub(/(\w)-/, '\1_')
  if method_defined?(cmd) and cmd != 'run'
    args[0, 1] = expanded_args if expanded_args
    send(cmd, args)
  end
end

#submodule(args) ⇒ Object

$ hub submodule add wycats/bundler vendor/bundler > git submodule add git://github.com/wycats/bundler.git vendor/bundler

$ hub submodule add -p wycats/bundler vendor/bundler > git submodule add [email protected]:wycats/bundler.git vendor/bundler

$ hub submodule add -b ryppl ryppl/pip vendor/bundler > git submodule add -b ryppl git://github.com/ryppl/pip.git vendor/pip



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/hub/commands.rb', line 101

def submodule(args)
  return unless index = args.index('add')
  args.delete_at index

  branch = args.index('-b') || args.index('--branch')
  if branch
    args.delete_at branch
    branch_name = args.delete_at branch
  end

  clone(args)

  if branch_name
    args.insert branch, '-b', branch_name
  end
  args.insert index, 'add'
end

#version(args) ⇒ Object Also known as: --version

$ hub version > git version (print hub version)



484
485
486
487
488
# File 'lib/hub/commands.rb', line 484

def version(args)
  args.after do
    puts "hub version %s" % Version
  end
end