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'

Constants included from Context

Hub::Context::GIT_CONFIG, Hub::Context::LGHCONF, Hub::Context::REMOTES

Instance Method Summary collapse

Methods included from Context

current_branch, current_remote, default_remote, github_token, github_url, github_user, http_clone?, is_repo?, normalize_branch, remote_for, remotes, remotes_group, repo_name, repo_owner, repo_user, tracked_branch, tracked_for

Instance Method Details

#alias(args) ⇒ Object



420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
# File 'lib/hub/commands.rb', line 420

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

#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 -p pjhyett/github-fi > open github.com/pjhyett/github-fi

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

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

$ hub browse -p github-fi > open github.com/YOUR_LOGIN/github-fi



338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
# File 'lib/hub/commands.rb', line 338

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 'wiki'
      params[:web] = 'wiki'
    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



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/hub/commands.rb', line 192

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})}
      scheme, user, repo, sha = $1, $2, $3, $4
      args[args.index(ref)] = sha
    when /^(\w+)@([a-f1-9]{7,40})$/
      scheme, user, repo, sha = nil, $1, nil, $2
      args[args.index(ref)] = sha
    else
      user = nil
    end

    if user
      # cherry-pick comes after the fetch
      args.after args.to_exec.join(' ')

      if user == repo_owner
        # fetch from origin if the repo belongs to the user
        args.replace ['fetch', default_remote]
      elsif remotes.include?(user)
        args.replace ['fetch', user]
      else
        secure = scheme == 'https:'
        remote_url = github_url(:user => user, :repo => repo, :private => secure)
        args.replace ['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



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/hub/commands.rb', line 55

def clone(args)
  ssh = args.delete('-p')

  last_args = args[1..-1].reject { |arg| arg == "--" }.last(3)
  last_args.each do |arg|
    if arg =~ /^-/
      # Skip mandatory arguments.
      last_args.shift if arg =~ /^(--(ref|o|br|u|t|d)[^=]+|-(o|b|u|d))$/
      next
    end

    if arg =~ %r{.+?://|.+?@} || 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
  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 -p myfork topsecret > open github.com/myfork/REPO/compare/topsecret $ hub compare -u 1.0…2.0 prints “github.com/CURRENT_REPO/compare/1.0…2.0



386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
# File 'lib/hub/commands.rb', line 386

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



258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/hub/commands.rb', line 258

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
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



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/hub/commands.rb', line 147

def fetch(args)
  # $ hub fetch --multiple <name1>, <name2>, ...
  if args.include?('--multiple')
    names = args.words[1..-1]
  # $ hub fetch <name>
  elsif name = args.words[1]
    # $ hub fetch <name1>,<name2>,...
    if name =~ /^\w+(,\w+)+$/
      index = args.index(name)
      args.delete(name)
      names = name.split(',')
      args.insert(index, *names)
      args.insert(index, '--multiple')
    else
      names = [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?
    commands = names.map { |name| "git remote add #{name} #{github_url(:user => name)}" }
    commands << args.to_exec.join(' ')
    args.replace commands.shift.split(' ')
    args.shift # don't want "git"
    args.after commands.join('; ')
  end
end

#fork(args) ⇒ Object

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



236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
# File 'lib/hub/commands.rb', line 236

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
end

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

$ hub help (print improved help text)



475
476
477
478
479
480
481
482
483
484
485
486
# File 'lib/hub/commands.rb', line 475

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



411
412
413
414
415
416
417
418
# File 'lib/hub/commands.rb', line 411

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.



491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
# File 'lib/hub/commands.rb', line 491

def improved_help_text
  <<-help
usage: git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]
[-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR]
[--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]

Basic Commands:
   init       Create an empty git repository or reinitialize an existing one
   add        Add new or modified files to the staging area
   rm         Remove files from the working directory and staging area
   mv         Move or rename a file, a directory, or a symlink
   status     Show the status of the working directory and staging area
   commit     Record changes to the repository

History Commands:
   log        Show the commit history log
   diff       Show changes between commits, commit and working tree, etc
   show       Show information about commits, tags or files

Branching Commands:
   branch     List, create, or delete branches
   checkout   Switch the active branch to another branch
   merge      Join two or more development histories (branches) together
   tag        Create, list, delete, sign or verify a tag object

Remote Commands:
   clone      Clone a remote repository into a new directory
   fetch      Download data, tags and branches from a remote repository
   pull       Fetch from and merge with another repository or a local branch
   push       Upload data, tags and branches to a remote repository
   remote     View and manage a set of remote repositories

Advanced commands:
   reset      Reset your staging area or working directory to another point
   rebase     Re-apply a series of patches in one branch onto another
   bisect     Find by binary search the change that introduced a bug
   grep       Print files with lines matching a pattern in your codebase

See 'git help COMMAND' for more information on a specific command.
help
end

#init(args) ⇒ Object

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



226
227
228
229
230
231
# File 'lib/hub/commands.rb', line 226

def init(args)
  if args.delete('-g')
    url = github_url(:private => true, :repo => File.basename(Dir.pwd))
    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



302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
# File 'lib/hub/commands.rb', line 302

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

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

  after = "git push #{remotes.shift} #{branch}"

  while remotes.length > 0
    after += "; git push #{remotes.shift} #{branch}"
  end

  args.after after
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



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/hub/commands.rb', line 112

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

#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



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/hub/commands.rb', line 86

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)



466
467
468
469
470
# File 'lib/hub/commands.rb', line 466

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