hub 1.10.6 → 1.11.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of hub might be problematic. Click here for more details.
- data/README.md +30 -62
- data/Rakefile +24 -15
- data/bin/bench +37 -0
- data/lib/hub.rb +1 -0
- data/lib/hub/commands.rb +178 -57
- data/lib/hub/context.rb +63 -20
- data/lib/hub/github_api.rb +193 -71
- data/lib/hub/speedy_stdlib.rb +107 -0
- data/lib/hub/ssh_config.rb +1 -1
- data/lib/hub/standalone.rb +31 -3
- data/lib/hub/version.rb +1 -1
- data/man/hub.1 +46 -23
- data/man/hub.1.html +46 -29
- data/man/hub.1.ronn +30 -15
- data/script/cached-bundle +46 -0
- data/script/s3-put +71 -0
- data/script/test +41 -0
- data/script/test_each +9 -0
- data/test/context_test.rb +79 -0
- data/test/fakebin/git +1 -1
- data/test/fakebin/open +2 -2
- data/test/github_api_test.rb +79 -0
- data/test/helper.rb +2 -2
- data/test/hub_test.rb +85 -197
- data/test/standalone_test.rb +6 -2
- metadata +22 -15
- data/HISTORY.md +0 -244
data/README.md
CHANGED
@@ -31,16 +31,21 @@ Installing on OS X is easiest with Homebrew:
|
|
31
31
|
$ brew install hub
|
32
32
|
~~~
|
33
33
|
|
34
|
-
###
|
34
|
+
### `rake install` from source
|
35
35
|
|
36
|
-
|
36
|
+
This is the preferred installation method when no package manager that
|
37
|
+
supports hub is available:
|
37
38
|
|
38
39
|
~~~ sh
|
39
|
-
|
40
|
-
|
40
|
+
# Download or clone the project from GitHub:
|
41
|
+
$ git clone git://github.com/github/hub.git
|
42
|
+
$ cd hub
|
43
|
+
$ rake install
|
41
44
|
~~~
|
42
45
|
|
43
|
-
|
46
|
+
On a Unix-based OS, this installs under `PREFIX`, which is `/usr/local` by default.
|
47
|
+
|
48
|
+
Now you should be ready to roll:
|
44
49
|
|
45
50
|
~~~ sh
|
46
51
|
$ hub version
|
@@ -48,12 +53,9 @@ git version 1.7.6
|
|
48
53
|
hub version 1.8.3
|
49
54
|
~~~
|
50
55
|
|
51
|
-
####
|
56
|
+
#### Windows "Git Bash" (msysGit) note
|
52
57
|
|
53
|
-
|
54
|
-
`hub` executable in `/bin` instead of `~/bin`.
|
55
|
-
|
56
|
-
Avoid aliasing hub as `git` due to the fact that mysysgit automatically
|
58
|
+
Avoid aliasing hub as `git` due to the fact that msysGit automatically
|
57
59
|
configures your prompt to include git information, and you want to avoid slowing
|
58
60
|
that down. See [Is your shell prompt slow?](#is-your-shell-prompt-slow)
|
59
61
|
|
@@ -78,16 +80,6 @@ $ hub hub standalone > ~/bin/hub && chmod +x ~/bin/hub
|
|
78
80
|
This installs a standalone version which doesn't require RubyGems to
|
79
81
|
run, so it's faster.
|
80
82
|
|
81
|
-
### Source
|
82
|
-
|
83
|
-
You can also install from source:
|
84
|
-
|
85
|
-
~~~ sh
|
86
|
-
$ git clone git://github.com/defunkt/hub.git
|
87
|
-
$ cd hub
|
88
|
-
$ rake install prefix=/usr/local
|
89
|
-
~~~
|
90
|
-
|
91
83
|
### Help! It's slow!
|
92
84
|
|
93
85
|
#### Is `hub` noticeably slower than plain git?
|
@@ -153,8 +145,8 @@ eval "$(hub alias -s)"
|
|
153
145
|
hub repository contains tab-completion scripts for bash and zsh. These scripts
|
154
146
|
complement existing completion scripts that ship with git.
|
155
147
|
|
156
|
-
* [hub bash completion](https://github.com/
|
157
|
-
* [hub zsh completion](https://github.com/
|
148
|
+
* [hub bash completion](https://github.com/github/hub/blob/master/etc/hub.bash_completion.sh)
|
149
|
+
* [hub zsh completion](https://github.com/github/hub/blob/master/etc/hub.zsh_completion)
|
158
150
|
|
159
151
|
|
160
152
|
Commands
|
@@ -213,15 +205,15 @@ superpowers:
|
|
213
205
|
### git am, git apply
|
214
206
|
|
215
207
|
$ git am https://github.com/defunkt/hub/pull/55
|
216
|
-
|
208
|
+
[ downloads patch via API ]
|
217
209
|
> git am /tmp/55.patch
|
218
210
|
|
219
211
|
$ git am --ignore-whitespace https://github.com/davidbalbert/hub/commit/fdb9921
|
220
|
-
|
212
|
+
[ downloads patch via API ]
|
221
213
|
> git am --ignore-whitespace /tmp/fdb9921.patch
|
222
214
|
|
223
215
|
$ git apply https://gist.github.com/8da7fb575debd88c54cf
|
224
|
-
|
216
|
+
[ downloads patch via API ]
|
225
217
|
> git apply /tmp/gist-8da7fb575debd88c54cf.txt
|
226
218
|
|
227
219
|
### git fork
|
@@ -238,10 +230,7 @@ superpowers:
|
|
238
230
|
[ opened pull request on GitHub for "YOUR_USER:feature" ]
|
239
231
|
|
240
232
|
# explicit title, pull base & head:
|
241
|
-
$ git pull-request "
|
242
|
-
|
243
|
-
$ git pull-request -i 123
|
244
|
-
[ attached pull request to issue #123 ]
|
233
|
+
$ git pull-request -m "Implemented feature X" -b defunkt:master -h mislav:feature
|
245
234
|
|
246
235
|
### git checkout
|
247
236
|
|
@@ -326,15 +315,21 @@ superpowers:
|
|
326
315
|
|
327
316
|
### git submodule
|
328
317
|
|
329
|
-
$
|
318
|
+
$ git submodule add wycats/bundler vendor/bundler
|
330
319
|
> git submodule add git://github.com/wycats/bundler.git vendor/bundler
|
331
320
|
|
332
|
-
$
|
321
|
+
$ git submodule add -p wycats/bundler vendor/bundler
|
333
322
|
> git submodule add git@github.com:wycats/bundler.git vendor/bundler
|
334
323
|
|
335
|
-
$
|
324
|
+
$ git submodule add -b ryppl --name pip ryppl/pip vendor/pip
|
336
325
|
> git submodule add -b ryppl --name pip git://github.com/ryppl/pip.git vendor/pip
|
337
326
|
|
327
|
+
### git ci-status
|
328
|
+
|
329
|
+
$ git ci-status [commit]
|
330
|
+
> (prints CI state of commit and exits with appropriate code)
|
331
|
+
> One of: success (0), error (1), failure (1), pending (2), no status (3)
|
332
|
+
|
338
333
|
|
339
334
|
### git help
|
340
335
|
|
@@ -369,40 +364,13 @@ $ git clone defunkt/repl
|
|
369
364
|
~~~
|
370
365
|
|
371
366
|
|
372
|
-
Contributing
|
373
|
-
------------
|
374
|
-
|
375
|
-
These instructions assume that _you already have hub installed_ and aliased as
|
376
|
-
`git` (see "Aliasing").
|
377
|
-
|
378
|
-
1. Clone hub:
|
379
|
-
`git clone defunkt/hub && cd hub`
|
380
|
-
1. Ensure Bundler is installed:
|
381
|
-
`which bundle || gem install bundler`
|
382
|
-
1. Install development dependencies:
|
383
|
-
`bundle install`
|
384
|
-
2. Verify that existing tests pass:
|
385
|
-
`bundle exec rake`
|
386
|
-
3. Create a topic branch:
|
387
|
-
`git checkout -b feature`
|
388
|
-
4. **Make your changes.** (It helps a lot if you write tests first.)
|
389
|
-
5. Verify that tests still pass:
|
390
|
-
`bundle exec rake`
|
391
|
-
6. Fork hub on GitHub (adds a remote named "YOUR_USER"):
|
392
|
-
`git fork`
|
393
|
-
7. Push to your fork:
|
394
|
-
`git push -u YOUR_USER feature`
|
395
|
-
8. Open a pull request describing your changes:
|
396
|
-
`git pull-request`
|
397
|
-
|
398
|
-
|
399
367
|
Meta
|
400
368
|
----
|
401
369
|
|
402
|
-
* Home: <https://github.com/
|
403
|
-
* Bugs: <https://github.com/
|
370
|
+
* Home: <https://github.com/github/hub>
|
371
|
+
* Bugs: <https://github.com/github/hub/issues>
|
404
372
|
* Gem: <https://rubygems.org/gems/hub>
|
405
|
-
* Authors: <https://github.com/
|
373
|
+
* Authors: <https://github.com/github/hub/contributors>
|
406
374
|
|
407
375
|
### Prior art
|
408
376
|
|
data/Rakefile
CHANGED
@@ -7,6 +7,7 @@ require 'rake/testtask'
|
|
7
7
|
def command?(util)
|
8
8
|
Rake::Task[:load_path].invoke
|
9
9
|
context = Object.new
|
10
|
+
require 'uri'
|
10
11
|
require 'hub/context'
|
11
12
|
context.extend Hub::Context
|
12
13
|
context.send(:command?, util)
|
@@ -31,9 +32,7 @@ task :default => [:test, :features]
|
|
31
32
|
|
32
33
|
Rake::TestTask.new do |t|
|
33
34
|
t.libs << 'test'
|
34
|
-
t.ruby_opts << '-rubygems'
|
35
35
|
t.pattern = 'test/**/*_test.rb'
|
36
|
-
t.verbose = false
|
37
36
|
end
|
38
37
|
|
39
38
|
task :features do
|
@@ -71,7 +70,7 @@ if command? :ronn
|
|
71
70
|
|
72
71
|
# generate man page with ronn
|
73
72
|
compile_ronn = lambda { |destination, type, contents|
|
74
|
-
File.popen("ronn --pipe --#{type} --organization=
|
73
|
+
File.popen("ronn --pipe --#{type} --organization=GITHUB --manual='Hub Manual'", 'w+') { |io|
|
75
74
|
io.write contents
|
76
75
|
io.close_write
|
77
76
|
File.open(destination, 'w') { |f| f << io.read }
|
@@ -97,6 +96,7 @@ end
|
|
97
96
|
|
98
97
|
file "hub" => FileList.new("lib/hub.rb", "lib/hub/*.rb", "man/hub.1") do |task|
|
99
98
|
Rake::Task[:load_path].invoke
|
99
|
+
require 'hub/version'
|
100
100
|
require 'hub/standalone'
|
101
101
|
Hub::Standalone.save(task.name)
|
102
102
|
end
|
@@ -104,15 +104,24 @@ end
|
|
104
104
|
desc "Build standalone script"
|
105
105
|
task :standalone => "hub"
|
106
106
|
|
107
|
-
desc
|
107
|
+
desc %{Install standalone script and man page.
|
108
|
+
On Unix-based OS, installs into PREFIX (default: `/usr/local`).
|
109
|
+
On Windows, installs into Ruby's main bin directory.}
|
108
110
|
task :install => "hub" do
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
111
|
+
require 'rbconfig'
|
112
|
+
if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
|
113
|
+
bindir = RbConfig::CONFIG['bindir']
|
114
|
+
File.open(File.join(bindir, 'hub.bat'), 'w') { |f| f.write('@"ruby.exe" "%~dpn0" %*') }
|
115
|
+
FileUtils.cp 'hub', bindir
|
116
|
+
else
|
117
|
+
prefix = ENV['PREFIX'] || ENV['prefix'] || '/usr/local'
|
118
|
+
|
119
|
+
FileUtils.mkdir_p "#{prefix}/bin"
|
120
|
+
FileUtils.cp "hub", "#{prefix}/bin", :preserve => true
|
121
|
+
|
122
|
+
FileUtils.mkdir_p "#{prefix}/share/man/man1"
|
123
|
+
FileUtils.cp "man/hub.1", "#{prefix}/share/man/man1"
|
124
|
+
end
|
116
125
|
end
|
117
126
|
|
118
127
|
#
|
@@ -152,17 +161,17 @@ task :homebrew do
|
|
152
161
|
sh 'git pull -q origin master'
|
153
162
|
|
154
163
|
formula_file = 'Library/Formula/hub.rb'
|
155
|
-
sha = `curl
|
164
|
+
sha = `curl -fsSL https://github.com/github/hub/archive/v#{Hub::VERSION}.tar.gz | shasum`.split(/\s+/).first
|
156
165
|
abort unless $?.success? and sha.length == 40
|
157
166
|
|
158
167
|
formula = File.read formula_file
|
159
|
-
formula.sub!
|
160
|
-
formula.sub!
|
168
|
+
formula.sub!(/\bv\d+(\.\d+)*/, "v#{Hub::VERSION}")
|
169
|
+
formula.sub!(/\b[0-9a-f]{40}\b/, sha)
|
161
170
|
File.open(formula_file, 'w') {|f| f << formula }
|
162
171
|
|
163
172
|
branch = "hub-v#{Hub::VERSION}"
|
164
173
|
sh "git checkout -q -B #{branch}"
|
165
|
-
sh "git commit -m '
|
174
|
+
sh "git commit -m 'hub v#{Hub::VERSION}' -- #{formula_file}"
|
166
175
|
sh "git push -u mislav #{branch}"
|
167
176
|
sh "hub pull-request 'upgrade hub to v#{Hub::VERSION}'"
|
168
177
|
|
data/bin/bench
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
set -e
|
3
|
+
|
4
|
+
iterations=20
|
5
|
+
|
6
|
+
ruby="$(rbenv which ruby)"
|
7
|
+
ruby_args="--disable-gems"
|
8
|
+
|
9
|
+
unset RUBYLIB
|
10
|
+
unset RUBYOPT
|
11
|
+
|
12
|
+
export TIMEFORMAT='%3R'
|
13
|
+
|
14
|
+
time_ruby() {
|
15
|
+
(time "$ruby" $ruby_args "$@" >/dev/null) 2>&1
|
16
|
+
}
|
17
|
+
|
18
|
+
time_plain() {
|
19
|
+
(time "$@" >/dev/null) 2>&1
|
20
|
+
}
|
21
|
+
|
22
|
+
measure() {
|
23
|
+
local count="$iterations"
|
24
|
+
echo "$@"
|
25
|
+
{ while [ "$count" -gt 0 ]; do
|
26
|
+
# time_ruby "$@"
|
27
|
+
time_plain "$@"
|
28
|
+
count=$((count - 1))
|
29
|
+
done
|
30
|
+
} | stdev
|
31
|
+
}
|
32
|
+
|
33
|
+
/usr/bin/ruby -v
|
34
|
+
measure tmp/hub-slow version
|
35
|
+
measure tmp/hub-slow browse -u
|
36
|
+
measure tmp/hub-fast version
|
37
|
+
measure tmp/hub-fast browse -u
|
data/lib/hub.rb
CHANGED
data/lib/hub/commands.rb
CHANGED
@@ -38,7 +38,7 @@ module Hub
|
|
38
38
|
OWNER_RE = /[a-zA-Z0-9][a-zA-Z0-9-]*/
|
39
39
|
NAME_WITH_OWNER_RE = /^(?:#{NAME_RE}|#{OWNER_RE}\/#{NAME_RE})$/
|
40
40
|
|
41
|
-
CUSTOM_COMMANDS = %w[alias create browse compare fork pull-request]
|
41
|
+
CUSTOM_COMMANDS = %w[alias create browse compare fork pull-request ci-status]
|
42
42
|
|
43
43
|
def run(args)
|
44
44
|
slurp_global_flags(args)
|
@@ -70,16 +70,58 @@ module Hub
|
|
70
70
|
abort "fatal: #{err.message}"
|
71
71
|
end
|
72
72
|
|
73
|
+
|
74
|
+
# $ hub ci-status
|
75
|
+
# $ hub ci-status 6f6d9797f9d6e56c3da623a97cfc3f45daf9ae5f
|
76
|
+
# $ hub ci-status master
|
77
|
+
# $ hub ci-status origin/master
|
78
|
+
def ci_status(args)
|
79
|
+
args.shift
|
80
|
+
ref = args.words.first || 'HEAD'
|
81
|
+
verbose = args.include?('-v')
|
82
|
+
|
83
|
+
unless project = local_repo.main_project
|
84
|
+
abort "Aborted: the origin remote doesn't point to a GitHub repository."
|
85
|
+
end
|
86
|
+
|
87
|
+
unless sha = local_repo.git_command("rev-parse -q #{ref}")
|
88
|
+
abort "Aborted: no revision could be determined from '#{ref}'"
|
89
|
+
end
|
90
|
+
|
91
|
+
statuses = api_client.statuses(project, sha)
|
92
|
+
status = statuses.first
|
93
|
+
if status
|
94
|
+
ref_state = status['state']
|
95
|
+
ref_target_url = status['target_url']
|
96
|
+
else
|
97
|
+
ref_state = 'no status'
|
98
|
+
ref_target_url = nil
|
99
|
+
end
|
100
|
+
|
101
|
+
exit_code = case ref_state
|
102
|
+
when 'success' then 0
|
103
|
+
when 'failure', 'error' then 1
|
104
|
+
when 'pending' then 2
|
105
|
+
else 3
|
106
|
+
end
|
107
|
+
|
108
|
+
if verbose and ref_target_url
|
109
|
+
$stdout.puts "%s: %s" % [ref_state, ref_target_url]
|
110
|
+
else
|
111
|
+
$stdout.puts ref_state
|
112
|
+
end
|
113
|
+
exit exit_code
|
114
|
+
end
|
115
|
+
|
73
116
|
# $ hub pull-request
|
74
117
|
# $ hub pull-request "My humble contribution"
|
75
|
-
# $ hub pull-request -i 92
|
76
118
|
# $ hub pull-request https://github.com/rtomayko/tilt/issues/92
|
77
119
|
def pull_request(args)
|
78
120
|
args.shift
|
79
121
|
options = { }
|
80
122
|
force = explicit_owner = false
|
81
123
|
base_project = local_repo.main_project
|
82
|
-
head_project =
|
124
|
+
tracked_branch, head_project = remote_branch_and_project(method(:github_user))
|
83
125
|
|
84
126
|
unless current_branch
|
85
127
|
abort "Aborted: not currently on any branch."
|
@@ -101,6 +143,13 @@ module Hub
|
|
101
143
|
case arg
|
102
144
|
when '-f'
|
103
145
|
force = true
|
146
|
+
when '-F', '--file'
|
147
|
+
file = args.shift
|
148
|
+
text = file == '-' ? $stdin.read : File.read(file)
|
149
|
+
options[:title], options[:body] = read_msg(text)
|
150
|
+
when '-m', '--message'
|
151
|
+
text = args.shift
|
152
|
+
options[:title], options[:body] = read_msg(text)
|
104
153
|
when '-b'
|
105
154
|
base_project, options[:base] = from_github_ref.call(args.shift, base_project)
|
106
155
|
when '-h'
|
@@ -113,17 +162,24 @@ module Hub
|
|
113
162
|
if url = resolve_github_url(arg) and url.project_path =~ /^issues\/(\d+)/
|
114
163
|
options[:issue] = $1
|
115
164
|
base_project = url.project
|
116
|
-
elsif !options[:title]
|
165
|
+
elsif !options[:title]
|
166
|
+
options[:title] = arg
|
167
|
+
warn "hub: Specifying pull request title without a flag is deprecated."
|
168
|
+
warn "Please use one of `-m' or `-F' options."
|
117
169
|
else
|
118
170
|
abort "invalid argument: #{arg}"
|
119
171
|
end
|
120
172
|
end
|
121
173
|
end
|
122
174
|
|
175
|
+
if options[:issue]
|
176
|
+
warn "Warning: Issue to pull request conversion is deprecated and might not work in the future."
|
177
|
+
end
|
178
|
+
|
123
179
|
options[:project] = base_project
|
124
180
|
options[:base] ||= master_branch.short_name
|
125
181
|
|
126
|
-
if
|
182
|
+
if options[:head].nil? && tracked_branch
|
127
183
|
if !tracked_branch.remote?
|
128
184
|
# The current branch is tracking another local branch. Pretend there is
|
129
185
|
# no upstream configuration at all.
|
@@ -136,12 +192,6 @@ module Hub
|
|
136
192
|
end
|
137
193
|
options[:head] ||= (tracked_branch || current_branch).short_name
|
138
194
|
|
139
|
-
# when no tracking, assume remote branch is published under active user's fork
|
140
|
-
user = github_user(head_project.host)
|
141
|
-
if head_project.owner != user and !tracked_branch and !explicit_owner
|
142
|
-
head_project = head_project.owned_by(user)
|
143
|
-
end
|
144
|
-
|
145
195
|
remote_branch = "#{head_project.remote}/#{options[:head]}"
|
146
196
|
options[:head] = "#{head_project.owner}:#{options[:head]}"
|
147
197
|
|
@@ -174,8 +224,9 @@ module Hub
|
|
174
224
|
[format, base_branch, remote_branch]
|
175
225
|
end
|
176
226
|
|
177
|
-
options[:title], options[:body] = pullrequest_editmsg(commit_summary) { |msg|
|
178
|
-
|
227
|
+
options[:title], options[:body] = pullrequest_editmsg(commit_summary) { |msg, initial_message|
|
228
|
+
initial_message ||= default_message
|
229
|
+
msg.puts initial_message if initial_message
|
179
230
|
msg.puts ""
|
180
231
|
msg.puts "# Requesting a pull to #{base_project.owner}:#{options[:base]} from #{options[:head]}"
|
181
232
|
msg.puts "#"
|
@@ -196,8 +247,16 @@ module Hub
|
|
196
247
|
warn "Are you sure that #{base_url} exists?"
|
197
248
|
end
|
198
249
|
exit 1
|
250
|
+
else
|
251
|
+
delete_editmsg
|
199
252
|
end
|
200
253
|
|
254
|
+
# $ hub e-note
|
255
|
+
# $ hub e-note "My humble contribution"
|
256
|
+
# $ hub e-note -i 92
|
257
|
+
# $ hub e-note https://github.com/rtomayko/tilt/issues/92
|
258
|
+
alias_method :e_note, :pull_request
|
259
|
+
|
201
260
|
# $ hub clone rtomayko/tilt
|
202
261
|
# > git clone git://github.com/rtomayko/tilt.
|
203
262
|
#
|
@@ -225,7 +284,10 @@ module Hub
|
|
225
284
|
name, owner = arg, nil
|
226
285
|
owner, name = name.split('/', 2) if name.index('/')
|
227
286
|
project = github_project(name, owner || github_user)
|
228
|
-
ssh
|
287
|
+
unless ssh || args[0] == 'submodule' || args.noop? || https_protocol?
|
288
|
+
repo_info = api_client.repo_info(project)
|
289
|
+
ssh = repo_info.success? && (repo_info.data['private'] || repo_info.data['permissions']['push'])
|
290
|
+
end
|
229
291
|
args[idx] = project.git_url(:private => ssh, :https => https_protocol?)
|
230
292
|
end
|
231
293
|
break
|
@@ -381,8 +443,9 @@ module Hub
|
|
381
443
|
|
382
444
|
idx = args.index url_arg
|
383
445
|
args.delete_at idx
|
384
|
-
args.insert idx, merge_head, '
|
385
|
-
|
446
|
+
args.insert idx, merge_head, '-m', "Merge pull request ##{pull_id} from #{merge_head}\n\n#{pull_data['title']}"
|
447
|
+
idx = args.index '-m'
|
448
|
+
args.insert idx, '--no-ff' unless args.include?('--ff-only')
|
386
449
|
end
|
387
450
|
end
|
388
451
|
|
@@ -421,27 +484,40 @@ module Hub
|
|
421
484
|
end
|
422
485
|
|
423
486
|
# $ hub am https://github.com/defunkt/hub/pull/55
|
424
|
-
#
|
487
|
+
# ... downloads patch via API ...
|
425
488
|
# > git am /tmp/55.patch
|
426
489
|
def am(args)
|
427
490
|
if url = args.find { |a| a =~ %r{^https?://(gist\.)?github\.com/} }
|
428
491
|
idx = args.index(url)
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
492
|
+
if $1 == 'gist.'
|
493
|
+
path_parts = $'.sub(/#.*/, '').split('/')
|
494
|
+
gist_id = path_parts.last
|
495
|
+
patch_name = "gist-#{gist_id}.txt"
|
496
|
+
patch = api_client.gist_raw(gist_id)
|
497
|
+
else
|
498
|
+
gh_url = resolve_github_url(url)
|
499
|
+
case gh_url.project_path
|
500
|
+
when /^pull\/(\d+)/
|
501
|
+
pull_id = $1.to_i
|
502
|
+
patch_name = "#{pull_id}.patch"
|
503
|
+
patch = api_client.pullrequest_patch(gh_url.project, pull_id)
|
504
|
+
when /^commit\/([a-f0-9]{7,40})/
|
505
|
+
commit_sha = $1
|
506
|
+
patch_name = "#{commit_sha}.patch"
|
507
|
+
patch = api_client.commit_patch(gh_url.project, commit_sha)
|
508
|
+
else
|
509
|
+
raise ArgumentError, url
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
patch_file = File.join(tmp_dir, patch_name)
|
514
|
+
File.open(patch_file, 'w') { |file| file.write(patch) }
|
439
515
|
args[idx] = patch_file
|
440
516
|
end
|
441
517
|
end
|
442
518
|
|
443
519
|
# $ hub apply https://github.com/defunkt/hub/pull/55
|
444
|
-
#
|
520
|
+
# ... downloads patch via API ...
|
445
521
|
# > git apply /tmp/55.patch
|
446
522
|
alias_method :apply, :am
|
447
523
|
|
@@ -594,8 +670,8 @@ module Hub
|
|
594
670
|
branch = master_branch
|
595
671
|
else
|
596
672
|
# $ hub browse
|
597
|
-
project =
|
598
|
-
branch
|
673
|
+
branch, project = remote_branch_and_project(method(:github_user))
|
674
|
+
branch ||= master_branch
|
599
675
|
end
|
600
676
|
|
601
677
|
abort "Usage: hub browse [<USER>/]<REPOSITORY>" unless project
|
@@ -603,9 +679,9 @@ module Hub
|
|
603
679
|
# $ hub browse -- wiki
|
604
680
|
path = case subpage = args.shift
|
605
681
|
when 'commits'
|
606
|
-
"/commits/#{branch
|
682
|
+
"/commits/#{branch_in_url(branch)}"
|
607
683
|
when 'tree', NilClass
|
608
|
-
"/tree/#{branch
|
684
|
+
"/tree/#{branch_in_url(branch)}" if branch and !branch.master?
|
609
685
|
else
|
610
686
|
"/#{subpage}"
|
611
687
|
end
|
@@ -625,11 +701,10 @@ module Hub
|
|
625
701
|
def compare(args)
|
626
702
|
args.shift
|
627
703
|
browse_command(args) do
|
704
|
+
branch, project = remote_branch_and_project(method(:github_user))
|
628
705
|
if args.empty?
|
629
|
-
branch = current_branch.upstream
|
630
706
|
if branch and not branch.master?
|
631
707
|
range = branch.short_name
|
632
|
-
project = current_project
|
633
708
|
else
|
634
709
|
abort "Usage: hub compare [USER] [<START>...]<END>"
|
635
710
|
end
|
@@ -637,9 +712,9 @@ module Hub
|
|
637
712
|
sha_or_tag = /((?:#{OWNER_RE}:)?\w[\w.-]+\w)/
|
638
713
|
# replaces two dots with three: "sha1...sha2"
|
639
714
|
range = args.pop.sub(/^#{sha_or_tag}\.\.#{sha_or_tag}$/, '\1...\2')
|
640
|
-
|
641
|
-
|
642
|
-
|
715
|
+
if owner = args.pop
|
716
|
+
project = project.owned_by(owner)
|
717
|
+
end
|
643
718
|
end
|
644
719
|
|
645
720
|
project.web_url "/compare/#{range}"
|
@@ -680,23 +755,23 @@ module Hub
|
|
680
755
|
|
681
756
|
if script
|
682
757
|
puts "alias git=hub"
|
683
|
-
if 'zsh' == shell
|
684
|
-
puts "if type compdef >/dev/null; then"
|
685
|
-
puts " compdef hub=git"
|
686
|
-
puts "fi"
|
687
|
-
end
|
688
758
|
else
|
689
759
|
profile = case shell
|
690
760
|
when 'bash' then '~/.bash_profile'
|
691
761
|
when 'zsh' then '~/.zshrc'
|
692
762
|
when 'ksh' then '~/.profile'
|
763
|
+
when 'fish' then '~/.config/fish/config.fish'
|
693
764
|
else
|
694
765
|
'your profile'
|
695
766
|
end
|
696
767
|
|
697
768
|
puts "# Wrap git automatically by adding the following to #{profile}:"
|
698
769
|
puts
|
699
|
-
|
770
|
+
if shell == 'fish'
|
771
|
+
puts 'eval (hub alias -s)'
|
772
|
+
else
|
773
|
+
puts 'eval "$(hub alias -s)"'
|
774
|
+
end
|
700
775
|
end
|
701
776
|
|
702
777
|
exit
|
@@ -715,13 +790,19 @@ module Hub
|
|
715
790
|
def help(args)
|
716
791
|
command = args.words[1]
|
717
792
|
|
718
|
-
if command == 'hub'
|
793
|
+
if command == 'hub' || custom_command?(command)
|
719
794
|
puts hub_manpage
|
720
795
|
exit
|
721
|
-
elsif command.nil?
|
722
|
-
|
723
|
-
|
724
|
-
|
796
|
+
elsif command.nil?
|
797
|
+
if args.has_flag?('-a', '--all')
|
798
|
+
# Add the special hub commands to the end of "git help -a" output.
|
799
|
+
args.after 'echo', ["\nhub custom commands\n"]
|
800
|
+
args.after 'echo', CUSTOM_COMMANDS.map {|cmd| " #{cmd}" }
|
801
|
+
else
|
802
|
+
ENV['GIT_PAGER'] = '' unless args.has_flag?('-p', '--paginate') # Use `cat`.
|
803
|
+
puts improved_help_text
|
804
|
+
exit
|
805
|
+
end
|
725
806
|
end
|
726
807
|
end
|
727
808
|
alias_method "--help", :help
|
@@ -732,18 +813,22 @@ module Hub
|
|
732
813
|
# from the command line.
|
733
814
|
#
|
734
815
|
|
816
|
+
def branch_in_url(branch)
|
817
|
+
CGI.escape(branch.short_name).gsub("%2F", "/")
|
818
|
+
end
|
819
|
+
|
735
820
|
def api_client
|
736
821
|
@api_client ||= begin
|
737
822
|
config_file = ENV['HUB_CONFIG'] || '~/.config/hub'
|
738
823
|
file_store = GitHubAPI::FileStore.new File.expand_path(config_file)
|
739
824
|
file_config = GitHubAPI::Configuration.new file_store
|
740
|
-
GitHubAPI.new file_config, :app_url => 'http://
|
825
|
+
GitHubAPI.new file_config, :app_url => 'http://hub.github.com/'
|
741
826
|
end
|
742
827
|
end
|
743
828
|
|
744
829
|
def github_user host = nil, &block
|
745
830
|
host ||= (local_repo(false) || Context::LocalRepo).default_host
|
746
|
-
api_client.
|
831
|
+
api_client.username_via_auth_dance(host, &block)
|
747
832
|
end
|
748
833
|
|
749
834
|
def custom_command? cmd
|
@@ -818,6 +903,7 @@ GitHub Commands:
|
|
818
903
|
create Create this repository on GitHub and add GitHub as origin
|
819
904
|
browse Open a GitHub page in the default browser
|
820
905
|
compare Open a compare page on GitHub
|
906
|
+
ci-status Show the CI status of a commit
|
821
907
|
|
822
908
|
See 'git help <command>' for more information on a specific command.
|
823
909
|
help
|
@@ -900,7 +986,8 @@ help
|
|
900
986
|
# in order to turn our raw roff (manpage markup) into something
|
901
987
|
# readable on the terminal.
|
902
988
|
def groff_command
|
903
|
-
|
989
|
+
cols = terminal_width
|
990
|
+
"groff -Wall -mtty-char -mandoc -Tascii -rLL=#{cols}n -rLT=#{cols}n"
|
904
991
|
end
|
905
992
|
|
906
993
|
# Returns the raw hub manpage. If we're not running in standalone
|
@@ -944,7 +1031,8 @@ help
|
|
944
1031
|
Kernel.select [STDIN]
|
945
1032
|
|
946
1033
|
pager = ENV['GIT_PAGER'] ||
|
947
|
-
`git config --get-all core.pager`.split.first ||
|
1034
|
+
`git config --get-all core.pager`.split("\n").first ||
|
1035
|
+
ENV['PAGER'] ||
|
948
1036
|
'less -isr'
|
949
1037
|
|
950
1038
|
pager = 'cat' if pager.empty?
|
@@ -962,24 +1050,53 @@ help
|
|
962
1050
|
end
|
963
1051
|
|
964
1052
|
def pullrequest_editmsg(changes)
|
965
|
-
message_file =
|
1053
|
+
message_file = pullrequest_editmsg_file
|
1054
|
+
|
1055
|
+
if valid_editmsg_file?(message_file)
|
1056
|
+
title, body = read_editmsg(message_file)
|
1057
|
+
previous_message = [title, body].compact.join("\n\n") if title
|
1058
|
+
end
|
1059
|
+
|
966
1060
|
File.open(message_file, 'w') { |msg|
|
967
|
-
yield msg
|
1061
|
+
yield msg, previous_message
|
968
1062
|
if changes
|
969
1063
|
msg.puts "#\n# Changes:\n#"
|
970
1064
|
msg.puts changes.gsub(/^/, '# ').gsub(/ +$/, '')
|
971
1065
|
end
|
972
1066
|
}
|
1067
|
+
|
973
1068
|
edit_cmd = Array(git_editor).dup
|
974
|
-
edit_cmd << '-c' << 'set ft=gitcommit' if edit_cmd[0] =~ /^[mg]?vim$/
|
1069
|
+
edit_cmd << '-c' << 'set ft=gitcommit tw=0 wrap lbr' if edit_cmd[0] =~ /^[mg]?vim$/
|
975
1070
|
edit_cmd << message_file
|
976
1071
|
system(*edit_cmd)
|
977
|
-
|
1072
|
+
|
1073
|
+
unless $?.success?
|
1074
|
+
# writing was cancelled, or the editor never opened in the first place
|
1075
|
+
delete_editmsg(message_file)
|
1076
|
+
abort "error using text editor for pull request message"
|
1077
|
+
end
|
1078
|
+
|
978
1079
|
title, body = read_editmsg(message_file)
|
979
1080
|
abort "Aborting due to empty pull request title" unless title
|
980
1081
|
[title, body]
|
981
1082
|
end
|
982
1083
|
|
1084
|
+
# This unfortunate hack is because older versions of hub never cleaned up
|
1085
|
+
# the pullrequest_editmsg_file, which newer hub would pick up and
|
1086
|
+
# misinterpret as a message which should be reused after a failed PR.
|
1087
|
+
def valid_editmsg_file?(message_file)
|
1088
|
+
File.exists?(message_file) &&
|
1089
|
+
File.mtime(message_file) > File.mtime(__FILE__)
|
1090
|
+
end
|
1091
|
+
|
1092
|
+
def read_msg(message)
|
1093
|
+
message.split("\n\n", 2).each {|s| s.strip! }.reject {|s| s.empty? }
|
1094
|
+
end
|
1095
|
+
|
1096
|
+
def pullrequest_editmsg_file
|
1097
|
+
File.join(git_dir, 'PULLREQ_EDITMSG')
|
1098
|
+
end
|
1099
|
+
|
983
1100
|
def read_editmsg(file)
|
984
1101
|
title, body = '', ''
|
985
1102
|
File.open(file, 'r') { |msg|
|
@@ -995,6 +1112,10 @@ help
|
|
995
1112
|
[title =~ /\S/ ? title : nil, body =~ /\S/ ? body : nil]
|
996
1113
|
end
|
997
1114
|
|
1115
|
+
def delete_editmsg(file = pullrequest_editmsg_file)
|
1116
|
+
File.delete(file) if File.exist?(file)
|
1117
|
+
end
|
1118
|
+
|
998
1119
|
def expand_alias(cmd)
|
999
1120
|
if expanded = git_alias_for(cmd)
|
1000
1121
|
if expanded.index('!') != 0
|
@@ -1003,7 +1124,7 @@ help
|
|
1003
1124
|
end
|
1004
1125
|
end
|
1005
1126
|
end
|
1006
|
-
|
1127
|
+
|
1007
1128
|
def display_api_exception(action, response)
|
1008
1129
|
$stderr.puts "Error #{action}: #{response.message.strip} (HTTP #{response.status})"
|
1009
1130
|
if 422 == response.status and response.error_message?
|