git-hub 1.5.1 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- metadata +29 -55
- data/LICENSE +0 -20
- data/README.md +0 -339
- data/Rakefile +0 -104
- data/bin/hub +0 -7
- data/lib/hub.rb +0 -5
- data/lib/hub/args.rb +0 -99
- data/lib/hub/commands.rb +0 -716
- data/lib/hub/context.rb +0 -159
- data/lib/hub/runner.rb +0 -71
- data/lib/hub/standalone.rb +0 -52
- data/lib/hub/version.rb +0 -3
- data/man/hub.1 +0 -356
- data/man/hub.1.html +0 -370
- data/man/hub.1.ronn +0 -275
- data/test/alias_test.rb +0 -41
- data/test/deps.rip +0 -1
- data/test/fakebin/git +0 -11
- data/test/fakebin/open +0 -3
- data/test/helper.rb +0 -111
- data/test/hub_test.rb +0 -844
- data/test/standalone_test.rb +0 -49
data/Rakefile
DELETED
@@ -1,104 +0,0 @@
|
|
1
|
-
require 'rake/testtask'
|
2
|
-
|
3
|
-
#
|
4
|
-
# Helpers
|
5
|
-
#
|
6
|
-
|
7
|
-
def command?(command)
|
8
|
-
`which #{command} 2>/dev/null`
|
9
|
-
$?.success?
|
10
|
-
end
|
11
|
-
|
12
|
-
task :load_hub do
|
13
|
-
$LOAD_PATH.unshift 'lib'
|
14
|
-
require 'hub'
|
15
|
-
end
|
16
|
-
|
17
|
-
task :check_dirty do
|
18
|
-
if !`git status`.include?('nothing to commit')
|
19
|
-
abort "dirty index - not publishing!"
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
|
24
|
-
#
|
25
|
-
# Tests
|
26
|
-
#
|
27
|
-
|
28
|
-
task :default => :test
|
29
|
-
|
30
|
-
if command? :turn
|
31
|
-
desc "Run tests"
|
32
|
-
task :test do
|
33
|
-
suffix = "-n #{ENV['TEST']}" if ENV['TEST']
|
34
|
-
sh "turn test/*.rb #{suffix}"
|
35
|
-
end
|
36
|
-
else
|
37
|
-
Rake::TestTask.new do |t|
|
38
|
-
t.libs << 'lib'
|
39
|
-
t.ruby_opts << '-rubygems'
|
40
|
-
t.pattern = 'test/**/*_test.rb'
|
41
|
-
t.verbose = false
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
if command? :kicker
|
46
|
-
desc "Launch Kicker (like autotest)"
|
47
|
-
task :kicker do
|
48
|
-
puts "Kicking... (ctrl+c to cancel)"
|
49
|
-
exec "kicker -e rake test lib"
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
|
54
|
-
#
|
55
|
-
# Ron
|
56
|
-
#
|
57
|
-
|
58
|
-
if command? :ronn
|
59
|
-
desc "Show the manual"
|
60
|
-
task :man => "man:build" do
|
61
|
-
exec "man man/hub.1"
|
62
|
-
end
|
63
|
-
|
64
|
-
desc "Build the manual"
|
65
|
-
task "man:build" do
|
66
|
-
sh "ronn -br5 --organization=DEFUNKT --manual='Git Manual' man/*.ronn"
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
|
71
|
-
#
|
72
|
-
# Gems
|
73
|
-
#
|
74
|
-
|
75
|
-
desc "Build standalone script"
|
76
|
-
task :standalone => :load_hub do
|
77
|
-
require 'hub/standalone'
|
78
|
-
Hub::Standalone.save('hub')
|
79
|
-
end
|
80
|
-
|
81
|
-
desc "Install standalone script and man pages"
|
82
|
-
task :install => :standalone do
|
83
|
-
prefix = ENV['PREFIX'] || ENV['prefix'] || '/usr/local'
|
84
|
-
|
85
|
-
FileUtils.mkdir_p "#{prefix}/bin"
|
86
|
-
FileUtils.cp "hub", "#{prefix}/bin"
|
87
|
-
|
88
|
-
FileUtils.mkdir_p "#{prefix}/share/man/man1"
|
89
|
-
FileUtils.cp "man/hub.1", "#{prefix}/share/man/man1"
|
90
|
-
end
|
91
|
-
|
92
|
-
desc "Publish to GitHub Pages"
|
93
|
-
task :pages => [ "man:build", :check_dirty, :standalone ] do
|
94
|
-
cp "man/hub.1.html", "html"
|
95
|
-
sh "git checkout gh-pages"
|
96
|
-
sh "mv hub standalone"
|
97
|
-
sh "git add standalone*"
|
98
|
-
sh "mv html hub.1.html"
|
99
|
-
sh "git add hub.1.html"
|
100
|
-
sh "git commit -m 'update standalone'"
|
101
|
-
sh "git push origin gh-pages"
|
102
|
-
sh "git checkout master"
|
103
|
-
puts :done
|
104
|
-
end
|
data/bin/hub
DELETED
data/lib/hub.rb
DELETED
data/lib/hub/args.rb
DELETED
@@ -1,99 +0,0 @@
|
|
1
|
-
module Hub
|
2
|
-
# The Args class exists to make it more convenient to work with
|
3
|
-
# command line arguments intended for git from within the Hub
|
4
|
-
# codebase.
|
5
|
-
#
|
6
|
-
# The ARGV array is converted into an Args instance by the Hub
|
7
|
-
# instance when instantiated.
|
8
|
-
class Args < Array
|
9
|
-
attr_accessor :executable
|
10
|
-
|
11
|
-
def initialize(*args)
|
12
|
-
super
|
13
|
-
@executable = ENV["GIT"] || "git"
|
14
|
-
@after = nil
|
15
|
-
@skip = false
|
16
|
-
@original_args = args.first
|
17
|
-
@chain = [nil]
|
18
|
-
end
|
19
|
-
|
20
|
-
# Adds an `after` callback.
|
21
|
-
# A callback can be a command or a proc.
|
22
|
-
def after(cmd_or_args = nil, args = nil, &block)
|
23
|
-
@chain.insert(-1, normalize_callback(cmd_or_args, args, block))
|
24
|
-
end
|
25
|
-
|
26
|
-
# Adds a `before` callback.
|
27
|
-
# A callback can be a command or a proc.
|
28
|
-
def before(cmd_or_args = nil, args = nil, &block)
|
29
|
-
@chain.insert(@chain.index(nil), normalize_callback(cmd_or_args, args, block))
|
30
|
-
end
|
31
|
-
|
32
|
-
# Tells if there are multiple (chained) commands or not.
|
33
|
-
def chained?
|
34
|
-
@chain.size > 1
|
35
|
-
end
|
36
|
-
|
37
|
-
# Returns an array of all commands.
|
38
|
-
def commands
|
39
|
-
chain = @chain.dup
|
40
|
-
chain[chain.index(nil)] = self.to_exec
|
41
|
-
chain
|
42
|
-
end
|
43
|
-
|
44
|
-
# Skip running this command.
|
45
|
-
def skip!
|
46
|
-
@skip ||= true
|
47
|
-
end
|
48
|
-
|
49
|
-
# Boolean indicating whether this command will run.
|
50
|
-
def skip?
|
51
|
-
@skip
|
52
|
-
end
|
53
|
-
|
54
|
-
# Array of `executable` followed by all args suitable as arguments
|
55
|
-
# for `exec` or `system` calls.
|
56
|
-
def to_exec(args = self)
|
57
|
-
[executable].concat args
|
58
|
-
end
|
59
|
-
|
60
|
-
# All the words (as opposed to flags) contained in this argument
|
61
|
-
# list.
|
62
|
-
#
|
63
|
-
# args = Args.new([ 'remote', 'add', '-f', 'tekkub' ])
|
64
|
-
# args.words == [ 'remote', 'add', 'tekkub' ]
|
65
|
-
def words
|
66
|
-
reject { |arg| arg.index('-') == 0 }
|
67
|
-
end
|
68
|
-
|
69
|
-
# All the flags (as opposed to words) contained in this argument
|
70
|
-
# list.
|
71
|
-
#
|
72
|
-
# args = Args.new([ 'remote', 'add', '-f', 'tekkub' ])
|
73
|
-
# args.flags == [ '-f' ]
|
74
|
-
def flags
|
75
|
-
self - words
|
76
|
-
end
|
77
|
-
|
78
|
-
# Tests if arguments were modified since instantiation
|
79
|
-
def changed?
|
80
|
-
chained? or self != @original_args
|
81
|
-
end
|
82
|
-
|
83
|
-
private
|
84
|
-
|
85
|
-
def normalize_callback(cmd_or_args, args, block)
|
86
|
-
if block
|
87
|
-
block
|
88
|
-
elsif args
|
89
|
-
[cmd_or_args].concat args
|
90
|
-
elsif Array === cmd_or_args
|
91
|
-
self.to_exec cmd_or_args
|
92
|
-
elsif cmd_or_args
|
93
|
-
cmd_or_args
|
94
|
-
else
|
95
|
-
raise ArgumentError, "command or block required"
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
data/lib/hub/commands.rb
DELETED
@@ -1,716 +0,0 @@
|
|
1
|
-
module Hub
|
2
|
-
# See context.rb
|
3
|
-
module Context; end
|
4
|
-
|
5
|
-
# The Commands module houses the git commands that hub
|
6
|
-
# lovingly wraps. If a method exists here, it is expected to have a
|
7
|
-
# corresponding git command which either gets run before or after
|
8
|
-
# the method executes.
|
9
|
-
#
|
10
|
-
# The typical flow is as follows:
|
11
|
-
#
|
12
|
-
# 1. hub is invoked from the command line:
|
13
|
-
# $ hub clone rtomayko/tilt
|
14
|
-
#
|
15
|
-
# 2. The Hub class is initialized:
|
16
|
-
# >> hub = Hub.new('clone', 'rtomayko/tilt')
|
17
|
-
#
|
18
|
-
# 3. The method representing the git subcommand is executed with the
|
19
|
-
# full args:
|
20
|
-
# >> Commands.clone('clone', 'rtomayko/tilt')
|
21
|
-
#
|
22
|
-
# 4. That method rewrites the args as it sees fit:
|
23
|
-
# >> args[1] = "git://github.com/" + args[1] + ".git"
|
24
|
-
# => "git://github.com/rtomayko/tilt.git"
|
25
|
-
#
|
26
|
-
# 5. The new args are used to run `git`:
|
27
|
-
# >> exec "git", "clone", "git://github.com/rtomayko/tilt.git"
|
28
|
-
#
|
29
|
-
# An optional `after` callback can be set. If so, it is run after
|
30
|
-
# step 5 (which then performs a `system` call rather than an
|
31
|
-
# `exec`). See `Hub::Args` for more information on the `after` callback.
|
32
|
-
module Commands
|
33
|
-
# We are a blank slate.
|
34
|
-
instance_methods.each { |m| undef_method(m) unless m =~ /(^__|send|to\?$)/ }
|
35
|
-
extend self
|
36
|
-
|
37
|
-
# Provides `github_url` and various inspection methods
|
38
|
-
extend Context
|
39
|
-
|
40
|
-
API_REPO = 'http://github.com/api/v2/yaml/repos/show/%s/%s'
|
41
|
-
API_FORK = 'http://github.com/api/v2/yaml/repos/fork/%s/%s'
|
42
|
-
API_CREATE = 'http://github.com/api/v2/yaml/repos/create'
|
43
|
-
|
44
|
-
def run(args)
|
45
|
-
# Hack to emulate git-style
|
46
|
-
args.unshift 'help' if args.grep(/^[^-]|version|exec-path$|html-path/).empty?
|
47
|
-
|
48
|
-
cmd = args[0]
|
49
|
-
expanded_args = expand_alias(cmd)
|
50
|
-
cmd = expanded_args[0] if expanded_args
|
51
|
-
|
52
|
-
# git commands can have dashes
|
53
|
-
cmd = cmd.sub(/(\w)-/, '\1_')
|
54
|
-
if method_defined?(cmd) and cmd != 'run'
|
55
|
-
args[0, 1] = expanded_args if expanded_args
|
56
|
-
send(cmd, args)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
# $ hub clone rtomayko/tilt
|
61
|
-
# > git clone git://github.com/rtomayko/tilt.
|
62
|
-
#
|
63
|
-
# $ hub clone -p kneath/hemingway
|
64
|
-
# > git clone git@github.com:kneath/hemingway.git
|
65
|
-
#
|
66
|
-
# $ hub clone tilt
|
67
|
-
# > git clone git://github.com/YOUR_LOGIN/tilt.
|
68
|
-
#
|
69
|
-
# $ hub clone -p github
|
70
|
-
# > git clone git@github.com:YOUR_LOGIN/hemingway.git
|
71
|
-
def clone(args)
|
72
|
-
ssh = args.delete('-p')
|
73
|
-
has_values = /^(--(upload-pack|template|depth|origin|branch|reference)|-[ubo])$/
|
74
|
-
|
75
|
-
idx = 1
|
76
|
-
while idx < args.length
|
77
|
-
arg = args[idx]
|
78
|
-
if arg.index('-') == 0
|
79
|
-
idx += 1 if arg =~ has_values
|
80
|
-
elsif arg.index('://') or arg.index('@') or File.directory?(arg)
|
81
|
-
# Bail out early for URLs and local paths.
|
82
|
-
break
|
83
|
-
elsif arg.scan('/').size <= 1 && !arg.include?(':')
|
84
|
-
# $ hub clone rtomayko/tilt
|
85
|
-
# $ hub clone tilt
|
86
|
-
args[args.index(arg)] = github_url(:repo => arg, :private => ssh)
|
87
|
-
break
|
88
|
-
end
|
89
|
-
idx += 1
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
# $ hub submodule add wycats/bundler vendor/bundler
|
94
|
-
# > git submodule add git://github.com/wycats/bundler.git vendor/bundler
|
95
|
-
#
|
96
|
-
# $ hub submodule add -p wycats/bundler vendor/bundler
|
97
|
-
# > git submodule add git@github.com:wycats/bundler.git vendor/bundler
|
98
|
-
#
|
99
|
-
# $ hub submodule add -b ryppl ryppl/pip vendor/bundler
|
100
|
-
# > git submodule add -b ryppl git://github.com/ryppl/pip.git vendor/pip
|
101
|
-
def submodule(args)
|
102
|
-
return unless index = args.index('add')
|
103
|
-
args.delete_at index
|
104
|
-
|
105
|
-
branch = args.index('-b') || args.index('--branch')
|
106
|
-
if branch
|
107
|
-
args.delete_at branch
|
108
|
-
branch_name = args.delete_at branch
|
109
|
-
end
|
110
|
-
|
111
|
-
clone(args)
|
112
|
-
|
113
|
-
if branch_name
|
114
|
-
args.insert branch, '-b', branch_name
|
115
|
-
end
|
116
|
-
args.insert index, 'add'
|
117
|
-
end
|
118
|
-
|
119
|
-
# $ hub remote add pjhyett
|
120
|
-
# > git remote add pjhyett git://github.com/pjhyett/THIS_REPO.git
|
121
|
-
#
|
122
|
-
# $ hub remote add -p mojombo
|
123
|
-
# > git remote add mojombo git@github.com:mojombo/THIS_REPO.git
|
124
|
-
#
|
125
|
-
# $ hub remote add origin
|
126
|
-
# > git remote add origin git://github.com/YOUR_LOGIN/THIS_REPO.git
|
127
|
-
def remote(args)
|
128
|
-
return unless ['add','set-url'].include?(args[1]) && args.last !~ %r{.+?://|.+?@|^[./]}
|
129
|
-
|
130
|
-
ssh = args.delete('-p')
|
131
|
-
|
132
|
-
# user/repo
|
133
|
-
args.last =~ /\b(.+?)(?:\/(.+))?$/
|
134
|
-
user, repo = $1, $2
|
135
|
-
|
136
|
-
if args.words[2] == 'origin' && args.words[3].nil?
|
137
|
-
# Origin special case triggers default user/repo
|
138
|
-
user = repo = nil
|
139
|
-
elsif args.words[-2] == args.words[1]
|
140
|
-
# rtomayko/tilt => rtomayko
|
141
|
-
# Make sure you dance around flags.
|
142
|
-
idx = args.index( args.words[-1] )
|
143
|
-
args[idx] = user
|
144
|
-
else
|
145
|
-
# They're specifying the remote name manually (e.g.
|
146
|
-
# git remote add blah rtomayko/tilt), so just drop the last
|
147
|
-
# argument.
|
148
|
-
args.replace args[0...-1]
|
149
|
-
end
|
150
|
-
|
151
|
-
args << github_url(:user => user, :repo => repo, :private => ssh)
|
152
|
-
end
|
153
|
-
|
154
|
-
# $ hub fetch mislav
|
155
|
-
# > git remote add mislav git://github.com/mislav/REPO.git
|
156
|
-
# > git fetch mislav
|
157
|
-
#
|
158
|
-
# $ hub fetch --multiple mislav xoebus
|
159
|
-
# > git remote add mislav ...
|
160
|
-
# > git remote add xoebus ...
|
161
|
-
# > git fetch --multiple mislav xoebus
|
162
|
-
def fetch(args)
|
163
|
-
# $ hub fetch --multiple <name1>, <name2>, ...
|
164
|
-
if args.include?('--multiple')
|
165
|
-
names = args.words[1..-1]
|
166
|
-
# $ hub fetch <name>
|
167
|
-
elsif remote_name = args.words[1]
|
168
|
-
# $ hub fetch <name1>,<name2>,...
|
169
|
-
if remote_name =~ /^\w+(,\w+)+$/
|
170
|
-
index = args.index(remote_name)
|
171
|
-
args.delete(remote_name)
|
172
|
-
names = remote_name.split(',')
|
173
|
-
args.insert(index, *names)
|
174
|
-
args.insert(index, '--multiple')
|
175
|
-
else
|
176
|
-
names = [remote_name]
|
177
|
-
end
|
178
|
-
else
|
179
|
-
names = []
|
180
|
-
end
|
181
|
-
|
182
|
-
names.reject! { |name|
|
183
|
-
name =~ /\W/ or remotes.include?(name) or
|
184
|
-
remotes_group(name) or not repo_exists?(name)
|
185
|
-
}
|
186
|
-
|
187
|
-
if names.any?
|
188
|
-
names.each do |name|
|
189
|
-
args.before ['remote', 'add', name, github_url(:user => name)]
|
190
|
-
end
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
# $ git cherry-pick http://github.com/mislav/hub/commit/a319d88#comments
|
195
|
-
# > git remote add -f mislav git://github.com/mislav/hub.git
|
196
|
-
# > git cherry-pick a319d88
|
197
|
-
#
|
198
|
-
# $ git cherry-pick mislav@a319d88
|
199
|
-
# > git remote add -f mislav git://github.com/mislav/hub.git
|
200
|
-
# > git cherry-pick a319d88
|
201
|
-
#
|
202
|
-
# $ git cherry-pick mislav@SHA
|
203
|
-
# > git fetch mislav
|
204
|
-
# > git cherry-pick SHA
|
205
|
-
def cherry_pick(args)
|
206
|
-
unless args.include?('-m') or args.include?('--mainline')
|
207
|
-
case ref = args.words.last
|
208
|
-
when %r{^(?:https?:)//github.com/(.+?)/(.+?)/commit/([a-f0-9]{7,40})}
|
209
|
-
user, repo, sha = $1, $2, $3
|
210
|
-
args[args.index(ref)] = sha
|
211
|
-
when /^(\w+)@([a-f1-9]{7,40})$/
|
212
|
-
user, repo, sha = $1, nil, $2
|
213
|
-
args[args.index(ref)] = sha
|
214
|
-
else
|
215
|
-
user = nil
|
216
|
-
end
|
217
|
-
|
218
|
-
if user
|
219
|
-
if user == repo_owner
|
220
|
-
# fetch from origin if the repo belongs to the user
|
221
|
-
args.before ['fetch', default_remote]
|
222
|
-
elsif remotes.include?(user)
|
223
|
-
args.before ['fetch', user]
|
224
|
-
else
|
225
|
-
remote_url = github_url(:user => user, :repo => repo, :private => false)
|
226
|
-
args.before ['remote', 'add', '-f', user, remote_url]
|
227
|
-
end
|
228
|
-
end
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
# $ hub am https://github.com/defunkt/hub/pull/55
|
233
|
-
# > curl https://github.com/defunkt/hub/pull/55.patch -o /tmp/55.patch
|
234
|
-
# > git am /tmp/55.patch
|
235
|
-
def am(args)
|
236
|
-
if url = args.find { |a| a =~ %r{^https?://(gist\.)?github\.com/} }
|
237
|
-
idx = args.index(url)
|
238
|
-
gist = $1 == 'gist.'
|
239
|
-
ext = gist ? '.txt' : '.patch'
|
240
|
-
url += ext unless File.extname(url) == ext
|
241
|
-
patch_file = File.join(ENV['TMPDIR'], "#{gist ? 'gist-' : ''}#{File.basename(url)}")
|
242
|
-
args.before 'curl', ['-#LA', "hub #{Hub::Version}", url, '-o', patch_file]
|
243
|
-
args[idx] = patch_file
|
244
|
-
end
|
245
|
-
end
|
246
|
-
|
247
|
-
# $ hub init -g
|
248
|
-
# > git init
|
249
|
-
# > git remote add origin git@github.com:USER/REPO.git
|
250
|
-
def init(args)
|
251
|
-
if args.delete('-g')
|
252
|
-
url = github_url(:private => true, :repo => current_dirname)
|
253
|
-
args.after "git remote add origin #{url}"
|
254
|
-
end
|
255
|
-
end
|
256
|
-
|
257
|
-
# $ hub fork
|
258
|
-
# ... hardcore forking action ...
|
259
|
-
# > git remote add -f YOUR_USER git@github.com:YOUR_USER/CURRENT_REPO.git
|
260
|
-
def fork(args)
|
261
|
-
# can't do anything without token and original owner name
|
262
|
-
if github_user && github_token && repo_owner
|
263
|
-
if repo_exists?(github_user)
|
264
|
-
puts "#{github_user}/#{repo_name} already exists on GitHub"
|
265
|
-
else
|
266
|
-
fork_repo
|
267
|
-
end
|
268
|
-
|
269
|
-
if args.include?('--no-remote')
|
270
|
-
exit
|
271
|
-
else
|
272
|
-
url = github_url(:private => true)
|
273
|
-
args.replace %W"remote add -f #{github_user} #{url}"
|
274
|
-
args.after { puts "new remote: #{github_user}" }
|
275
|
-
end
|
276
|
-
end
|
277
|
-
rescue Net::HTTPExceptions
|
278
|
-
response = $!.response
|
279
|
-
warn "error creating fork: #{response.message} (HTTP #{response.code})"
|
280
|
-
exit 1
|
281
|
-
end
|
282
|
-
|
283
|
-
# $ hub create
|
284
|
-
# ... create repo on github ...
|
285
|
-
# > git remote add -f origin git@github.com:YOUR_USER/CURRENT_REPO.git
|
286
|
-
def create(args)
|
287
|
-
if !is_repo?
|
288
|
-
puts "'create' must be run from inside a git repository"
|
289
|
-
args.skip!
|
290
|
-
elsif github_user && github_token
|
291
|
-
args.shift
|
292
|
-
options = {}
|
293
|
-
options[:private] = true if args.delete('-p')
|
294
|
-
|
295
|
-
until args.empty?
|
296
|
-
case arg = args.shift
|
297
|
-
when '-d'
|
298
|
-
options[:description] = args.shift
|
299
|
-
when '-h'
|
300
|
-
options[:homepage] = args.shift
|
301
|
-
else
|
302
|
-
puts "unexpected argument: #{arg}"
|
303
|
-
return
|
304
|
-
end
|
305
|
-
end
|
306
|
-
|
307
|
-
if repo_exists?(github_user)
|
308
|
-
puts "#{github_user}/#{repo_name} already exists on GitHub"
|
309
|
-
action = "set remote origin"
|
310
|
-
else
|
311
|
-
action = "created repository"
|
312
|
-
create_repo(options)
|
313
|
-
end
|
314
|
-
|
315
|
-
url = github_url(:private => true)
|
316
|
-
|
317
|
-
if remotes.first != 'origin'
|
318
|
-
args.replace %W"remote add -f origin #{url}"
|
319
|
-
else
|
320
|
-
args.replace %W"remote -v"
|
321
|
-
end
|
322
|
-
|
323
|
-
args.after { puts "#{action}: #{github_user}/#{repo_name}" }
|
324
|
-
end
|
325
|
-
rescue Net::HTTPExceptions
|
326
|
-
response = $!.response
|
327
|
-
warn "error creating repository: #{response.message} (HTTP #{response.code})"
|
328
|
-
exit 1
|
329
|
-
end
|
330
|
-
|
331
|
-
# $ hub push origin,staging cool-feature
|
332
|
-
# > git push origin cool-feature
|
333
|
-
# > git push staging cool-feature
|
334
|
-
def push(args)
|
335
|
-
return unless args[1] =~ /,/
|
336
|
-
|
337
|
-
branch = args[2]
|
338
|
-
remotes = args[1].split(',')
|
339
|
-
args[1] = remotes.shift
|
340
|
-
|
341
|
-
remotes.each do |name|
|
342
|
-
args.after ['push', name, branch]
|
343
|
-
end
|
344
|
-
end
|
345
|
-
|
346
|
-
# $ hub browse
|
347
|
-
# > open https://github.com/CURRENT_REPO
|
348
|
-
#
|
349
|
-
# $ hub browse -- issues
|
350
|
-
# > open https://github.com/CURRENT_REPO/issues
|
351
|
-
#
|
352
|
-
# $ hub browse pjhyett/github-services
|
353
|
-
# > open https://github.com/pjhyett/github-services
|
354
|
-
#
|
355
|
-
# $ hub browse github-services
|
356
|
-
# > open https://github.com/YOUR_LOGIN/github-services
|
357
|
-
#
|
358
|
-
# $ hub browse github-services wiki
|
359
|
-
# > open https://github.com/YOUR_LOGIN/github-services/wiki
|
360
|
-
def browse(args)
|
361
|
-
args.shift
|
362
|
-
browse_command(args) do
|
363
|
-
user = repo = nil
|
364
|
-
dest = args.shift
|
365
|
-
dest = nil if dest == '--'
|
366
|
-
|
367
|
-
if dest
|
368
|
-
# $ hub browse pjhyett/github-services
|
369
|
-
# $ hub browse github-services
|
370
|
-
repo = dest
|
371
|
-
elsif repo_user
|
372
|
-
# $ hub browse
|
373
|
-
user = repo_user
|
374
|
-
else
|
375
|
-
abort "Usage: hub browse [<USER>/]<REPOSITORY>"
|
376
|
-
end
|
377
|
-
|
378
|
-
params = { :user => user, :repo => repo }
|
379
|
-
|
380
|
-
# $ hub browse -- wiki
|
381
|
-
case subpage = args.shift
|
382
|
-
when 'commits'
|
383
|
-
branch = (!dest && tracked_branch) || 'master'
|
384
|
-
params[:web] = "/commits/#{branch}"
|
385
|
-
when 'tree', NilClass
|
386
|
-
branch = !dest && tracked_branch
|
387
|
-
params[:web] = "/tree/#{branch}" if branch && branch != 'master'
|
388
|
-
else
|
389
|
-
params[:web] = "/#{subpage}"
|
390
|
-
end
|
391
|
-
|
392
|
-
params
|
393
|
-
end
|
394
|
-
end
|
395
|
-
|
396
|
-
# $ hub compare 1.0...fix
|
397
|
-
# > open https://github.com/CURRENT_REPO/compare/1.0...fix
|
398
|
-
# $ hub compare refactor
|
399
|
-
# > open https://github.com/CURRENT_REPO/compare/refactor
|
400
|
-
# $ hub compare myfork feature
|
401
|
-
# > open https://github.com/myfork/REPO/compare/feature
|
402
|
-
# $ hub compare -u 1.0...2.0
|
403
|
-
# "https://github.com/CURRENT_REPO/compare/1.0...2.0"
|
404
|
-
def compare(args)
|
405
|
-
args.shift
|
406
|
-
browse_command(args) do
|
407
|
-
if args.empty?
|
408
|
-
branch = tracked_branch
|
409
|
-
if branch && branch != 'master'
|
410
|
-
range, user = branch, repo_user
|
411
|
-
else
|
412
|
-
abort "Usage: hub compare [USER] [<START>...]<END>"
|
413
|
-
end
|
414
|
-
else
|
415
|
-
range = args.pop
|
416
|
-
user = args.pop || repo_user
|
417
|
-
end
|
418
|
-
{ :user => user, :web => "/compare/#{range}" }
|
419
|
-
end
|
420
|
-
end
|
421
|
-
|
422
|
-
# $ hub hub standalone
|
423
|
-
# Prints the "standalone" version of hub for an easy, memorable
|
424
|
-
# installation sequence:
|
425
|
-
#
|
426
|
-
# $ gem install git-hub
|
427
|
-
# $ hub hub standalone > ~/bin/hub && chmod 755 ~/bin/hub
|
428
|
-
# $ gem uninstall git-hub
|
429
|
-
def hub(args)
|
430
|
-
return help(args) unless args[1] == 'standalone'
|
431
|
-
require 'hub/standalone'
|
432
|
-
puts Hub::Standalone.build
|
433
|
-
exit
|
434
|
-
rescue LoadError
|
435
|
-
abort "hub is running in standalone mode."
|
436
|
-
end
|
437
|
-
|
438
|
-
def alias(args)
|
439
|
-
shells = {
|
440
|
-
'sh' => 'alias git=hub',
|
441
|
-
'bash' => 'alias git=hub',
|
442
|
-
'zsh' => 'function git(){hub "$@"}',
|
443
|
-
'csh' => 'alias git hub',
|
444
|
-
'fish' => 'alias git hub'
|
445
|
-
}
|
446
|
-
|
447
|
-
silent = args.delete('-s')
|
448
|
-
|
449
|
-
if shell = args[1]
|
450
|
-
if silent.nil?
|
451
|
-
puts "Run this in your shell to start using `hub` as `git`:"
|
452
|
-
print " "
|
453
|
-
end
|
454
|
-
else
|
455
|
-
puts "usage: hub alias [-s] SHELL", ""
|
456
|
-
puts "You already have hub installed and available in your PATH,"
|
457
|
-
puts "but to get the full experience you'll want to alias it to"
|
458
|
-
puts "`git`.", ""
|
459
|
-
puts "To see how to accomplish this for your shell, run the alias"
|
460
|
-
puts "command again with the name of your shell.", ""
|
461
|
-
puts "Known shells:"
|
462
|
-
shells.map { |key, _| key }.sort.each do |key|
|
463
|
-
puts " " + key
|
464
|
-
end
|
465
|
-
puts "", "Options:"
|
466
|
-
puts " -s Silent. Useful when using the output with eval, e.g."
|
467
|
-
puts " $ eval `hub alias -s bash`"
|
468
|
-
|
469
|
-
exit
|
470
|
-
end
|
471
|
-
|
472
|
-
if shells[shell]
|
473
|
-
puts shells[shell]
|
474
|
-
else
|
475
|
-
abort "fatal: never heard of `#{shell}'"
|
476
|
-
end
|
477
|
-
|
478
|
-
exit
|
479
|
-
end
|
480
|
-
|
481
|
-
# $ hub version
|
482
|
-
# > git version
|
483
|
-
# (print hub version)
|
484
|
-
def version(args)
|
485
|
-
args.after do
|
486
|
-
puts "hub version %s" % Version
|
487
|
-
end
|
488
|
-
end
|
489
|
-
alias_method "--version", :version
|
490
|
-
|
491
|
-
# $ hub help
|
492
|
-
# (print improved help text)
|
493
|
-
def help(args)
|
494
|
-
command = args.grep(/^[^-]/)[1]
|
495
|
-
|
496
|
-
if command == 'hub'
|
497
|
-
puts hub_manpage
|
498
|
-
exit
|
499
|
-
elsif command.nil? && args.grep(/^--?a/).empty?
|
500
|
-
ENV['GIT_PAGER'] = '' if args.grep(/^-{1,2}p/).empty? # Use `cat`.
|
501
|
-
puts improved_help_text
|
502
|
-
exit
|
503
|
-
end
|
504
|
-
end
|
505
|
-
alias_method "--help", :help
|
506
|
-
|
507
|
-
# The text print when `hub help` is run, kept in its own method
|
508
|
-
# for the convenience of the author.
|
509
|
-
def improved_help_text
|
510
|
-
<<-help
|
511
|
-
usage: git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]
|
512
|
-
[-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR]
|
513
|
-
[--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]
|
514
|
-
|
515
|
-
Basic Commands:
|
516
|
-
init Create an empty git repository or reinitialize an existing one
|
517
|
-
add Add new or modified files to the staging area
|
518
|
-
rm Remove files from the working directory and staging area
|
519
|
-
mv Move or rename a file, a directory, or a symlink
|
520
|
-
status Show the status of the working directory and staging area
|
521
|
-
commit Record changes to the repository
|
522
|
-
|
523
|
-
History Commands:
|
524
|
-
log Show the commit history log
|
525
|
-
diff Show changes between commits, commit and working tree, etc
|
526
|
-
show Show information about commits, tags or files
|
527
|
-
|
528
|
-
Branching Commands:
|
529
|
-
branch List, create, or delete branches
|
530
|
-
checkout Switch the active branch to another branch
|
531
|
-
merge Join two or more development histories (branches) together
|
532
|
-
tag Create, list, delete, sign or verify a tag object
|
533
|
-
|
534
|
-
Remote Commands:
|
535
|
-
clone Clone a remote repository into a new directory
|
536
|
-
fetch Download data, tags and branches from a remote repository
|
537
|
-
pull Fetch from and merge with another repository or a local branch
|
538
|
-
push Upload data, tags and branches to a remote repository
|
539
|
-
remote View and manage a set of remote repositories
|
540
|
-
|
541
|
-
Advanced commands:
|
542
|
-
reset Reset your staging area or working directory to another point
|
543
|
-
rebase Re-apply a series of patches in one branch onto another
|
544
|
-
bisect Find by binary search the change that introduced a bug
|
545
|
-
grep Print files with lines matching a pattern in your codebase
|
546
|
-
|
547
|
-
See 'git help COMMAND' for more information on a specific command.
|
548
|
-
help
|
549
|
-
end
|
550
|
-
|
551
|
-
private
|
552
|
-
#
|
553
|
-
# Helper methods are private so they cannot be invoked
|
554
|
-
# from the command line.
|
555
|
-
#
|
556
|
-
|
557
|
-
# Checks whether a command exists on this system in the $PATH.
|
558
|
-
#
|
559
|
-
# name - The String name of the command to check for.
|
560
|
-
#
|
561
|
-
# Returns a Boolean.
|
562
|
-
def command?(name)
|
563
|
-
`which #{name} 2>/dev/null`
|
564
|
-
$?.success?
|
565
|
-
end
|
566
|
-
|
567
|
-
# Detects commands to launch the user's browser, checking $BROWSER
|
568
|
-
# first then falling back to a few common launchers. Aborts with
|
569
|
-
# an error if it can't find anything appropriate.
|
570
|
-
#
|
571
|
-
# Returns a launch command.
|
572
|
-
def browser_launcher
|
573
|
-
if ENV['BROWSER']
|
574
|
-
ENV['BROWSER']
|
575
|
-
elsif RUBY_PLATFORM.include?('darwin')
|
576
|
-
"open"
|
577
|
-
elsif command?("xdg-open")
|
578
|
-
"xdg-open"
|
579
|
-
elsif command?("cygstart")
|
580
|
-
"cygstart"
|
581
|
-
else
|
582
|
-
abort "Please set $BROWSER to a web launcher to use this command."
|
583
|
-
end
|
584
|
-
end
|
585
|
-
|
586
|
-
# Handles common functionality of browser commands like `browse`
|
587
|
-
# and `compare`. Yields a block that returns params for `github_url`.
|
588
|
-
def browse_command(args)
|
589
|
-
url_only = args.delete('-u')
|
590
|
-
$stderr.puts "Warning: the `-p` flag has no effect anymore" if args.delete('-p')
|
591
|
-
params = yield
|
592
|
-
|
593
|
-
args.executable = url_only ? 'echo' : browser_launcher
|
594
|
-
args.push github_url({:web => true, :private => true}.update(params))
|
595
|
-
end
|
596
|
-
|
597
|
-
|
598
|
-
# Returns the terminal-formatted manpage, ready to be printed to
|
599
|
-
# the screen.
|
600
|
-
def hub_manpage
|
601
|
-
return "** Can't find groff(1)" unless command?('groff')
|
602
|
-
|
603
|
-
require 'open3'
|
604
|
-
out = nil
|
605
|
-
Open3.popen3(groff_command) do |stdin, stdout, _|
|
606
|
-
stdin.puts hub_raw_manpage
|
607
|
-
stdin.close
|
608
|
-
out = stdout.read.strip
|
609
|
-
end
|
610
|
-
out
|
611
|
-
end
|
612
|
-
|
613
|
-
# The groff command complete with crazy arguments we need to run
|
614
|
-
# in order to turn our raw roff (manpage markup) into something
|
615
|
-
# readable on the terminal.
|
616
|
-
def groff_command
|
617
|
-
"groff -Wall -mtty-char -mandoc -Tascii"
|
618
|
-
end
|
619
|
-
|
620
|
-
# Returns the raw hub manpage. If we're not running in standalone
|
621
|
-
# mode, it's a file sitting at the root under the `man`
|
622
|
-
# directory.
|
623
|
-
#
|
624
|
-
# If we are running in standalone mode the manpage will be
|
625
|
-
# included after the __END__ of the file so we can grab it using
|
626
|
-
# DATA.
|
627
|
-
def hub_raw_manpage
|
628
|
-
if File.exists? file = File.dirname(__FILE__) + '/../../man/hub.1'
|
629
|
-
File.read(file)
|
630
|
-
else
|
631
|
-
DATA.read
|
632
|
-
end
|
633
|
-
end
|
634
|
-
|
635
|
-
# All calls to `puts` in after hooks or commands are paged,
|
636
|
-
# git-style.
|
637
|
-
def puts(*args)
|
638
|
-
page_stdout
|
639
|
-
super
|
640
|
-
end
|
641
|
-
|
642
|
-
# http://nex-3.com/posts/73-git-style-automatic-paging-in-ruby
|
643
|
-
def page_stdout
|
644
|
-
return unless $stdout.tty?
|
645
|
-
|
646
|
-
read, write = IO.pipe
|
647
|
-
|
648
|
-
if Kernel.fork
|
649
|
-
# Parent process, become pager
|
650
|
-
$stdin.reopen(read)
|
651
|
-
read.close
|
652
|
-
write.close
|
653
|
-
|
654
|
-
# Don't page if the input is short enough
|
655
|
-
ENV['LESS'] = 'FSRX'
|
656
|
-
|
657
|
-
# Wait until we have input before we start the pager
|
658
|
-
Kernel.select [STDIN]
|
659
|
-
|
660
|
-
pager = ENV['GIT_PAGER'] ||
|
661
|
-
`git config --get-all core.pager`.split.first || ENV['PAGER'] ||
|
662
|
-
'less -isr'
|
663
|
-
|
664
|
-
pager = 'cat' if pager.empty?
|
665
|
-
|
666
|
-
exec pager rescue exec "/bin/sh", "-c", pager
|
667
|
-
else
|
668
|
-
# Child process
|
669
|
-
$stdout.reopen(write)
|
670
|
-
$stderr.reopen(write) if $stderr.tty?
|
671
|
-
read.close
|
672
|
-
write.close
|
673
|
-
end
|
674
|
-
end
|
675
|
-
|
676
|
-
# Determines whether a user has a fork of the current repo on GitHub.
|
677
|
-
def repo_exists?(user)
|
678
|
-
require 'net/http'
|
679
|
-
url = API_REPO % [user, repo_name]
|
680
|
-
Net::HTTPSuccess === Net::HTTP.get_response(URI(url))
|
681
|
-
end
|
682
|
-
|
683
|
-
# Forks the current repo using the GitHub API.
|
684
|
-
#
|
685
|
-
# Returns nothing.
|
686
|
-
def fork_repo
|
687
|
-
url = API_FORK % [repo_owner, repo_name]
|
688
|
-
response = Net::HTTP.post_form(URI(url), 'login' => github_user, 'token' => github_token)
|
689
|
-
response.error! unless Net::HTTPSuccess === response
|
690
|
-
end
|
691
|
-
|
692
|
-
# Creates a new repo using the GitHub API.
|
693
|
-
#
|
694
|
-
# Returns nothing.
|
695
|
-
def create_repo(options = {})
|
696
|
-
url = API_CREATE
|
697
|
-
params = {'login' => github_user, 'token' => github_token, 'name' => repo_name}
|
698
|
-
params['public'] = '0' if options[:private]
|
699
|
-
params['description'] = options[:description] if options[:description]
|
700
|
-
params['homepage'] = options[:homepage] if options[:homepage]
|
701
|
-
|
702
|
-
response = Net::HTTP.post_form(URI(url), params)
|
703
|
-
response.error! unless Net::HTTPSuccess === response
|
704
|
-
end
|
705
|
-
|
706
|
-
def expand_alias(cmd)
|
707
|
-
if expanded = git_alias_for(cmd)
|
708
|
-
if expanded.index('!') != 0
|
709
|
-
require 'shellwords' unless expanded.respond_to? :shellsplit
|
710
|
-
expanded.shellsplit
|
711
|
-
end
|
712
|
-
end
|
713
|
-
end
|
714
|
-
|
715
|
-
end
|
716
|
-
end
|