]> Tony Duckles's Git Repositories (git.nynim.org) - dotfiles.git/blob - bin/hub
argv: turns arguments into lines (useful for testing shell quotes)
[dotfiles.git] / bin / hub
1 #!/usr/bin/env ruby
2 #
3 # This file, hub, is generated code.
4 # Please DO NOT EDIT or send patches for it.
5 #
6 # Please take a look at the source from
7 # http://github.com/defunkt/hub
8 # and submit patches against the individual files
9 # that build hub.
10 #
11
12 module Hub
13 class Args < Array
14 attr_accessor :executable
15
16 def initialize(*args)
17 super
18 @executable = ENV["GIT"] || "git"
19 @after = nil
20 @skip = false
21 @original_args = args.first
22 @chain = [nil]
23 end
24
25 def after(cmd_or_args = nil, args = nil, &block)
26 @chain.insert(-1, normalize_callback(cmd_or_args, args, block))
27 end
28
29 def before(cmd_or_args = nil, args = nil, &block)
30 @chain.insert(@chain.index(nil), normalize_callback(cmd_or_args, args, block))
31 end
32
33 def chained?
34 @chain.size > 1
35 end
36
37 def commands
38 chain = @chain.dup
39 chain[chain.index(nil)] = self.to_exec
40 chain
41 end
42
43 def skip!
44 @skip ||= true
45 end
46
47 def skip?
48 @skip
49 end
50
51 def to_exec(args = self)
52 Array(executable) + args
53 end
54
55 def words
56 reject { |arg| arg.index('-') == 0 }
57 end
58
59 def flags
60 self - words
61 end
62
63 def changed?
64 chained? or self != @original_args
65 end
66
67 def has_flag?(*flags)
68 pattern = flags.flatten.map { |f| Regexp.escape(f) }.join('|')
69 !grep(/^#{pattern}(?:=|$)/).empty?
70 end
71
72 private
73
74 def normalize_callback(cmd_or_args, args, block)
75 if block
76 block
77 elsif args
78 [cmd_or_args].concat args
79 elsif Array === cmd_or_args
80 self.to_exec cmd_or_args
81 elsif cmd_or_args
82 cmd_or_args
83 else
84 raise ArgumentError, "command or block required"
85 end
86 end
87 end
88 end
89 module Hub
90 module Context; end
91
92 module Commands
93 instance_methods.each { |m| undef_method(m) unless m =~ /(^__|send|to\?$)/ }
94 extend self
95
96 extend Context
97
98 API_REPO = 'http://github.com/api/v2/yaml/repos/show/%s/%s'
99 API_FORK = 'http://github.com/api/v2/yaml/repos/fork/%s/%s'
100 API_CREATE = 'http://github.com/api/v2/yaml/repos/create'
101
102 def run(args)
103 slurp_global_flags(args)
104
105 args.unshift 'help' if args.empty?
106
107 cmd = args[0]
108 expanded_args = expand_alias(cmd)
109 cmd = expanded_args[0] if expanded_args
110
111 cmd = cmd.sub(/(\w)-/, '\1_')
112 if method_defined?(cmd) and cmd != 'run'
113 args[0, 1] = expanded_args if expanded_args
114 send(cmd, args)
115 end
116 end
117
118 def clone(args)
119 ssh = args.delete('-p')
120 has_values = /^(--(upload-pack|template|depth|origin|branch|reference)|-[ubo])$/
121
122 idx = 1
123 while idx < args.length
124 arg = args[idx]
125 if arg.index('-') == 0
126 idx += 1 if arg =~ has_values
127 elsif arg.index('://') or arg.index('@') or File.directory?(arg)
128 break
129 elsif arg.scan('/').size <= 1 && !arg.include?(':')
130 args[args.index(arg)] = github_url(:repo => arg, :private => ssh)
131 break
132 end
133 idx += 1
134 end
135 end
136
137 def submodule(args)
138 return unless index = args.index('add')
139 args.delete_at index
140
141 branch = args.index('-b') || args.index('--branch')
142 if branch
143 args.delete_at branch
144 branch_name = args.delete_at branch
145 end
146
147 clone(args)
148
149 if branch_name
150 args.insert branch, '-b', branch_name
151 end
152 args.insert index, 'add'
153 end
154
155 def remote(args)
156 return unless ['add','set-url'].include?(args[1]) && args.last !~ %r{.+?://|.+?@|^[./]}
157
158 ssh = args.delete('-p')
159
160 args.last =~ /\b(.+?)(?:\/(.+))?$/
161 user, repo = $1, $2
162
163 if args.words[2] == 'origin' && args.words[3].nil?
164 user = repo = nil
165 elsif args.words[-2] == args.words[1]
166 idx = args.index( args.words[-1] )
167 args[idx] = user
168 else
169 args.replace args[0...-1]
170 end
171
172 args << github_url(:user => user, :repo => repo, :private => ssh)
173 end
174
175 def fetch(args)
176 if args.include?('--multiple')
177 names = args.words[1..-1]
178 elsif remote_name = args.words[1]
179 if remote_name =~ /^\w+(,\w+)+$/
180 index = args.index(remote_name)
181 args.delete(remote_name)
182 names = remote_name.split(',')
183 args.insert(index, *names)
184 args.insert(index, '--multiple')
185 else
186 names = [remote_name]
187 end
188 else
189 names = []
190 end
191
192 names.reject! { |name|
193 name =~ /\W/ or remotes.include?(name) or
194 remotes_group(name) or not repo_exists?(name)
195 }
196
197 if names.any?
198 names.each do |name|
199 args.before ['remote', 'add', name, github_url(:user => name)]
200 end
201 end
202 end
203
204 def cherry_pick(args)
205 unless args.include?('-m') or args.include?('--mainline')
206 case ref = args.words.last
207 when %r{^(?:https?:)//github.com/(.+?)/(.+?)/commit/([a-f0-9]{7,40})}
208 user, repo, sha = $1, $2, $3
209 args[args.index(ref)] = sha
210 when /^(\w+)@([a-f1-9]{7,40})$/
211 user, repo, sha = $1, nil, $2
212 args[args.index(ref)] = sha
213 else
214 user = nil
215 end
216
217 if user
218 if user == repo_owner
219 args.before ['fetch', default_remote]
220 elsif remotes.include?(user)
221 args.before ['fetch', user]
222 else
223 remote_url = github_url(:user => user, :repo => repo, :private => false)
224 args.before ['remote', 'add', '-f', user, remote_url]
225 end
226 end
227 end
228 end
229
230 def am(args)
231 if url = args.find { |a| a =~ %r{^https?://(gist\.)?github\.com/} }
232 idx = args.index(url)
233 gist = $1 == 'gist.'
234 url = url.sub(%r{(/pull/\d+)/\w*$}, '\1') unless gist
235 ext = gist ? '.txt' : '.patch'
236 url += ext unless File.extname(url) == ext
237 patch_file = File.join(ENV['TMPDIR'], "#{gist ? 'gist-' : ''}#{File.basename(url)}")
238 args.before 'curl', ['-#LA', "hub #{Hub::Version}", url, '-o', patch_file]
239 args[idx] = patch_file
240 end
241 end
242
243 def init(args)
244 if args.delete('-g')
245 url = github_url(:private => true, :repo => current_dirname)
246 args.after "git remote add origin #{url}"
247 end
248 end
249
250 def fork(args)
251 if github_user && github_token && repo_owner
252 if repo_exists?(github_user)
253 puts "#{github_user}/#{repo_name} already exists on GitHub"
254 else
255 fork_repo
256 end
257
258 if args.include?('--no-remote')
259 exit
260 else
261 url = github_url(:private => true)
262 args.replace %W"remote add -f #{github_user} #{url}"
263 args.after { puts "new remote: #{github_user}" }
264 end
265 end
266 rescue Net::HTTPExceptions
267 response = $!.response
268 warn "error creating fork: #{response.message} (HTTP #{response.code})"
269 exit 1
270 end
271
272 def create(args)
273 if !is_repo?
274 puts "'create' must be run from inside a git repository"
275 args.skip!
276 elsif github_user && github_token
277 args.shift
278 options = {}
279 options[:private] = true if args.delete('-p')
280
281 until args.empty?
282 case arg = args.shift
283 when '-d'
284 options[:description] = args.shift
285 when '-h'
286 options[:homepage] = args.shift
287 else
288 puts "unexpected argument: #{arg}"
289 return
290 end
291 end
292
293 if repo_exists?(github_user)
294 puts "#{github_user}/#{repo_name} already exists on GitHub"
295 action = "set remote origin"
296 else
297 action = "created repository"
298 create_repo(options)
299 end
300
301 url = github_url(:private => true)
302
303 if remotes.first != 'origin'
304 args.replace %W"remote add -f origin #{url}"
305 else
306 args.replace %W"remote -v"
307 end
308
309 args.after { puts "#{action}: #{github_user}/#{repo_name}" }
310 end
311 rescue Net::HTTPExceptions
312 response = $!.response
313 warn "error creating repository: #{response.message} (HTTP #{response.code})"
314 exit 1
315 end
316
317 def push(args)
318 return if args[1].nil? || !args[1].index(',')
319
320 branch = (args[2] ||= normalize_branch(current_branch))
321 remotes = args[1].split(',')
322 args[1] = remotes.shift
323
324 remotes.each do |name|
325 args.after ['push', name, branch]
326 end
327 end
328
329 def browse(args)
330 args.shift
331 browse_command(args) do
332 user = repo = nil
333 dest = args.shift
334 dest = nil if dest == '--'
335
336 if dest
337 repo = dest
338 elsif repo_user
339 user = repo_user
340 else
341 abort "Usage: hub browse [<USER>/]<REPOSITORY>"
342 end
343
344 params = { :user => user, :repo => repo }
345
346 case subpage = args.shift
347 when 'commits'
348 branch = (!dest && tracked_branch) || 'master'
349 params[:web] = "/commits/#{branch}"
350 when 'tree', NilClass
351 branch = !dest && tracked_branch
352 params[:web] = "/tree/#{branch}" if branch && branch != 'master'
353 else
354 params[:web] = "/#{subpage}"
355 end
356
357 params
358 end
359 end
360
361 def compare(args)
362 args.shift
363 browse_command(args) do
364 if args.empty?
365 branch = tracked_branch
366 if branch && branch != 'master'
367 range, user = branch, repo_user
368 else
369 abort "Usage: hub compare [USER] [<START>...]<END>"
370 end
371 else
372 range = args.pop
373 user = args.pop || repo_user
374 end
375 { :user => user, :web => "/compare/#{range}" }
376 end
377 end
378
379 def hub(args)
380 return help(args) unless args[1] == 'standalone'
381 require 'hub/standalone'
382 puts Hub::Standalone.build
383 exit
384 rescue LoadError
385 abort "hub is running in standalone mode."
386 end
387
388 def alias(args)
389 shells = {
390 'sh' => 'alias git=hub',
391 'bash' => 'alias git=hub',
392 'zsh' => 'function git(){hub "$@"}',
393 'csh' => 'alias git hub',
394 'fish' => 'alias git hub'
395 }
396
397 silent = args.delete('-s')
398
399 if shell = args[1]
400 if silent.nil?
401 puts "Run this in your shell to start using `hub` as `git`:"
402 print " "
403 end
404 else
405 puts "usage: hub alias [-s] SHELL", ""
406 puts "You already have hub installed and available in your PATH,"
407 puts "but to get the full experience you'll want to alias it to"
408 puts "`git`.", ""
409 puts "To see how to accomplish this for your shell, run the alias"
410 puts "command again with the name of your shell.", ""
411 puts "Known shells:"
412 shells.map { |key, _| key }.sort.each do |key|
413 puts " " + key
414 end
415 puts "", "Options:"
416 puts " -s Silent. Useful when using the output with eval, e.g."
417 puts " $ eval `hub alias -s bash`"
418
419 exit
420 end
421
422 if shells[shell]
423 puts shells[shell]
424 else
425 abort "fatal: never heard of `#{shell}'"
426 end
427
428 exit
429 end
430
431 def version(args)
432 args.after do
433 puts "hub version %s" % Version
434 end
435 end
436 alias_method "--version", :version
437
438 def help(args)
439 command = args.words[1]
440
441 if command == 'hub'
442 puts hub_manpage
443 exit
444 elsif command.nil? && !args.has_flag?('-a', '--all')
445 ENV['GIT_PAGER'] = '' unless args.has_flag?('-p', '--paginate') # Use `cat`.
446 puts improved_help_text
447 exit
448 end
449 end
450 alias_method "--help", :help
451
452 def improved_help_text
453 <<-help
454 usage: git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]
455 [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR]
456 [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]
457
458 Basic Commands:
459 init Create an empty git repository or reinitialize an existing one
460 add Add new or modified files to the staging area
461 rm Remove files from the working directory and staging area
462 mv Move or rename a file, a directory, or a symlink
463 status Show the status of the working directory and staging area
464 commit Record changes to the repository
465
466 History Commands:
467 log Show the commit history log
468 diff Show changes between commits, commit and working tree, etc
469 show Show information about commits, tags or files
470
471 Branching Commands:
472 branch List, create, or delete branches
473 checkout Switch the active branch to another branch
474 merge Join two or more development histories (branches) together
475 tag Create, list, delete, sign or verify a tag object
476
477 Remote Commands:
478 clone Clone a remote repository into a new directory
479 fetch Download data, tags and branches from a remote repository
480 pull Fetch from and merge with another repository or a local branch
481 push Upload data, tags and branches to a remote repository
482 remote View and manage a set of remote repositories
483
484 Advanced commands:
485 reset Reset your staging area or working directory to another point
486 rebase Re-apply a series of patches in one branch onto another
487 bisect Find by binary search the change that introduced a bug
488 grep Print files with lines matching a pattern in your codebase
489
490 See 'git help COMMAND' for more information on a specific command.
491 help
492 end
493
494 private
495
496 def slurp_global_flags(args)
497 flags = %w[ -c -p --paginate --no-pager --no-replace-objects --bare --version --help ]
498 flags2 = %w[ --exec-path= --git-dir= --work-tree= ]
499
500 globals = []
501 locals = []
502
503 while args[0] && (flags.include?(args[0]) || flags2.any? {|f| args[0].index(f) == 0 })
504 flag = args.shift
505 case flag
506 when '--version', '--help'
507 args.unshift flag.sub('--', '')
508 when '-c'
509 config_pair = args.shift
510 key, value = config_pair.split('=', 2)
511 Context::GIT_CONFIG["config #{key}"] = value.to_s
512
513 globals << flag << config_pair
514 when '-p', '--paginate', '--no-pager'
515 locals << flag
516 else
517 globals << flag
518 end
519 end
520
521 Context::GIT_CONFIG.executable = Array(Context::GIT_CONFIG.executable).concat(globals)
522 args.executable = Array(args.executable).concat(globals).concat(locals)
523 end
524
525 def browse_command(args)
526 url_only = args.delete('-u')
527 $stderr.puts "Warning: the `-p` flag has no effect anymore" if args.delete('-p')
528 params = yield
529
530 args.executable = url_only ? 'echo' : browser_launcher
531 args.push github_url({:web => true, :private => true}.update(params))
532 end
533
534 def hub_manpage
535 return "** Can't find groff(1)" unless command?('groff')
536
537 require 'open3'
538 out = nil
539 Open3.popen3(groff_command) do |stdin, stdout, _|
540 stdin.puts hub_raw_manpage
541 stdin.close
542 out = stdout.read.strip
543 end
544 out
545 end
546
547 def groff_command
548 "groff -Wall -mtty-char -mandoc -Tascii"
549 end
550
551 def hub_raw_manpage
552 if File.exists? file = File.dirname(__FILE__) + '/../../man/hub.1'
553 File.read(file)
554 else
555 DATA.read
556 end
557 end
558
559 def puts(*args)
560 page_stdout
561 super
562 end
563
564 def page_stdout
565 return unless $stdout.tty?
566
567 read, write = IO.pipe
568
569 if Kernel.fork
570 $stdin.reopen(read)
571 read.close
572 write.close
573
574 ENV['LESS'] = 'FSRX'
575
576 Kernel.select [STDIN]
577
578 pager = ENV['GIT_PAGER'] ||
579 `git config --get-all core.pager`.split.first || ENV['PAGER'] ||
580 'less -isr'
581
582 pager = 'cat' if pager.empty?
583
584 exec pager rescue exec "/bin/sh", "-c", pager
585 else
586 $stdout.reopen(write)
587 $stderr.reopen(write) if $stderr.tty?
588 read.close
589 write.close
590 end
591 end
592
593 def repo_exists?(user)
594 require 'net/http'
595 url = API_REPO % [user, repo_name]
596 Net::HTTPSuccess === Net::HTTP.get_response(URI(url))
597 end
598
599 def fork_repo
600 url = API_FORK % [repo_owner, repo_name]
601 response = Net::HTTP.post_form(URI(url), 'login' => github_user, 'token' => github_token)
602 response.error! unless Net::HTTPSuccess === response
603 end
604
605 def create_repo(options = {})
606 url = API_CREATE
607 params = {'login' => github_user, 'token' => github_token, 'name' => repo_name}
608 params['public'] = '0' if options[:private]
609 params['description'] = options[:description] if options[:description]
610 params['homepage'] = options[:homepage] if options[:homepage]
611
612 response = Net::HTTP.post_form(URI(url), params)
613 response.error! unless Net::HTTPSuccess === response
614 end
615
616 def expand_alias(cmd)
617 if expanded = git_alias_for(cmd)
618 if expanded.index('!') != 0
619 require 'shellwords' unless defined?(::Shellwords)
620 Shellwords.shellwords(expanded)
621 end
622 end
623 end
624
625 end
626 end
627 require 'shellwords'
628
629 module Hub
630 module Context
631 private
632
633 class ShellOutCache < Hash
634 attr_accessor :executable
635
636 def initialize(executable = nil, &block)
637 super(&block)
638 @executable = executable
639 end
640
641 def to_exec(args)
642 args = Shellwords.shellwords(args) if args.respond_to? :to_str
643 Array(executable) + Array(args)
644 end
645 end
646
647 GIT_CONFIG = ShellOutCache.new(ENV['GIT'] || 'git') do |cache, cmd|
648 full_cmd = cache.to_exec(cmd)
649 cmd_string = full_cmd.respond_to?(:shelljoin) ? full_cmd.shelljoin : full_cmd.join(' ')
650 result = %x{#{cmd_string}}.chomp
651 cache[cmd] = $?.success? && !result.empty? ? result : nil
652 end
653
654 REMOTES = Hash.new do |cache, remote|
655 if remote
656 urls = GIT_CONFIG["config --get-all remote.#{remote}.url"].to_s.split("\n")
657
658 if urls.find { |u| u =~ %r{\bgithub\.com[:/](.+)/(.+).git$} }
659 cache[remote] = { :user => $1, :repo => $2 }
660 else
661 cache[remote] = { }
662 end
663 else
664 cache[remote] = { }
665 end
666 end
667
668 LGHCONF = "http://help.github.com/git-email-settings/"
669
670 def repo_owner
671 REMOTES[default_remote][:user]
672 end
673
674 def repo_user
675 REMOTES[current_remote][:user]
676 end
677
678 def repo_name
679 REMOTES[default_remote][:repo] || current_dirname
680 end
681
682 def github_user(fatal = true)
683 if user = ENV['GITHUB_USER'] || GIT_CONFIG['config github.user']
684 user
685 elsif fatal
686 abort("** No GitHub user set. See #{LGHCONF}")
687 end
688 end
689
690 def github_token(fatal = true)
691 if token = ENV['GITHUB_TOKEN'] || GIT_CONFIG['config github.token']
692 token
693 elsif fatal
694 abort("** No GitHub token set. See #{LGHCONF}")
695 end
696 end
697
698 def current_branch
699 GIT_CONFIG['symbolic-ref -q HEAD']
700 end
701
702 def tracked_branch
703 branch = current_branch && tracked_for(current_branch)
704 normalize_branch(branch) if branch
705 end
706
707 def remotes
708 list = GIT_CONFIG['remote'].to_s.split("\n")
709 main = list.delete('origin') and list.unshift(main)
710 list
711 end
712
713 def remotes_group(name)
714 GIT_CONFIG["config remotes.#{name}"]
715 end
716
717 def current_remote
718 return if remotes.empty?
719 (current_branch && remote_for(current_branch)) || default_remote
720 end
721
722 def default_remote
723 remotes.first
724 end
725
726 def normalize_branch(branch)
727 branch.sub('refs/heads/', '')
728 end
729
730 def remote_for(branch)
731 GIT_CONFIG['config branch.%s.remote' % normalize_branch(branch)]
732 end
733
734 def tracked_for(branch)
735 GIT_CONFIG['config branch.%s.merge' % normalize_branch(branch)]
736 end
737
738 def http_clone?
739 GIT_CONFIG['config --bool hub.http-clone'] == 'true'
740 end
741
742 def git_alias_for(name)
743 GIT_CONFIG["config alias.#{name}"]
744 end
745
746 def is_repo?
747 GIT_CONFIG['config core.repositoryformatversion']
748 end
749
750 def github_url(options = {})
751 repo = options[:repo]
752 user, repo = repo.split('/') if repo && repo.index('/')
753 user ||= options[:user] || github_user
754 repo ||= repo_name
755 secure = options[:private]
756
757 if options[:web]
758 scheme = secure ? 'https:' : 'http:'
759 path = options[:web] == true ? '' : options[:web].to_s
760 if repo =~ /\.wiki$/
761 repo = repo.sub(/\.wiki$/, '')
762 unless '/wiki' == path
763 path = '/wiki%s' % if path =~ %r{^/commits/} then '/_history'
764 else path.sub(/\w+/, '_\0')
765 end
766 end
767 end
768 '%s//github.com/%s/%s%s' % [scheme, user, repo, path]
769 else
770 if secure
771 url = 'git@github.com:%s/%s.git'
772 elsif http_clone?
773 url = 'http://github.com/%s/%s.git'
774 else
775 url = 'git://github.com/%s/%s.git'
776 end
777
778 url % [user, repo]
779 end
780 end
781
782 DIRNAME = File.basename(Dir.pwd)
783
784 def current_dirname
785 DIRNAME
786 end
787
788 def browser_launcher
789 require 'rbconfig'
790 browser = ENV['BROWSER'] ||
791 (RbConfig::CONFIG['host_os'].include?('darwin') && 'open') ||
792 (RbConfig::CONFIG['host_os'] =~ /msdos|mswin|djgpp|mingw|windows/ && 'start') ||
793 %w[xdg-open cygstart x-www-browser firefox opera mozilla netscape].find { |comm| which comm }
794
795 abort "Please set $BROWSER to a web launcher to use this command." unless browser
796 Array(browser)
797 end
798
799 def which(cmd)
800 exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
801 ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
802 exts.each { |ext|
803 exe = "#{path}/#{cmd}#{ext}"
804 return exe if File.executable? exe
805 }
806 end
807 return nil
808 end
809
810 def command?(name)
811 !which(name).nil?
812 end
813 end
814 end
815 module Hub
816 class Runner
817 attr_reader :args
818
819 def initialize(*args)
820 @args = Args.new(args)
821 Commands.run(@args)
822 end
823
824 def self.execute(*args)
825 new(*args).execute
826 end
827
828 def command
829 if args.skip?
830 ''
831 else
832 commands.join('; ')
833 end
834 end
835
836 def commands
837 args.commands.map do |cmd|
838 if cmd.respond_to?(:join)
839 cmd.map { |c| (c.index(' ') || c.empty?) ? "'#{c}'" : c }.join(' ')
840 else
841 cmd.to_s
842 end
843 end
844 end
845
846 def execute
847 unless args.skip?
848 if args.chained?
849 execute_command_chain
850 else
851 exec(*args.to_exec)
852 end
853 end
854 end
855
856 def execute_command_chain
857 commands = args.commands
858 commands.each_with_index do |cmd, i|
859 if cmd.respond_to?(:call) then cmd.call
860 elsif i == commands.length - 1
861 exec(*cmd)
862 else
863 exit($?.exitstatus) unless system(*cmd)
864 end
865 end
866 end
867 end
868 end
869 module Hub
870 Version = VERSION = '1.6.1'
871 end
872 Hub::Runner.execute(*ARGV)
873 __END__
874 .\" generated with Ronn/v0.7.3
875 .\" http://github.com/rtomayko/ronn/tree/0.7.3
876 .
877 .TH "HUB" "1" "May 2011" "DEFUNKT" "Git Manual"
878 .
879 .SH "NAME"
880 \fBhub\fR \- git + hub = github
881 .
882 .SH "SYNOPSIS"
883 \fBhub\fR \fICOMMAND\fR \fIOPTIONS\fR
884 .
885 .br
886 \fBhub alias\fR [\fB\-s\fR] \fISHELL\fR
887 .
888 .P
889 \fBgit init \-g\fR \fIOPTIONS\fR
890 .
891 .br
892 \fBgit create\fR [\fB\-p\fR] [\fB\-d <DESCRIPTION>\fR] [\fB\-h <HOMEPAGE>\fR]
893 .
894 .br
895 \fBgit clone\fR [\fB\-p\fR] \fIOPTIONS\fR [\fIUSER\fR/]\fIREPOSITORY\fR \fIDIRECTORY\fR
896 .
897 .br
898 \fBgit remote add\fR [\fB\-p\fR] \fIOPTIONS\fR \fIUSER\fR[/\fIREPOSITORY\fR]
899 .
900 .br
901 \fBgit remote set\-url\fR [\fB\-p\fR] \fIOPTIONS\fR \fIREMOTE\-NAME\fR \fIUSER\fR[/\fIREPOSITORY\fR]
902 .
903 .br
904 \fBgit fetch\fR \fIUSER\-1\fR,[\fIUSER\-2\fR,\.\.\.]
905 .
906 .br
907 \fBgit cherry\-pick\fR \fIGITHUB\-REF\fR
908 .
909 .br
910 \fBgit am\fR \fIGITHUB\-URL\fR
911 .
912 .br
913 \fBgit push\fR \fIREMOTE\-1\fR,\fIREMOTE\-2\fR,\.\.\.,\fIREMOTE\-N\fR [\fIREF\fR]
914 .
915 .br
916 \fBgit browse\fR [\fB\-u\fR] [[\fIUSER\fR\fB/\fR]\fIREPOSITORY\fR] [SUBPAGE]
917 .
918 .br
919 \fBgit compare\fR [\fB\-u\fR] [\fIUSER\fR] [\fISTART\fR\.\.\.]\fIEND\fR
920 .
921 .br
922 \fBgit submodule add\fR [\fB\-p\fR] \fIOPTIONS\fR [\fIUSER\fR/]\fIREPOSITORY\fR \fIDIRECTORY\fR
923 .
924 .br
925 \fBgit fork\fR [\fB\-\-no\-remote\fR]
926 .
927 .SH "DESCRIPTION"
928 \fBhub\fR enhances various \fBgit\fR commands with GitHub remote expansion\. The alias command displays information on configuring your environment:
929 .
930 .IP "\(bu" 4
931 \fBhub alias\fR [\fB\-s\fR] \fISHELL\fR: Writes shell aliasing code for \fISHELL\fR (\fBbash\fR, \fBsh\fR, \fBzsh\fR, \fBcsh\fR) to standard output\. With the \fB\-s\fR option, the output of this command can be evaluated directly within the shell:
932 .
933 .br
934 \fBeval $(hub alias \-s bash)\fR
935 .
936 .IP "\(bu" 4
937 \fBgit init\fR \fB\-g\fR \fIOPTIONS\fR: Create a git repository as with git\-init(1) and add remote \fBorigin\fR at "git@github\.com:\fIUSER\fR/\fIREPOSITORY\fR\.git"; \fIUSER\fR is your GitHub username and \fIREPOSITORY\fR is the current working directory\'s basename\.
938 .
939 .IP "\(bu" 4
940 \fBgit create\fR [\fB\-p\fR] [\fB\-d <DESCRIPTION>\fR] [\fB\-h <HOMEPAGE>\fR]:
941 .
942 .br
943 Create a new public github repository from the current git repository and add remote \fBorigin\fR at "git@github\.com:\fIUSER\fR/\fIREPOSITORY\fR\.git"; \fIUSER\fR is your GitHub username and \fIREPOSITORY\fR is the current working directory\'s basename\. With \fB\-p\fR, create a private repository\. \fB\-d\fR and \fB\-h\fR set the repository\'s description and homepage, respectively\.
944 .
945 .IP "\(bu" 4
946 \fBgit clone\fR [\fB\-p\fR] \fIOPTIONS\fR [\fIUSER\fR\fB/\fR]\fIREPOSITORY\fR \fIDIRECTORY\fR:
947 .
948 .br
949 Clone repository "git://github\.com/\fIUSER\fR/\fIREPOSITORY\fR\.git" into \fIDIRECTORY\fR as with git\-clone(1)\. When \fIUSER\fR/ is omitted, assumes your GitHub login\. With \fB\-p\fR, use private remote "git@github\.com:\fIUSER\fR/\fIREPOSITORY\fR\.git"\.
950 .
951 .IP "\(bu" 4
952 \fBgit remote add\fR [\fB\-p\fR] \fIOPTIONS\fR \fIUSER\fR[\fB/\fR\fIREPOSITORY\fR]:
953 .
954 .br
955 Add remote "git://github\.com/\fIUSER\fR/\fIREPOSITORY\fR\.git" as with git\-remote(1)\. When /\fIREPOSITORY\fR is omitted, the basename of the current working directory is used\. With \fB\-p\fR, use private remote "git@github\.com:\fIUSER\fR/\fIREPOSITORY\fR\.git"\. If \fIUSER\fR is "origin" then uses your GitHub login\.
956 .
957 .IP "\(bu" 4
958 \fBgit remote set\-url\fR [\fB\-p\fR] \fIOPTIONS\fR \fIREMOTE\-NAME\fR \fIUSER\fR[/\fIREPOSITORY\fR]
959 .
960 .br
961 Sets the url of remote \fIREMOTE\-NAME\fR using the same rules as \fBgit remote add\fR\.
962 .
963 .IP "\(bu" 4
964 \fBgit fetch\fR \fIUSER\-1\fR,[\fIUSER\-2\fR,\.\.\.]: Adds missing remote(s) with \fBgit remote add\fR prior to fetching\. New remotes are only added if they correspond to valid forks on GitHub\.
965 .
966 .IP "\(bu" 4
967 \fBgit cherry\-pick\fR \fIGITHUB\-REF\fR: Cherry\-pick a commit from a fork using either full URL to the commit or GitHub\-flavored Markdown notation, which is \fBuser@sha\fR\. If the remote doesn\'t yet exist, it will be added\. A \fBgit fetch <user>\fR is issued prior to the cherry\-pick attempt\.
968 .
969 .IP "\(bu" 4
970 \fBgit am\fR \fIGITHUB\-URL\fR: Downloads the patch file for the pull request or commit at the URL and applies that patch from disk with \fBgit am\fR\. Similar to \fBcherry\-pick\fR, but doesn\'t add new remotes\.
971 .
972 .IP "\(bu" 4
973 \fBgit push\fR \fIREMOTE\-1\fR,\fIREMOTE\-2\fR,\.\.\.,\fIREMOTE\-N\fR [\fIREF\fR]: Push \fIREF\fR to each of \fIREMOTE\-1\fR through \fIREMOTE\-N\fR by executing multiple \fBgit push\fR commands\.
974 .
975 .IP "\(bu" 4
976 \fBgit browse\fR [\fB\-u\fR] [[\fIUSER\fR\fB/\fR]\fIREPOSITORY\fR] [SUBPAGE]: Open repository\'s GitHub page in the system\'s default web browser using \fBopen(1)\fR or the \fBBROWSER\fR env variable\. If the repository isn\'t specified, \fBbrowse\fR opens the page of the repository found in the current directory\. If SUBPAGE is specified, the browser will open on the specified subpage: one of "wiki", "commits", "issues" or other (the default is "tree")\.
977 .
978 .IP "\(bu" 4
979 \fBgit compare\fR [\fB\-u\fR] [\fIUSER\fR] [\fISTART\fR\.\.\.]\fIEND\fR: Open a GitHub compare view page in the system\'s default web browser\. \fISTART\fR to \fIEND\fR are branch names, tag names, or commit SHA1s specifying the range of history to compare\. If \fISTART\fR is omitted, GitHub will compare against the base branch (the default is "master")\.
980 .
981 .IP "\(bu" 4
982 \fBgit submodule add\fR [\fB\-p\fR] \fIOPTIONS\fR [\fIUSER\fR/]\fIREPOSITORY\fR \fIDIRECTORY\fR:
983 .
984 .br
985 Submodule repository "git://github\.com/\fIUSER\fR/\fIREPOSITORY\fR\.git" into \fIDIRECTORY\fR as with git\-submodule(1)\. When \fIUSER\fR/ is omitted, assumes your GitHub login\. With \fB\-p\fR, use private remote "git@github\.com:\fIUSER\fR/\fIREPOSITORY\fR\.git"\.
986 .
987 .IP "\(bu" 4
988 \fBgit fork\fR [\fB\-\-no\-remote\fR]: Forks the original project (referenced by "origin" remote) on GitHub and adds a new remote for it under your username\. Requires \fBgithub\.token\fR to be set (see CONFIGURATION)\.
989 .
990 .IP "\(bu" 4
991 \fBgit help\fR: Display enhanced git\-help(1)\.
992 .
993 .IP "" 0
994 .
995 .SH "CONFIGURATION"
996 Use git\-config(1) to display the currently configured GitHub username:
997 .
998 .IP "" 4
999 .
1000 .nf
1001
1002 $ git config \-\-global github\.user
1003 .
1004 .fi
1005 .
1006 .IP "" 0
1007 .
1008 .P
1009 Or, set the GitHub username and token with:
1010 .
1011 .IP "" 4
1012 .
1013 .nf
1014
1015 $ git config \-\-global github\.user <username>
1016 $ git config \-\-global github\.token <token>
1017 .
1018 .fi
1019 .
1020 .IP "" 0
1021 .
1022 .P
1023 See \fIhttp://github\.com/guides/local\-github\-config\fR for more information\.
1024 .
1025 .P
1026 You can also tell \fBhub\fR to use \fBhttp://\fR rather than \fBgit://\fR when cloning:
1027 .
1028 .IP "" 4
1029 .
1030 .nf
1031
1032 $ git config \-\-global \-\-bool hub\.http\-clone true
1033 .
1034 .fi
1035 .
1036 .IP "" 0
1037 .
1038 .P
1039 Want to use environment variables instead of a local gitconfig for authentication?
1040 .
1041 .IP "\(bu" 4
1042 \fBGITHUB_USER\fR \- If set, this will be used instead of the \fBgithub\.user\fR config
1043 .
1044 .IP "\(bu" 4
1045 \fBGITHUB_TOKEN\fR \- If set, this will be used instead of the \fBgithub\.token\fR
1046 .
1047 .IP "" 0
1048 .
1049 .SH "EXAMPLES"
1050 .
1051 .SS "git clone"
1052 .
1053 .nf
1054
1055 $ git clone schacon/ticgit
1056 > git clone git://github\.com/schacon/ticgit\.git
1057
1058 $ git clone \-p schacon/ticgit
1059 > git clone git@github\.com:schacon/ticgit\.git
1060
1061 $ git clone resque
1062 > git clone git://github\.com/YOUR_USER/resque\.git
1063
1064 $ git clone \-p resque
1065 > git clone git@github\.com:YOUR_USER/resque\.git
1066 .
1067 .fi
1068 .
1069 .SS "git remote add"
1070 .
1071 .nf
1072
1073 $ git remote add rtomayko
1074 > git remote add rtomayko git://github\.com/rtomayko/CURRENT_REPO\.git
1075
1076 $ git remote add \-p rtomayko
1077 > git remote add rtomayko git@github\.com:rtomayko/CURRENT_REPO\.git
1078
1079 $ git remote add origin
1080 > git remote add origin git://github\.com/YOUR_USER/CURRENT_REPO\.git
1081 .
1082 .fi
1083 .
1084 .SS "git fetch"
1085 .
1086 .nf
1087
1088 $ git fetch mislav
1089 > git remote add mislav git://github\.com/mislav/REPO\.git
1090 > git fetch mislav
1091
1092 $ git fetch mislav,xoebus
1093 > git remote add mislav \.\.\.
1094 > git remote add xoebus \.\.\.
1095 > git fetch \-\-multiple mislav xoebus
1096 .
1097 .fi
1098 .
1099 .SS "git cherry\-pick"
1100 .
1101 .nf
1102
1103 $ git cherry\-pick http://github\.com/mislav/REPO/commit/SHA
1104 > git remote add \-f mislav git://github\.com/mislav/REPO\.git
1105 > git cherry\-pick SHA
1106
1107 $ git cherry\-pick mislav@SHA
1108 > git remote add \-f mislav git://github\.com/mislav/CURRENT_REPO\.git
1109 > git cherry\-pick SHA
1110
1111 $ git cherry\-pick mislav@SHA
1112 > git fetch mislav
1113 > git cherry\-pick SHA
1114 .
1115 .fi
1116 .
1117 .SS "git am"
1118 .
1119 .nf
1120
1121 $ git am https://github\.com/defunkt/hub/pull/55
1122 > curl https://github\.com/defunkt/hub/pull/55\.patch \-o /tmp/55\.patch
1123 > git am /tmp/55\.patch
1124
1125 $ git am \-\-ignore\-whitespace https://github\.com/davidbalbert/hub/commit/fdb9921
1126 > curl https://github\.com/davidbalbert/hub/commit/fdb9921\.patch \-o /tmp/fdb9921\.patch
1127 > git am \-\-ignore\-whitespace /tmp/fdb9921\.patch
1128 .
1129 .fi
1130 .
1131 .SS "git fork"
1132 .
1133 .nf
1134
1135 $ git fork
1136 \.\.\. hardcore forking action \.\.\.
1137 > git remote add YOUR_USER git@github\.com:YOUR_USER/CURRENT_REPO\.git
1138 .
1139 .fi
1140 .
1141 .SS "git init"
1142 .
1143 .nf
1144
1145 $ git init \-g
1146 > git init
1147 > git remote add origin git@github\.com:YOUR_USER/REPO\.git
1148 .
1149 .fi
1150 .
1151 .SS "git create"
1152 .
1153 .nf
1154
1155 $ git create
1156 \.\.\. hardcore creating action \.\.\.
1157 > git remote add origin git@github\.com:YOUR_USER/CURRENT_REPO\.git
1158 .
1159 .fi
1160 .
1161 .SS "git push"
1162 .
1163 .nf
1164
1165 $ git push origin,staging,qa bert_timeout
1166 > git push origin bert_timeout
1167 > git push staging bert_timeout
1168 > git push qa bert_timeout
1169 .
1170 .fi
1171 .
1172 .SS "git browse"
1173 .
1174 .nf
1175
1176 $ git browse
1177 > open https://github\.com/YOUR_USER/CURRENT_REPO
1178
1179 $ git browse \-\- issues
1180 > open https://github\.com/YOUR_USER/CURRENT_REPO/issues
1181
1182 $ git browse schacon/ticgit
1183 > open https://github\.com/schacon/ticgit
1184
1185 $ git browse resque
1186 > open https://github\.com/YOUR_USER/resque
1187
1188 $ git browse resque network
1189 > open https://github\.com/YOUR_USER/resque/network
1190 .
1191 .fi
1192 .
1193 .SS "git compare"
1194 .
1195 .nf
1196
1197 $ git compare refactor
1198 > open https://github\.com/CURRENT_REPO/compare/refactor
1199
1200 $ git compare 1\.0\.\.\.1\.1
1201 > open https://github\.com/CURRENT_REPO/compare/1\.0\.\.\.1\.1
1202
1203 $ git compare \-u fix
1204 > (https://github\.com/CURRENT_REPO/compare/fix)
1205
1206 $ git compare other\-user patch
1207 > open https://github\.com/other\-user/REPO/compare/patch
1208 .
1209 .fi
1210 .
1211 .SS "git help"
1212 .
1213 .nf
1214
1215 $ git help
1216 > (improved git help)
1217 $ git help hub
1218 > (hub man page)
1219 .
1220 .fi
1221 .
1222 .SH "BUGS"
1223 \fIhttp://github\.com/defunkt/hub/issues\fR
1224 .
1225 .SH "AUTHORS"
1226 \fIhttps://github\.com/defunkt/hub/contributors\fR
1227 .
1228 .SH "SEE ALSO"
1229 git(1), git\-clone(1), git\-remote(1), git\-init(1), \fIhttp://github\.com\fR, \fIhttp://github\.com/defunkt/hub\fR