git-hub 1.4.1 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +20 -20
- data/lib/hub/args.rb +48 -26
- data/lib/hub/commands.rb +43 -50
- data/lib/hub/context.rb +14 -9
- data/lib/hub/runner.rb +28 -27
- data/lib/hub/version.rb +1 -1
- data/man/hub.1 +103 -108
- data/man/hub.1.html +116 -161
- data/man/hub.1.ronn +49 -31
- data/test/fakebin/git +5 -1
- data/test/helper.rb +11 -0
- data/test/hub_test.rb +188 -140
- metadata +10 -25
data/README.md
CHANGED
@@ -30,7 +30,7 @@ Install
|
|
30
30
|
|
31
31
|
`hub` is most easily installed as a standalone script:
|
32
32
|
|
33
|
-
curl
|
33
|
+
curl http://chriswanstrath.com/hub/standalone -sLo ~/bin/hub &&
|
34
34
|
chmod 755 ~/bin/hub
|
35
35
|
|
36
36
|
Assuming `~/bin/` is in your `$PATH`, you're ready to roll:
|
@@ -191,39 +191,33 @@ Creates a new public github repository and adds the remote `origin` at
|
|
191
191
|
### git browse
|
192
192
|
|
193
193
|
$ git browse
|
194
|
-
> open
|
194
|
+
> open https://github.com/CURRENT_REPO
|
195
195
|
|
196
196
|
$ git browse -- issues
|
197
|
-
> open
|
197
|
+
> open https://github.com/CURRENT_REPO/issues
|
198
198
|
|
199
199
|
$ git browse schacon/ticgit
|
200
|
-
> open http://github.com/schacon/ticgit
|
201
|
-
|
202
|
-
$ git browse -p schacon/ticgit
|
203
200
|
> open https://github.com/schacon/ticgit
|
204
201
|
|
205
202
|
$ git browse resque
|
206
|
-
> open
|
203
|
+
> open https://github.com/YOUR_USER/resque
|
207
204
|
|
208
205
|
$ git browse resque network
|
209
|
-
> open
|
210
|
-
|
211
|
-
$ git browse -p resque
|
212
|
-
> open https://github.com:YOUR_USER/resque
|
206
|
+
> open https://github.com/YOUR_USER/resque/network
|
213
207
|
|
214
208
|
### git compare
|
215
209
|
|
216
210
|
$ git compare refactor
|
217
|
-
> open
|
211
|
+
> open https://github.com/CURRENT_REPO/compare/refactor
|
218
212
|
|
219
213
|
$ git compare 1.0...1.1
|
220
|
-
> open
|
214
|
+
> open https://github.com/CURRENT_REPO/compare/1.0...1.1
|
221
215
|
|
222
216
|
$ git compare -u fix
|
223
|
-
> (
|
217
|
+
> (https://github.com/CURRENT_REPO/compare/fix)
|
224
218
|
|
225
219
|
$ git compare other-user patch
|
226
|
-
> open
|
220
|
+
> open https://github.com/other-user/REPO/compare/patch
|
227
221
|
|
228
222
|
### git submodule
|
229
223
|
|
@@ -249,7 +243,7 @@ GitHub Login
|
|
249
243
|
------------
|
250
244
|
|
251
245
|
To get the most out of `hub`, you'll want to ensure your GitHub login
|
252
|
-
is stored locally in your Git config.
|
246
|
+
is stored locally in your Git config or environment variables.
|
253
247
|
|
254
248
|
To test it run this:
|
255
249
|
|
@@ -262,6 +256,12 @@ If you see nothing, you need to set the config setting:
|
|
262
256
|
For commands that require write access to GitHub (such as `fork`), you'll want to
|
263
257
|
setup "github.token" as well. See [local GitHub config guide][2] for more information.
|
264
258
|
|
259
|
+
Want to use environment variables instead of a local gitconfig?
|
260
|
+
|
261
|
+
* `GITHUB_USER` - If set, this will be used instead of the `github.user` config
|
262
|
+
value to determine your GitHub username.
|
263
|
+
* `GITHUB_TOKEN` - If set, this will be used instead of the `github.token` config
|
264
|
+
value to determine your GitHub API token.
|
265
265
|
|
266
266
|
Configuration
|
267
267
|
-------------
|
@@ -315,7 +315,7 @@ contribute to `hub`:
|
|
315
315
|
* `turn` (`gem install turn`)
|
316
316
|
* `mg` (`gem install mg`)
|
317
317
|
* `ronn` (`gem install ronn`)
|
318
|
-
* `
|
318
|
+
* `webmock` (`gem install webmock`)
|
319
319
|
|
320
320
|
Meta
|
321
321
|
----
|
@@ -328,10 +328,10 @@ Meta
|
|
328
328
|
* Gems: <http://gemcutter.org/gems/git-hub>
|
329
329
|
|
330
330
|
|
331
|
-
|
332
|
-
|
331
|
+
Authors
|
332
|
+
-------
|
333
333
|
|
334
|
-
|
334
|
+
<https://github.com/defunkt/hub/contributors>
|
335
335
|
|
336
336
|
[0]: http://help.github.com/forking/
|
337
337
|
[1]: http://github.com/defunkt/hub/issues
|
data/lib/hub/args.rb
CHANGED
@@ -12,27 +12,33 @@ module Hub
|
|
12
12
|
super
|
13
13
|
@executable = ENV["GIT"] || "git"
|
14
14
|
@after = nil
|
15
|
+
@skip = false
|
16
|
+
@original_args = args.first
|
17
|
+
@chain = [nil]
|
15
18
|
end
|
16
19
|
|
17
|
-
#
|
18
|
-
#
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
#
|
24
|
-
#
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
#
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
36
42
|
end
|
37
43
|
|
38
44
|
# Skip running this command.
|
@@ -45,15 +51,10 @@ module Hub
|
|
45
51
|
@skip
|
46
52
|
end
|
47
53
|
|
48
|
-
# Boolean indicating whether an `after` callback has been set.
|
49
|
-
def after?
|
50
|
-
!!@after
|
51
|
-
end
|
52
|
-
|
53
54
|
# Array of `executable` followed by all args suitable as arguments
|
54
55
|
# for `exec` or `system` calls.
|
55
|
-
def to_exec
|
56
|
-
[executable].concat
|
56
|
+
def to_exec(args = self)
|
57
|
+
[executable].concat args
|
57
58
|
end
|
58
59
|
|
59
60
|
# All the words (as opposed to flags) contained in this argument
|
@@ -73,5 +74,26 @@ module Hub
|
|
73
74
|
def flags
|
74
75
|
self - words
|
75
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
|
76
98
|
end
|
77
99
|
end
|
data/lib/hub/commands.rb
CHANGED
@@ -149,16 +149,16 @@ module Hub
|
|
149
149
|
if args.include?('--multiple')
|
150
150
|
names = args.words[1..-1]
|
151
151
|
# $ hub fetch <name>
|
152
|
-
elsif
|
152
|
+
elsif remote_name = args.words[1]
|
153
153
|
# $ hub fetch <name1>,<name2>,...
|
154
|
-
if
|
155
|
-
index = args.index(
|
156
|
-
args.delete(
|
157
|
-
names =
|
154
|
+
if remote_name =~ /^\w+(,\w+)+$/
|
155
|
+
index = args.index(remote_name)
|
156
|
+
args.delete(remote_name)
|
157
|
+
names = remote_name.split(',')
|
158
158
|
args.insert(index, *names)
|
159
159
|
args.insert(index, '--multiple')
|
160
160
|
else
|
161
|
-
names = [
|
161
|
+
names = [remote_name]
|
162
162
|
end
|
163
163
|
else
|
164
164
|
names = []
|
@@ -170,11 +170,9 @@ module Hub
|
|
170
170
|
}
|
171
171
|
|
172
172
|
if names.any?
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
args.shift # don't want "git"
|
177
|
-
args.after commands.join('; ')
|
173
|
+
names.each do |name|
|
174
|
+
args.before ['remote', 'add', name, github_url(:user => name)]
|
175
|
+
end
|
178
176
|
end
|
179
177
|
end
|
180
178
|
|
@@ -192,40 +190,49 @@ module Hub
|
|
192
190
|
def cherry_pick(args)
|
193
191
|
unless args.include?('-m') or args.include?('--mainline')
|
194
192
|
case ref = args.words.last
|
195
|
-
when %r{^(https?:)//github.com/(.+?)/(.+?)/commit/([a-f0-9]{7,40})}
|
196
|
-
|
193
|
+
when %r{^(?:https?:)//github.com/(.+?)/(.+?)/commit/([a-f0-9]{7,40})}
|
194
|
+
user, repo, sha = $1, $2, $3
|
197
195
|
args[args.index(ref)] = sha
|
198
196
|
when /^(\w+)@([a-f1-9]{7,40})$/
|
199
|
-
|
197
|
+
user, repo, sha = $1, nil, $2
|
200
198
|
args[args.index(ref)] = sha
|
201
199
|
else
|
202
200
|
user = nil
|
203
201
|
end
|
204
202
|
|
205
203
|
if user
|
206
|
-
# cherry-pick comes after the fetch
|
207
|
-
args.after args.to_exec.join(' ')
|
208
|
-
|
209
204
|
if user == repo_owner
|
210
205
|
# fetch from origin if the repo belongs to the user
|
211
|
-
args.
|
206
|
+
args.before ['fetch', default_remote]
|
212
207
|
elsif remotes.include?(user)
|
213
|
-
args.
|
208
|
+
args.before ['fetch', user]
|
214
209
|
else
|
215
|
-
|
216
|
-
|
217
|
-
args.replace ['remote', 'add', '-f', user, remote_url]
|
210
|
+
remote_url = github_url(:user => user, :repo => repo, :private => false)
|
211
|
+
args.before ['remote', 'add', '-f', user, remote_url]
|
218
212
|
end
|
219
213
|
end
|
220
214
|
end
|
221
215
|
end
|
222
216
|
|
217
|
+
# $ hub am https://github.com/defunkt/hub/pull/55
|
218
|
+
# > curl https://github.com/defunkt/hub/pull/55.patch -o /tmp/55.patch
|
219
|
+
# > git am /tmp/55.patch
|
220
|
+
def am(args)
|
221
|
+
if url = args.find { |a| a =~ %r{^https?://github\.com/} }
|
222
|
+
idx = args.index(url)
|
223
|
+
url += '.patch' unless File.extname(url) == '.patch'
|
224
|
+
patch_file = File.join(ENV['TMPDIR'], File.basename(url))
|
225
|
+
args.before 'curl', ['-#LA', "hub #{Hub::Version}", url, '-o', patch_file]
|
226
|
+
args[idx] = patch_file
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
223
230
|
# $ hub init -g
|
224
231
|
# > git init
|
225
232
|
# > git remote add origin git@github.com:USER/REPO.git
|
226
233
|
def init(args)
|
227
234
|
if args.delete('-g')
|
228
|
-
url = github_url(:private => true, :repo =>
|
235
|
+
url = github_url(:private => true, :repo => current_dirname)
|
229
236
|
args.after "git remote add origin #{url}"
|
230
237
|
end
|
231
238
|
end
|
@@ -306,35 +313,25 @@ module Hub
|
|
306
313
|
remotes = args[1].split(',')
|
307
314
|
args[1] = remotes.shift
|
308
315
|
|
309
|
-
|
310
|
-
|
311
|
-
while remotes.length > 0
|
312
|
-
after += "; git push #{remotes.shift} #{branch}"
|
316
|
+
remotes.each do |name|
|
317
|
+
args.after ['push', name, branch]
|
313
318
|
end
|
314
|
-
|
315
|
-
args.after after
|
316
319
|
end
|
317
320
|
|
318
321
|
# $ hub browse
|
319
|
-
# > open
|
322
|
+
# > open https://github.com/CURRENT_REPO
|
320
323
|
#
|
321
324
|
# $ hub browse -- issues
|
322
|
-
# > open
|
325
|
+
# > open https://github.com/CURRENT_REPO/issues
|
323
326
|
#
|
324
327
|
# $ hub browse pjhyett/github-services
|
325
|
-
# > open
|
326
|
-
#
|
327
|
-
# $ hub browse -p pjhyett/github-fi
|
328
|
-
# > open https://github.com/pjhyett/github-fi
|
328
|
+
# > open https://github.com/pjhyett/github-services
|
329
329
|
#
|
330
330
|
# $ hub browse github-services
|
331
|
-
# > open
|
331
|
+
# > open https://github.com/YOUR_LOGIN/github-services
|
332
332
|
#
|
333
333
|
# $ hub browse github-services wiki
|
334
|
-
# > open
|
335
|
-
#
|
336
|
-
# $ hub browse -p github-fi
|
337
|
-
# > open https://github.com/YOUR_LOGIN/github-fi
|
334
|
+
# > open https://github.com/YOUR_LOGIN/github-services/wiki
|
338
335
|
def browse(args)
|
339
336
|
args.shift
|
340
337
|
browse_command(args) do
|
@@ -357,8 +354,6 @@ module Hub
|
|
357
354
|
|
358
355
|
# $ hub browse -- wiki
|
359
356
|
case subpage = args.shift
|
360
|
-
when 'wiki'
|
361
|
-
params[:web] = 'wiki'
|
362
357
|
when 'commits'
|
363
358
|
branch = (!dest && tracked_branch) || 'master'
|
364
359
|
params[:web] = "/commits/#{branch}"
|
@@ -374,15 +369,13 @@ module Hub
|
|
374
369
|
end
|
375
370
|
|
376
371
|
# $ hub compare 1.0...fix
|
377
|
-
# > open
|
372
|
+
# > open https://github.com/CURRENT_REPO/compare/1.0...fix
|
378
373
|
# $ hub compare refactor
|
379
|
-
# > open
|
374
|
+
# > open https://github.com/CURRENT_REPO/compare/refactor
|
380
375
|
# $ hub compare myfork feature
|
381
|
-
# > open
|
382
|
-
# $ hub compare -p myfork topsecret
|
383
|
-
# > open https://github.com/myfork/REPO/compare/topsecret
|
376
|
+
# > open https://github.com/myfork/REPO/compare/feature
|
384
377
|
# $ hub compare -u 1.0...2.0
|
385
|
-
#
|
378
|
+
# "https://github.com/CURRENT_REPO/compare/1.0...2.0"
|
386
379
|
def compare(args)
|
387
380
|
args.shift
|
388
381
|
browse_command(args) do
|
@@ -569,11 +562,11 @@ help
|
|
569
562
|
# and `compare`. Yields a block that returns params for `github_url`.
|
570
563
|
def browse_command(args)
|
571
564
|
url_only = args.delete('-u')
|
572
|
-
|
565
|
+
$stderr.puts "Warning: the `-p` flag has no effect anymore" if args.delete('-p')
|
573
566
|
params = yield
|
574
567
|
|
575
568
|
args.executable = url_only ? 'echo' : browser_launcher
|
576
|
-
args.push github_url({:web => true, :private =>
|
569
|
+
args.push github_url({:web => true, :private => true}.update(params))
|
577
570
|
end
|
578
571
|
|
579
572
|
|
data/lib/hub/context.rb
CHANGED
@@ -2,6 +2,8 @@ module Hub
|
|
2
2
|
# Provides methods for inspecting the environment, such as GitHub user/token
|
3
3
|
# settings, repository info, and similar.
|
4
4
|
module Context
|
5
|
+
private
|
6
|
+
|
5
7
|
# Caches output when shelling out to git
|
6
8
|
GIT_CONFIG = Hash.new do |cache, cmd|
|
7
9
|
result = %x{git #{cmd}}.chomp
|
@@ -11,9 +13,9 @@ module Hub
|
|
11
13
|
# Parses URLs for git remotes and stores info
|
12
14
|
REMOTES = Hash.new do |cache, remote|
|
13
15
|
if remote
|
14
|
-
|
16
|
+
urls = GIT_CONFIG["config --get-all remote.#{remote}.url"].to_s.split("\n")
|
15
17
|
|
16
|
-
if
|
18
|
+
if urls.find { |u| u =~ %r{\bgithub\.com[:/](.+)/(.+).git$} }
|
17
19
|
cache[remote] = { :user => $1, :repo => $2 }
|
18
20
|
else
|
19
21
|
cache[remote] = { }
|
@@ -34,13 +36,13 @@ module Hub
|
|
34
36
|
end
|
35
37
|
|
36
38
|
def repo_name
|
37
|
-
REMOTES[default_remote][:repo] ||
|
39
|
+
REMOTES[default_remote][:repo] || current_dirname
|
38
40
|
end
|
39
41
|
|
40
42
|
# Either returns the GitHub user as set by git-config(1) or aborts
|
41
43
|
# with an error message.
|
42
44
|
def github_user(fatal = true)
|
43
|
-
if user = GIT_CONFIG['config github.user']
|
45
|
+
if user = ENV['GITHUB_USER'] || GIT_CONFIG['config github.user']
|
44
46
|
user
|
45
47
|
elsif fatal
|
46
48
|
abort("** No GitHub user set. See #{LGHCONF}")
|
@@ -48,7 +50,7 @@ module Hub
|
|
48
50
|
end
|
49
51
|
|
50
52
|
def github_token(fatal = true)
|
51
|
-
if token = GIT_CONFIG['config github.token']
|
53
|
+
if token = ENV['GITHUB_TOKEN'] || GIT_CONFIG['config github.token']
|
52
54
|
token
|
53
55
|
elsif fatal
|
54
56
|
abort("** No GitHub token set. See #{LGHCONF}")
|
@@ -119,10 +121,7 @@ module Hub
|
|
119
121
|
repo ||= repo_name
|
120
122
|
secure = options[:private]
|
121
123
|
|
122
|
-
if options[:web]
|
123
|
-
scheme = secure ? 'https:' : 'http:'
|
124
|
-
'%s//wiki.github.com/%s/%s/' % [scheme, user, repo]
|
125
|
-
elsif options[:web]
|
124
|
+
if options[:web]
|
126
125
|
scheme = secure ? 'https:' : 'http:'
|
127
126
|
path = options[:web] == true ? '' : options[:web].to_s
|
128
127
|
'%s//github.com/%s/%s%s' % [scheme, user, repo, path]
|
@@ -138,5 +137,11 @@ module Hub
|
|
138
137
|
url % [user, repo]
|
139
138
|
end
|
140
139
|
end
|
140
|
+
|
141
|
+
DIRNAME = File.basename(Dir.pwd)
|
142
|
+
|
143
|
+
def current_dirname
|
144
|
+
DIRNAME
|
145
|
+
end
|
141
146
|
end
|
142
147
|
end
|
data/lib/hub/runner.rb
CHANGED
@@ -11,11 +11,11 @@ module Hub
|
|
11
11
|
@args = Args.new(args)
|
12
12
|
|
13
13
|
# Hack to emulate git-style
|
14
|
-
@args.unshift 'help' if @args.grep(/^[^-]|version/).empty?
|
14
|
+
@args.unshift 'help' if @args.grep(/^[^-]|version|exec-path$|html-path/).empty?
|
15
15
|
|
16
16
|
# git commands can have dashes
|
17
17
|
cmd = @args[0].sub(/(\w)-/, '\1_')
|
18
|
-
Commands.send(cmd, @args) if Commands.
|
18
|
+
Commands.send(cmd, @args) if Commands.method_defined?(cmd)
|
19
19
|
end
|
20
20
|
|
21
21
|
# Shortcut
|
@@ -23,22 +23,23 @@ module Hub
|
|
23
23
|
new(*args).execute
|
24
24
|
end
|
25
25
|
|
26
|
-
#
|
27
|
-
# the target git command.
|
28
|
-
#
|
29
|
-
# See the `Hub::Args` class for more information on the `after`
|
30
|
-
# callback.
|
31
|
-
def after
|
32
|
-
args.after.to_s
|
33
|
-
end
|
34
|
-
|
35
|
-
# A string representation of the git command we would run if
|
36
|
-
# #execute were called.
|
26
|
+
# A string representation of the command that would run.
|
37
27
|
def command
|
38
28
|
if args.skip?
|
39
29
|
''
|
40
30
|
else
|
41
|
-
|
31
|
+
commands.join('; ')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# An array of all commands as strings.
|
36
|
+
def commands
|
37
|
+
args.commands.map do |cmd|
|
38
|
+
if cmd.respond_to?(:join)
|
39
|
+
cmd.map { |c| c.index(' ') ? "'#{c}'" : c }.join(' ')
|
40
|
+
else
|
41
|
+
cmd.to_s
|
42
|
+
end
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
@@ -50,25 +51,25 @@ module Hub
|
|
50
51
|
# execution if they don't make sense.
|
51
52
|
def execute
|
52
53
|
unless args.skip?
|
53
|
-
if args.
|
54
|
-
|
54
|
+
if args.chained?
|
55
|
+
execute_command_chain
|
55
56
|
else
|
56
57
|
exec(*args.to_exec)
|
57
58
|
end
|
58
59
|
end
|
59
60
|
end
|
60
61
|
|
61
|
-
# Runs
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
62
|
+
# Runs multiple commands in succession; exits at first failure.
|
63
|
+
def execute_command_chain
|
64
|
+
commands = args.commands
|
65
|
+
commands.each_with_index do |cmd, i|
|
66
|
+
if cmd.respond_to?(:call) then cmd.call
|
67
|
+
elsif i == commands.length - 1
|
68
|
+
# last command in chain
|
69
|
+
exec(*cmd)
|
70
|
+
else
|
71
|
+
exit($?.exitstatus) unless system(*cmd)
|
72
|
+
end
|
72
73
|
end
|
73
74
|
end
|
74
75
|
end
|