hub 1.7.0 → 1.8.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 +0 -1
- data/lib/hub.rb +1 -0
- data/lib/hub/commands.rb +113 -92
- data/lib/hub/context.rb +155 -20
- data/lib/hub/json.rb +76 -0
- data/lib/hub/version.rb +1 -1
- data/man/hub.1 +43 -3
- data/man/hub.1.html +24 -3
- data/man/hub.1.ronn +20 -2
- data/test/hub_test.rb +213 -42
- metadata +4 -3
data/lib/hub/json.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'strscan'
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
class Hub::JSON
|
5
|
+
WSP = /\s+/
|
6
|
+
OBJ = /[{\[]/
|
7
|
+
NUM = /-?\d+(\.\d+)?([eE][+-]?\d+)?/
|
8
|
+
BOL = /(?:true|false)\b/
|
9
|
+
NUL = /null\b/
|
10
|
+
|
11
|
+
extend Forwardable
|
12
|
+
|
13
|
+
attr_reader :scanner
|
14
|
+
alias_method :s, :scanner
|
15
|
+
private :s
|
16
|
+
def_delegators :scanner, :scan, :matched
|
17
|
+
|
18
|
+
def self.parse(str)
|
19
|
+
self.new(str).parse
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize data
|
23
|
+
@scanner = StringScanner.new data.to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
def space
|
27
|
+
scan WSP
|
28
|
+
end
|
29
|
+
|
30
|
+
def parse
|
31
|
+
space
|
32
|
+
object
|
33
|
+
end
|
34
|
+
|
35
|
+
def object
|
36
|
+
case scan(OBJ)
|
37
|
+
when '{' then hash
|
38
|
+
when '[' then array
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def value
|
43
|
+
object or string or
|
44
|
+
(scan(NUM) || scan(BOL)) ? eval(matched) :
|
45
|
+
scan(NUL) && nil
|
46
|
+
end
|
47
|
+
|
48
|
+
def hash
|
49
|
+
current = {}
|
50
|
+
space
|
51
|
+
until scan(/\}/)
|
52
|
+
key = string
|
53
|
+
scan(/\s*:\s*/)
|
54
|
+
current[key] = value
|
55
|
+
scan(/\s*,\s*/) or space
|
56
|
+
end
|
57
|
+
current
|
58
|
+
end
|
59
|
+
|
60
|
+
def array
|
61
|
+
current = []
|
62
|
+
space
|
63
|
+
until scan(/\]/)
|
64
|
+
current << value
|
65
|
+
scan(/\s*,\s*/) or space
|
66
|
+
end
|
67
|
+
current
|
68
|
+
end
|
69
|
+
|
70
|
+
def string
|
71
|
+
if str = scan(/"/)
|
72
|
+
begin; str << s.scan_until(/"/); end while s.pre_match[-1,1] == '\\'
|
73
|
+
eval str
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/hub/version.rb
CHANGED
data/man/hub.1
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
.\" generated with Ronn/v0.7.3
|
2
2
|
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
3
3
|
.
|
4
|
-
.TH "HUB" "1" "
|
4
|
+
.TH "HUB" "1" "January 2012" "DEFUNKT" "Git Manual"
|
5
5
|
.
|
6
6
|
.SH "NAME"
|
7
7
|
\fBhub\fR \- git + hub = github
|
@@ -138,8 +138,8 @@ Open a GitHub compare view page in the system\'s default web browser\. \fISTART\
|
|
138
138
|
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)\.
|
139
139
|
.
|
140
140
|
.TP
|
141
|
-
\fBgit pull\-request\fR [\fB\-f\fR] [\fITITLE\fR|\fB\-i\fR \fIISSUE\fR] [\fB\-b\fR \fIBASE\fR] [\fB\-h\fR \fIHEAD\fR]
|
142
|
-
|
141
|
+
\fBgit pull\-request\fR [\fB\-f\fR] [\fITITLE\fR|\fB\-i\fR \fIISSUE\fR|\fIISSUE\-URL\fR] [\fB\-b\fR \fIBASE\fR] [\fB\-h\fR \fIHEAD\fR]
|
142
|
+
Opens a pull request on GitHub for the project that the "origin" remote points to\. The default head of the pull request is the current branch\. Both base and head of the pull request can be explicitly given in one of the following formats: "branch", "owner:branch", "owner/repo:branch"\. This command will abort operation if it detects that the current topic branch has local commits that are not yet pushed to its upstream branch on the remote\. To skip this check, use \fB\-f\fR\.
|
143
143
|
.
|
144
144
|
.IP
|
145
145
|
If \fITITLE\fR is omitted, a text editor will open in which title and body of the pull request can be entered in the same manner as git commit message\.
|
@@ -193,6 +193,46 @@ $ git config \-\-global hub\.protocol https
|
|
193
193
|
.
|
194
194
|
.IP "" 0
|
195
195
|
.
|
196
|
+
.SS "GitHub Enterprise"
|
197
|
+
By default, hub will only work with repositories that have remotes which point to github\.com\. GitHub Enterprise hosts need to be whitelisted to configure hub to treat such remotes same as github\.com:
|
198
|
+
.
|
199
|
+
.IP "" 4
|
200
|
+
.
|
201
|
+
.nf
|
202
|
+
|
203
|
+
$ git config \-\-global \-\-add hub\.host my\.git\.org
|
204
|
+
.
|
205
|
+
.fi
|
206
|
+
.
|
207
|
+
.IP "" 0
|
208
|
+
.
|
209
|
+
.P
|
210
|
+
API username and token need also be configured for each Enterprise host:
|
211
|
+
.
|
212
|
+
.IP "" 4
|
213
|
+
.
|
214
|
+
.nf
|
215
|
+
|
216
|
+
$ git config \-\-global github\."my\.git\.org"\.user <username>
|
217
|
+
$ git config \-\-global github\."my\.git\.org"\.token <token>
|
218
|
+
.
|
219
|
+
.fi
|
220
|
+
.
|
221
|
+
.IP "" 0
|
222
|
+
.
|
223
|
+
.P
|
224
|
+
The default host for commands like \fBinit\fR and \fBclone\fR is still github\.com, but this can be affected with the \fIGITHUB_HOST\fR environment variable:
|
225
|
+
.
|
226
|
+
.IP "" 4
|
227
|
+
.
|
228
|
+
.nf
|
229
|
+
|
230
|
+
$ GITHUB_HOST=my\.git\.org git clone myproject
|
231
|
+
.
|
232
|
+
.fi
|
233
|
+
.
|
234
|
+
.IP "" 0
|
235
|
+
.
|
196
236
|
.SH "EXAMPLES"
|
197
237
|
.
|
198
238
|
.SS "git clone"
|
data/man/hub.1.html
CHANGED
@@ -176,8 +176,7 @@ GitHub will compare against the base branch (the default is "master").</p></dd>
|
|
176
176
|
<dt><code>git fork</code> [<code>--no-remote</code>]</dt><dd><p>Forks the original project (referenced by "origin" remote) on GitHub and
|
177
177
|
adds a new remote for it under your username. Requires <code>github.token</code> to
|
178
178
|
be set (see CONFIGURATION).</p></dd>
|
179
|
-
<dt><code>git pull-request</code> [<code>-f</code>] [<var>TITLE</var>|<code>-i</code> <var>ISSUE</var>] [<code>-b</code> <var>BASE</var>] [<code>-h</code> <var>HEAD</var>]</dt><dd><p
|
180
|
-
Opens a pull request on GitHub for the project that the "origin" remote
|
179
|
+
<dt><code>git pull-request</code> [<code>-f</code>] [<var>TITLE</var>|<code>-i</code> <var>ISSUE</var>|<var>ISSUE-URL</var>] [<code>-b</code> <var>BASE</var>] [<code>-h</code> <var>HEAD</var>]</dt><dd><p>Opens a pull request on GitHub for the project that the "origin" remote
|
181
180
|
points to. The default head of the pull request is the current branch.
|
182
181
|
Both base and head of the pull request can be explicitly given in one of
|
183
182
|
the following formats: "branch", "owner:branch", "owner/repo:branch".
|
@@ -221,6 +220,28 @@ URLs that otherwise use git and ssh protocols.</p>
|
|
221
220
|
<pre><code>$ git config --global hub.protocol https
|
222
221
|
</code></pre>
|
223
222
|
|
223
|
+
<h3 id="GitHub-Enterprise">GitHub Enterprise</h3>
|
224
|
+
|
225
|
+
<p>By default, hub will only work with repositories that have remotes which
|
226
|
+
point to github.com. GitHub Enterprise hosts need to be whitelisted to
|
227
|
+
configure hub to treat such remotes same as github.com:</p>
|
228
|
+
|
229
|
+
<pre><code>$ git config --global --add hub.host my.git.org
|
230
|
+
</code></pre>
|
231
|
+
|
232
|
+
<p>API username and token need also be configured for each Enterprise host:</p>
|
233
|
+
|
234
|
+
<pre><code>$ git config --global github."my.git.org".user <username>
|
235
|
+
$ git config --global github."my.git.org".token <token>
|
236
|
+
</code></pre>
|
237
|
+
|
238
|
+
<p>The default host for commands like <code>init</code> and <code>clone</code> is still
|
239
|
+
github.com, but this can be affected with the <var>GITHUB_HOST</var> environment
|
240
|
+
variable:</p>
|
241
|
+
|
242
|
+
<pre><code>$ GITHUB_HOST=my.git.org git clone myproject
|
243
|
+
</code></pre>
|
244
|
+
|
224
245
|
<h2 id="EXAMPLES">EXAMPLES</h2>
|
225
246
|
|
226
247
|
<h3 id="git-clone">git clone</h3>
|
@@ -428,7 +449,7 @@ $ git help hub
|
|
428
449
|
|
429
450
|
<ol class='man-decor man-foot man foot'>
|
430
451
|
<li class='tl'>DEFUNKT</li>
|
431
|
-
<li class='tc'>
|
452
|
+
<li class='tc'>January 2012</li>
|
432
453
|
<li class='tr'>hub(1)</li>
|
433
454
|
</ol>
|
434
455
|
|
data/man/hub.1.ronn
CHANGED
@@ -132,8 +132,7 @@ hub also adds some custom commands that are otherwise not present in git:
|
|
132
132
|
adds a new remote for it under your username. Requires `github.token` to
|
133
133
|
be set (see CONFIGURATION).
|
134
134
|
|
135
|
-
* `git pull-request` [`-f`] [<TITLE>|`-i` <ISSUE>] [`-b` <BASE>] [`-h` <HEAD>]:
|
136
|
-
`git pull-request` [`-f`] <ISSUE-URL> [`-h` <HEAD>]
|
135
|
+
* `git pull-request` [`-f`] [<TITLE>|`-i` <ISSUE>|<ISSUE-URL>] [`-b` <BASE>] [`-h` <HEAD>]:
|
137
136
|
Opens a pull request on GitHub for the project that the "origin" remote
|
138
137
|
points to. The default head of the pull request is the current branch.
|
139
138
|
Both base and head of the pull request can be explicitly given in one of
|
@@ -173,6 +172,25 @@ URLs that otherwise use git and ssh protocols.
|
|
173
172
|
|
174
173
|
$ git config --global hub.protocol https
|
175
174
|
|
175
|
+
### GitHub Enterprise
|
176
|
+
|
177
|
+
By default, hub will only work with repositories that have remotes which
|
178
|
+
point to github.com. GitHub Enterprise hosts need to be whitelisted to
|
179
|
+
configure hub to treat such remotes same as github.com:
|
180
|
+
|
181
|
+
$ git config --global --add hub.host my.git.org
|
182
|
+
|
183
|
+
API username and token need also be configured for each Enterprise host:
|
184
|
+
|
185
|
+
$ git config --global github."my.git.org".user <username>
|
186
|
+
$ git config --global github."my.git.org".token <token>
|
187
|
+
|
188
|
+
The default host for commands like `init` and `clone` is still
|
189
|
+
github.com, but this can be affected with the <GITHUB_HOST> environment
|
190
|
+
variable:
|
191
|
+
|
192
|
+
$ GITHUB_HOST=my.git.org git clone myproject
|
193
|
+
|
176
194
|
## EXAMPLES
|
177
195
|
|
178
196
|
{{README}}
|
data/test/hub_test.rb
CHANGED
@@ -3,7 +3,6 @@ require 'webmock/test_unit'
|
|
3
3
|
require 'rbconfig'
|
4
4
|
require 'yaml'
|
5
5
|
require 'forwardable'
|
6
|
-
require 'json'
|
7
6
|
|
8
7
|
WebMock::BodyPattern.class_eval do
|
9
8
|
undef normalize_hash
|
@@ -57,22 +56,26 @@ class HubTest < Test::Unit::TestCase
|
|
57
56
|
'rev-parse --symbolic-full-name master@{upstream}' => 'refs/remotes/origin/master',
|
58
57
|
'config --get --bool hub.http-clone' => 'false',
|
59
58
|
'config --get hub.protocol' => nil,
|
59
|
+
'config --get-all hub.host' => nil,
|
60
60
|
'rev-parse -q --git-dir' => '.git'
|
61
61
|
end
|
62
62
|
|
63
63
|
def test_private_clone
|
64
|
+
stub_no_git_repo
|
64
65
|
input = "clone -p rtomayko/ronn"
|
65
66
|
command = "git clone git@github.com:rtomayko/ronn.git"
|
66
67
|
assert_command input, command
|
67
68
|
end
|
68
69
|
|
69
70
|
def test_private_clone_noop
|
71
|
+
stub_no_git_repo
|
70
72
|
input = "--noop clone -p rtomayko/ronn"
|
71
73
|
command = "git clone git@github.com:rtomayko/ronn.git\n"
|
72
74
|
assert_output command, hub(input)
|
73
75
|
end
|
74
76
|
|
75
77
|
def test_https_clone
|
78
|
+
stub_no_git_repo
|
76
79
|
stub_https_is_preferred
|
77
80
|
input = "clone rtomayko/ronn"
|
78
81
|
command = "git clone https://github.com/rtomayko/ronn.git"
|
@@ -80,34 +83,47 @@ class HubTest < Test::Unit::TestCase
|
|
80
83
|
end
|
81
84
|
|
82
85
|
def test_public_clone
|
86
|
+
stub_no_git_repo
|
83
87
|
input = "clone rtomayko/ronn"
|
84
88
|
command = "git clone git://github.com/rtomayko/ronn.git"
|
85
89
|
assert_command input, command
|
86
90
|
end
|
87
91
|
|
88
92
|
def test_your_private_clone
|
93
|
+
stub_no_git_repo
|
89
94
|
input = "clone -p resque"
|
90
95
|
command = "git clone git@github.com:tpw/resque.git"
|
91
96
|
assert_command input, command
|
92
97
|
end
|
93
98
|
|
94
99
|
def test_your_clone_is_always_private
|
100
|
+
stub_no_git_repo
|
95
101
|
input = "clone resque"
|
96
102
|
command = "git clone git@github.com:tpw/resque.git"
|
97
103
|
assert_command input, command
|
98
104
|
end
|
99
105
|
|
106
|
+
def test_clone_repo_with_period
|
107
|
+
stub_no_git_repo
|
108
|
+
input = "clone hookio/hook.js"
|
109
|
+
command = "git clone git://github.com/hookio/hook.js.git"
|
110
|
+
assert_command input, command
|
111
|
+
end
|
112
|
+
|
100
113
|
def test_clone_with_arguments
|
114
|
+
stub_no_git_repo
|
101
115
|
input = "clone --bare -o master resque"
|
102
116
|
command = "git clone --bare -o master git@github.com:tpw/resque.git"
|
103
117
|
assert_command input, command
|
104
118
|
end
|
105
119
|
|
106
120
|
def test_clone_with_arguments_and_destination
|
121
|
+
stub_no_git_repo
|
107
122
|
assert_forwarded "clone --template=one/two git://github.com/tpw/resque.git --origin master resquetastic"
|
108
123
|
end
|
109
124
|
|
110
125
|
def test_your_private_clone_fails_without_config
|
126
|
+
stub_no_git_repo
|
111
127
|
out = hub("clone -p mustache") do
|
112
128
|
stub_github_user(nil)
|
113
129
|
end
|
@@ -116,6 +132,7 @@ class HubTest < Test::Unit::TestCase
|
|
116
132
|
end
|
117
133
|
|
118
134
|
def test_your_public_clone_fails_without_config
|
135
|
+
stub_no_git_repo
|
119
136
|
out = hub("clone mustache") do
|
120
137
|
stub_github_user(nil)
|
121
138
|
end
|
@@ -124,25 +141,39 @@ class HubTest < Test::Unit::TestCase
|
|
124
141
|
end
|
125
142
|
|
126
143
|
def test_private_clone_left_alone
|
144
|
+
stub_no_git_repo
|
127
145
|
assert_forwarded "clone git@github.com:rtomayko/ronn.git"
|
128
146
|
end
|
129
147
|
|
130
148
|
def test_public_clone_left_alone
|
149
|
+
stub_no_git_repo
|
131
150
|
assert_forwarded "clone git://github.com/rtomayko/ronn.git"
|
132
151
|
end
|
133
152
|
|
134
153
|
def test_normal_public_clone_with_path
|
154
|
+
stub_no_git_repo
|
135
155
|
assert_forwarded "clone git://github.com/rtomayko/ronn.git ronn-dev"
|
136
156
|
end
|
137
157
|
|
138
158
|
def test_normal_clone_from_path
|
159
|
+
stub_no_git_repo
|
139
160
|
assert_forwarded "clone ./test"
|
140
161
|
end
|
141
162
|
|
142
163
|
def test_clone_with_host_alias
|
164
|
+
stub_no_git_repo
|
143
165
|
assert_forwarded "clone server:git/repo.git"
|
144
166
|
end
|
145
167
|
|
168
|
+
def test_enterprise_clone
|
169
|
+
stub_no_git_repo
|
170
|
+
stub_github_user('myfiname', 'git.my.org')
|
171
|
+
with_host_env('git.my.org') do
|
172
|
+
assert_command "clone myrepo", "git clone git@git.my.org:myfiname/myrepo.git"
|
173
|
+
assert_command "clone another/repo", "git clone git@git.my.org:another/repo.git"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
146
177
|
def test_alias_expand
|
147
178
|
stub_alias 'c', 'clone --bare'
|
148
179
|
input = "c rtomayko/ronn"
|
@@ -168,16 +199,24 @@ class HubTest < Test::Unit::TestCase
|
|
168
199
|
assert_command input, command
|
169
200
|
end
|
170
201
|
|
202
|
+
def test_remote_add_with_name
|
203
|
+
input = "remote add another hookio/hub.js"
|
204
|
+
command = "git remote add another git://github.com/hookio/hub.js.git"
|
205
|
+
assert_command input, command
|
206
|
+
end
|
207
|
+
|
171
208
|
def test_private_remote_origin
|
172
209
|
input = "remote add -p origin"
|
173
210
|
command = "git remote add origin git@github.com:tpw/hub.git"
|
174
211
|
assert_command input, command
|
175
212
|
end
|
176
213
|
|
177
|
-
def
|
178
|
-
|
179
|
-
|
180
|
-
|
214
|
+
def test_public_remote_url_untouched
|
215
|
+
assert_forwarded "remote add origin http://github.com/defunkt/resque.git"
|
216
|
+
end
|
217
|
+
|
218
|
+
def test_private_remote_url_untouched
|
219
|
+
assert_forwarded "remote add origin git@github.com:defunkt/resque.git"
|
181
220
|
end
|
182
221
|
|
183
222
|
def test_remote_from_rel_path
|
@@ -192,8 +231,10 @@ class HubTest < Test::Unit::TestCase
|
|
192
231
|
assert_forwarded "remote add origin server:/git/repo.git"
|
193
232
|
end
|
194
233
|
|
195
|
-
def
|
196
|
-
|
234
|
+
def test_remote_add_enterprise
|
235
|
+
stub_hub_host('git.my.org')
|
236
|
+
stub_repo_url('git@git.my.org:defunkt/hub.git')
|
237
|
+
assert_command "remote add another", "git remote add another git@git.my.org:another/hub.git"
|
197
238
|
end
|
198
239
|
|
199
240
|
def test_public_submodule
|
@@ -294,6 +335,29 @@ class HubTest < Test::Unit::TestCase
|
|
294
335
|
"fetch xoebus"
|
295
336
|
end
|
296
337
|
|
338
|
+
def test_fetch_new_remote_https_protocol
|
339
|
+
stub_remotes_group('xoebus', nil)
|
340
|
+
stub_existing_fork('xoebus')
|
341
|
+
stub_https_is_preferred
|
342
|
+
|
343
|
+
assert_commands "git remote add xoebus https://github.com/xoebus/hub.git",
|
344
|
+
"git fetch xoebus",
|
345
|
+
"fetch xoebus"
|
346
|
+
end
|
347
|
+
|
348
|
+
def test_fetch_no_auth
|
349
|
+
stub_github_user nil
|
350
|
+
stub_github_token nil
|
351
|
+
stub_remotes_group('xoebus', nil)
|
352
|
+
# stub_existing_fork('xoebus')
|
353
|
+
stub_request(:get, "https://github.com/api/v2/yaml/repos/show/xoebus/hub").
|
354
|
+
to_return(:status => 200)
|
355
|
+
|
356
|
+
assert_commands "git remote add xoebus git://github.com/xoebus/hub.git",
|
357
|
+
"git fetch xoebus",
|
358
|
+
"fetch xoebus"
|
359
|
+
end
|
360
|
+
|
297
361
|
def test_fetch_new_remote_with_options
|
298
362
|
stub_remotes_group('xoebus', nil)
|
299
363
|
stub_existing_fork('xoebus')
|
@@ -404,6 +468,13 @@ class HubTest < Test::Unit::TestCase
|
|
404
468
|
end
|
405
469
|
end
|
406
470
|
|
471
|
+
def test_am_no_tmpdir
|
472
|
+
with_tmpdir(nil) do
|
473
|
+
cmd = Hub("am https://github.com/defunkt/hub/pull/55").command
|
474
|
+
assert_includes '/tmp/55.patch', cmd
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
407
478
|
def test_am_commit_url
|
408
479
|
with_tmpdir('/tmp/') do
|
409
480
|
url = 'https://github.com/davidbalbert/hub/commit/fdb9921'
|
@@ -460,9 +531,20 @@ class HubTest < Test::Unit::TestCase
|
|
460
531
|
end
|
461
532
|
|
462
533
|
def test_init
|
534
|
+
stub_no_remotes
|
535
|
+
stub_no_git_repo
|
463
536
|
assert_commands "git init", "git remote add origin git@github.com:tpw/hub.git", "init -g"
|
464
537
|
end
|
465
538
|
|
539
|
+
def test_init_enterprise
|
540
|
+
stub_no_remotes
|
541
|
+
stub_no_git_repo
|
542
|
+
stub_github_user('myfiname', 'git.my.org')
|
543
|
+
with_host_env('git.my.org') do
|
544
|
+
assert_commands "git init", "git remote add origin git@git.my.org:myfiname/hub.git", "init -g"
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
466
548
|
def test_init_no_login
|
467
549
|
out = hub("init -g") do
|
468
550
|
stub_github_user(nil)
|
@@ -528,7 +610,10 @@ class HubTest < Test::Unit::TestCase
|
|
528
610
|
|
529
611
|
def test_create_no_openssl
|
530
612
|
stub_no_remotes
|
531
|
-
stub_nonexisting_fork('tpw')
|
613
|
+
# stub_nonexisting_fork('tpw')
|
614
|
+
stub_request(:get, "http://#{auth}github.com/api/v2/yaml/repos/show/tpw/hub").
|
615
|
+
to_return(:status => 404)
|
616
|
+
|
532
617
|
stub_request(:post, "http://#{auth}github.com/api/v2/yaml/repos/create").
|
533
618
|
with(:body => { 'name' => 'hub' })
|
534
619
|
|
@@ -555,13 +640,15 @@ class HubTest < Test::Unit::TestCase
|
|
555
640
|
|
556
641
|
def test_create_with_env_authentication
|
557
642
|
stub_no_remotes
|
558
|
-
stub_nonexisting_fork('mojombo')
|
559
|
-
|
560
643
|
old_user = ENV['GITHUB_USER']
|
561
644
|
old_token = ENV['GITHUB_TOKEN']
|
562
645
|
ENV['GITHUB_USER'] = 'mojombo'
|
563
646
|
ENV['GITHUB_TOKEN'] = '123abc'
|
564
647
|
|
648
|
+
# stub_nonexisting_fork('mojombo')
|
649
|
+
stub_request(:get, "https://#{auth('mojombo', '123abc')}github.com/api/v2/yaml/repos/show/mojombo/hub").
|
650
|
+
to_return(:status => 404)
|
651
|
+
|
565
652
|
stub_request(:post, "https://#{auth('mojombo', '123abc')}github.com/api/v2/yaml/repos/create").
|
566
653
|
with(:body => { 'name' => 'hub' })
|
567
654
|
|
@@ -606,7 +693,7 @@ class HubTest < Test::Unit::TestCase
|
|
606
693
|
stub_no_remotes
|
607
694
|
stub_existing_fork('tpw')
|
608
695
|
|
609
|
-
expected = "tpw/hub already exists on
|
696
|
+
expected = "tpw/hub already exists on github.com\n"
|
610
697
|
expected << "remote add -f origin git@github.com:tpw/hub.git\n"
|
611
698
|
expected << "set remote origin: tpw/hub\n"
|
612
699
|
assert_equal expected, hub("create") { ENV['GIT'] = 'echo' }
|
@@ -617,7 +704,7 @@ class HubTest < Test::Unit::TestCase
|
|
617
704
|
stub_existing_fork('tpw')
|
618
705
|
stub_https_is_preferred
|
619
706
|
|
620
|
-
expected = "tpw/hub already exists on
|
707
|
+
expected = "tpw/hub already exists on github.com\n"
|
621
708
|
expected << "remote add -f origin https://github.com/tpw/hub.git\n"
|
622
709
|
expected << "set remote origin: tpw/hub\n"
|
623
710
|
assert_equal expected, hub("create") { ENV['GIT'] = 'echo' }
|
@@ -647,11 +734,33 @@ class HubTest < Test::Unit::TestCase
|
|
647
734
|
|
648
735
|
def test_fork
|
649
736
|
stub_nonexisting_fork('tpw')
|
650
|
-
stub_request(:post, "https://#{auth}github.com/api/v2/yaml/repos/fork/defunkt/hub")
|
737
|
+
stub_request(:post, "https://#{auth}github.com/api/v2/yaml/repos/fork/defunkt/hub").
|
738
|
+
with { |req| req.headers['Content-Length'] == 0 }
|
651
739
|
|
652
740
|
expected = "remote add -f tpw git@github.com:tpw/hub.git\n"
|
653
741
|
expected << "new remote: tpw\n"
|
654
|
-
|
742
|
+
assert_output expected, "fork"
|
743
|
+
end
|
744
|
+
|
745
|
+
def test_fork_not_in_repo
|
746
|
+
stub_no_git_repo
|
747
|
+
expected = "fatal: Not a git repository\n"
|
748
|
+
assert_output expected, "fork"
|
749
|
+
end
|
750
|
+
|
751
|
+
def test_fork_enterprise
|
752
|
+
stub_hub_host('git.my.org')
|
753
|
+
stub_repo_url('git@git.my.org:defunkt/hub.git')
|
754
|
+
stub_github_user('myfiname', 'git.my.org')
|
755
|
+
stub_github_token('789xyz', 'git.my.org')
|
756
|
+
|
757
|
+
stub_request(:get, "https://#{auth('myfiname', '789xyz')}git.my.org/api/v2/yaml/repos/show/myfiname/hub").
|
758
|
+
to_return(:status => 404)
|
759
|
+
stub_request(:post, "https://#{auth('myfiname', '789xyz')}git.my.org/api/v2/yaml/repos/fork/defunkt/hub")
|
760
|
+
|
761
|
+
expected = "remote add -f myfiname git@git.my.org:myfiname/hub.git\n"
|
762
|
+
expected << "new remote: myfiname\n"
|
763
|
+
assert_output expected, "fork"
|
655
764
|
end
|
656
765
|
|
657
766
|
def test_fork_failed
|
@@ -673,7 +782,7 @@ class HubTest < Test::Unit::TestCase
|
|
673
782
|
def test_fork_already_exists
|
674
783
|
stub_existing_fork('tpw')
|
675
784
|
|
676
|
-
expected = "tpw/hub already exists on
|
785
|
+
expected = "tpw/hub already exists on github.com\n"
|
677
786
|
expected << "remote add -f tpw git@github.com:tpw/hub.git\n"
|
678
787
|
expected << "new remote: tpw\n"
|
679
788
|
assert_equal expected, hub("fork") { ENV['GIT'] = 'echo' }
|
@@ -683,7 +792,7 @@ class HubTest < Test::Unit::TestCase
|
|
683
792
|
stub_existing_fork('tpw')
|
684
793
|
stub_https_is_preferred
|
685
794
|
|
686
|
-
expected = "tpw/hub already exists on
|
795
|
+
expected = "tpw/hub already exists on github.com\n"
|
687
796
|
expected << "remote add -f tpw https://github.com/tpw/hub.git\n"
|
688
797
|
expected << "new remote: tpw\n"
|
689
798
|
assert_equal expected, hub("fork") { ENV['GIT'] = 'echo' }
|
@@ -708,9 +817,10 @@ class HubTest < Test::Unit::TestCase
|
|
708
817
|
stub_branch('refs/heads/feature')
|
709
818
|
stub_tracking_nothing('feature')
|
710
819
|
|
711
|
-
stub_request(:post, "https://#{auth}github.com/api/v2/
|
712
|
-
with(:body => { 'pull' => {'base' => "master", 'head' => "tpw:feature", 'title' => "hereyougo"} })
|
713
|
-
|
820
|
+
stub_request(:post, "https://#{auth}github.com/api/v2/json/pulls/defunkt/hub").
|
821
|
+
with(:body => { 'pull' => {'base' => "master", 'head' => "tpw:feature", 'title' => "hereyougo"} }) { |req|
|
822
|
+
req.headers['Content-Length'] == 76
|
823
|
+
}.to_return(:body => mock_pullreq_response(1))
|
714
824
|
|
715
825
|
expected = "https://github.com/defunkt/hub/pull/1\n"
|
716
826
|
assert_output expected, "pull-request hereyougo -f"
|
@@ -721,7 +831,7 @@ class HubTest < Test::Unit::TestCase
|
|
721
831
|
stub_tracking('feature', 'mislav', 'yay-feature')
|
722
832
|
stub_command_output "rev-list --cherry mislav/master...", nil
|
723
833
|
|
724
|
-
stub_request(:post, "https://#{auth}github.com/api/v2/
|
834
|
+
stub_request(:post, "https://#{auth}github.com/api/v2/json/pulls/defunkt/hub").
|
725
835
|
with(:body => { 'pull' => {'base' => "master", 'head' => "mislav:yay-feature", 'title' => "hereyougo"} }).
|
726
836
|
to_return(:body => mock_pullreq_response(1))
|
727
837
|
|
@@ -729,8 +839,25 @@ class HubTest < Test::Unit::TestCase
|
|
729
839
|
assert_output expected, "pull-request hereyougo -f"
|
730
840
|
end
|
731
841
|
|
842
|
+
def test_pullrequest_enterprise_no_tracking
|
843
|
+
stub_hub_host('git.my.org')
|
844
|
+
stub_repo_url('git@git.my.org:defunkt/hub.git')
|
845
|
+
stub_github_user('myfiname', 'git.my.org')
|
846
|
+
stub_github_token('789xyz', 'git.my.org')
|
847
|
+
stub_branch('refs/heads/feature')
|
848
|
+
stub_tracking_nothing('feature')
|
849
|
+
stub_command_output "rev-list --cherry origin/feature...", nil
|
850
|
+
|
851
|
+
stub_request(:post, "https://#{auth('myfiname', '789xyz')}git.my.org/api/v2/json/pulls/defunkt/hub").
|
852
|
+
with(:body => { 'pull' => {'base' => "master", 'head' => "myfiname:feature", 'title' => "hereyougo"} }).
|
853
|
+
to_return(:body => mock_pullreq_response(1, 'defunkt/hub', 'git.my.org'))
|
854
|
+
|
855
|
+
expected = "https://git.my.org/defunkt/hub/pull/1\n"
|
856
|
+
assert_output expected, "pull-request hereyougo -f"
|
857
|
+
end
|
858
|
+
|
732
859
|
def test_pullrequest_explicit_head
|
733
|
-
stub_request(:post, "https://#{auth}github.com/api/v2/
|
860
|
+
stub_request(:post, "https://#{auth}github.com/api/v2/json/pulls/defunkt/hub").
|
734
861
|
with(:body => { 'pull' => {'base' => "master", 'head' => "tpw:yay-feature", 'title' => "hereyougo"} }).
|
735
862
|
to_return(:body => mock_pullreq_response(1))
|
736
863
|
|
@@ -739,7 +866,7 @@ class HubTest < Test::Unit::TestCase
|
|
739
866
|
end
|
740
867
|
|
741
868
|
def test_pullrequest_explicit_head_with_owner
|
742
|
-
stub_request(:post, "https://#{auth}github.com/api/v2/
|
869
|
+
stub_request(:post, "https://#{auth}github.com/api/v2/json/pulls/defunkt/hub").
|
743
870
|
with(:body => { 'pull' => {'base' => "master", 'head' => "mojombo:feature", 'title' => "hereyougo"} }).
|
744
871
|
to_return(:body => mock_pullreq_response(1))
|
745
872
|
|
@@ -748,7 +875,7 @@ class HubTest < Test::Unit::TestCase
|
|
748
875
|
end
|
749
876
|
|
750
877
|
def test_pullrequest_explicit_base
|
751
|
-
stub_request(:post, "https://#{auth}github.com/api/v2/
|
878
|
+
stub_request(:post, "https://#{auth}github.com/api/v2/json/pulls/defunkt/hub").
|
752
879
|
with(:body => { 'pull' => {'base' => "feature", 'head' => "defunkt:master", 'title' => "hereyougo"} }).
|
753
880
|
to_return(:body => mock_pullreq_response(1))
|
754
881
|
|
@@ -757,7 +884,7 @@ class HubTest < Test::Unit::TestCase
|
|
757
884
|
end
|
758
885
|
|
759
886
|
def test_pullrequest_explicit_base_with_owner
|
760
|
-
stub_request(:post, "https://#{auth}github.com/api/v2/
|
887
|
+
stub_request(:post, "https://#{auth}github.com/api/v2/json/pulls/mojombo/hub").
|
761
888
|
with(:body => { 'pull' => {'base' => "feature", 'head' => "defunkt:master", 'title' => "hereyougo"} }).
|
762
889
|
to_return(:body => mock_pullreq_response(1))
|
763
890
|
|
@@ -766,7 +893,7 @@ class HubTest < Test::Unit::TestCase
|
|
766
893
|
end
|
767
894
|
|
768
895
|
def test_pullrequest_explicit_base_with_repo
|
769
|
-
stub_request(:post, "https://#{auth}github.com/api/v2/
|
896
|
+
stub_request(:post, "https://#{auth}github.com/api/v2/json/pulls/mojombo/hubbub").
|
770
897
|
with(:body => { 'pull' => {'base' => "feature", 'head' => "defunkt:master", 'title' => "hereyougo"} }).
|
771
898
|
to_return(:body => mock_pullreq_response(1))
|
772
899
|
|
@@ -779,7 +906,7 @@ class HubTest < Test::Unit::TestCase
|
|
779
906
|
stub_tracking('myfix', 'mislav', 'awesomefix')
|
780
907
|
stub_command_output "rev-list --cherry mislav/awesomefix...", nil
|
781
908
|
|
782
|
-
stub_request(:post, "https://#{auth}github.com/api/v2/
|
909
|
+
stub_request(:post, "https://#{auth}github.com/api/v2/json/pulls/defunkt/hub").
|
783
910
|
with(:body => { 'pull' => {'base' => "master", 'head' => "mislav:awesomefix", 'issue' => '92'} }).
|
784
911
|
to_return(:body => mock_pullreq_response(92))
|
785
912
|
|
@@ -792,7 +919,7 @@ class HubTest < Test::Unit::TestCase
|
|
792
919
|
stub_tracking('myfix', 'mislav', 'awesomefix')
|
793
920
|
stub_command_output "rev-list --cherry mislav/awesomefix...", nil
|
794
921
|
|
795
|
-
stub_request(:post, "https://#{auth}github.com/api/v2/
|
922
|
+
stub_request(:post, "https://#{auth}github.com/api/v2/json/pulls/mojombo/hub").
|
796
923
|
with(:body => { 'pull' => {'base' => "master", 'head' => "mislav:awesomefix", 'issue' => '92'} }).
|
797
924
|
to_return(:body => mock_pullreq_response(92, 'mojombo/hub'))
|
798
925
|
|
@@ -800,12 +927,23 @@ class HubTest < Test::Unit::TestCase
|
|
800
927
|
assert_output expected, "pull-request https://github.com/mojombo/hub/issues/92#comment_4"
|
801
928
|
end
|
802
929
|
|
930
|
+
def test_pullrequest_fails
|
931
|
+
stub_request(:post, "https://#{auth}github.com/api/v2/json/pulls/defunkt/hub").
|
932
|
+
to_return(:status => [422, "Unprocessable Entity"],
|
933
|
+
:headers => {"Content-type" => "application/json"},
|
934
|
+
:body => %({"error":["oh no!", "it failed."]}))
|
935
|
+
|
936
|
+
expected = "Error creating pull request: Unprocessable Entity (HTTP 422)\n"
|
937
|
+
expected << "oh no!\nit failed.\n"
|
938
|
+
assert_output expected, "pull-request hereyougo -b feature -f"
|
939
|
+
end
|
940
|
+
|
803
941
|
def test_checkout_no_changes
|
804
942
|
assert_forwarded "checkout master"
|
805
943
|
end
|
806
944
|
|
807
945
|
def test_checkout_pullrequest
|
808
|
-
stub_request(:get, "
|
946
|
+
stub_request(:get, "https://#{auth}github.com/api/v2/json/pulls/defunkt/hub/73").
|
809
947
|
to_return(:body => mock_pull_response('blueyed:feature'))
|
810
948
|
|
811
949
|
assert_commands 'git remote add -f -t feature blueyed git://github.com/blueyed/hub.git',
|
@@ -813,8 +951,17 @@ class HubTest < Test::Unit::TestCase
|
|
813
951
|
"checkout https://github.com/defunkt/hub/pull/73/files"
|
814
952
|
end
|
815
953
|
|
954
|
+
def test_checkout_private_pullrequest
|
955
|
+
stub_request(:get, "https://#{auth}github.com/api/v2/json/pulls/defunkt/hub/73").
|
956
|
+
to_return(:body => mock_pull_response('blueyed:feature', :private))
|
957
|
+
|
958
|
+
assert_commands 'git remote add -f -t feature blueyed git@github.com:blueyed/hub.git',
|
959
|
+
'git checkout -b blueyed-feature blueyed/feature',
|
960
|
+
"checkout https://github.com/defunkt/hub/pull/73/files"
|
961
|
+
end
|
962
|
+
|
816
963
|
def test_checkout_pullrequest_custom_branch
|
817
|
-
stub_request(:get, "
|
964
|
+
stub_request(:get, "https://#{auth}github.com/api/v2/json/pulls/defunkt/hub/73").
|
818
965
|
to_return(:body => mock_pull_response('blueyed:feature'))
|
819
966
|
|
820
967
|
assert_commands 'git remote add -f -t feature blueyed git://github.com/blueyed/hub.git',
|
@@ -825,7 +972,7 @@ class HubTest < Test::Unit::TestCase
|
|
825
972
|
def test_checkout_pullrequest_existing_remote
|
826
973
|
stub_command_output 'remote', "origin\nblueyed"
|
827
974
|
|
828
|
-
stub_request(:get, "
|
975
|
+
stub_request(:get, "https://#{auth}github.com/api/v2/json/pulls/defunkt/hub/73").
|
829
976
|
to_return(:body => mock_pull_response('blueyed:feature'))
|
830
977
|
|
831
978
|
assert_commands 'git remote set-branches --add blueyed feature',
|
@@ -991,11 +1138,24 @@ config
|
|
991
1138
|
"open https://github.com/mislav/hub/commits/experimental"
|
992
1139
|
end
|
993
1140
|
|
1141
|
+
def test_hub_browse_on_complex_branch
|
1142
|
+
stub_branch('refs/heads/feature/foo')
|
1143
|
+
stub_tracking('feature/foo', 'mislav', 'feature/bar')
|
1144
|
+
|
1145
|
+
assert_command 'browse',
|
1146
|
+
'open https://github.com/mislav/hub/tree/feature/bar'
|
1147
|
+
end
|
1148
|
+
|
994
1149
|
def test_hub_browse_current
|
995
1150
|
assert_command "browse", "open https://github.com/defunkt/hub"
|
996
1151
|
assert_command "browse --", "open https://github.com/defunkt/hub"
|
997
1152
|
end
|
998
1153
|
|
1154
|
+
def test_hub_browse_current_https_uri
|
1155
|
+
stub_repo_url "https://github.com/defunkt/hub"
|
1156
|
+
assert_command "browse", "open https://github.com/defunkt/hub"
|
1157
|
+
end
|
1158
|
+
|
999
1159
|
def test_hub_browse_commit_from_current
|
1000
1160
|
assert_command "browse -- commit/6616e4", "open https://github.com/defunkt/hub/commit/6616e4"
|
1001
1161
|
end
|
@@ -1092,14 +1252,16 @@ config
|
|
1092
1252
|
assert_equal %w[git --bare -c core.awesome=true -c name=value --git-dir=/srv/www], git_reader.executable
|
1093
1253
|
end
|
1094
1254
|
|
1095
|
-
|
1255
|
+
private
|
1096
1256
|
|
1097
|
-
def stub_github_user(name)
|
1098
|
-
|
1257
|
+
def stub_github_user(name, host = '')
|
1258
|
+
host = %(."#{host}") unless host.empty?
|
1259
|
+
stub_config_value "github#{host}.user", name
|
1099
1260
|
end
|
1100
1261
|
|
1101
|
-
def stub_github_token(token)
|
1102
|
-
|
1262
|
+
def stub_github_token(token, host = '')
|
1263
|
+
host = %(."#{host}") unless host.empty?
|
1264
|
+
stub_config_value "github#{host}.token", token
|
1103
1265
|
end
|
1104
1266
|
|
1105
1267
|
def stub_repo_url(value, remote_name = 'origin')
|
@@ -1144,7 +1306,7 @@ config
|
|
1144
1306
|
end
|
1145
1307
|
|
1146
1308
|
def stub_fork(user, repo, status)
|
1147
|
-
stub_request(:get, "github.com/api/v2/yaml/repos/show/#{user}/#{repo}").
|
1309
|
+
stub_request(:get, "https://#{auth}github.com/api/v2/yaml/repos/show/#{user}/#{repo}").
|
1148
1310
|
to_return(:status => status)
|
1149
1311
|
end
|
1150
1312
|
|
@@ -1156,6 +1318,10 @@ config
|
|
1156
1318
|
stub_config_value 'hub.protocol', 'https'
|
1157
1319
|
end
|
1158
1320
|
|
1321
|
+
def stub_hub_host(names)
|
1322
|
+
stub_config_value "hub.host", Array(names).join("\n"), '--get-all'
|
1323
|
+
end
|
1324
|
+
|
1159
1325
|
def with_browser_env(value)
|
1160
1326
|
browser, ENV['BROWSER'] = ENV['BROWSER'], value
|
1161
1327
|
yield
|
@@ -1170,6 +1336,13 @@ config
|
|
1170
1336
|
ENV['TMPDIR'] = dir
|
1171
1337
|
end
|
1172
1338
|
|
1339
|
+
def with_host_env(value)
|
1340
|
+
host, ENV['GITHUB_HOST'] = ENV['GITHUB_HOST'], value
|
1341
|
+
yield
|
1342
|
+
ensure
|
1343
|
+
ENV['GITHUB_HOST'] = host
|
1344
|
+
end
|
1345
|
+
|
1173
1346
|
def assert_browser(browser)
|
1174
1347
|
assert_command "browse", "#{browser} https://github.com/defunkt/hub"
|
1175
1348
|
end
|
@@ -1188,14 +1361,12 @@ config
|
|
1188
1361
|
"#{user}%2Ftoken:#{password}@"
|
1189
1362
|
end
|
1190
1363
|
|
1191
|
-
def mock_pullreq_response(id, name_with_owner = 'defunkt/hub')
|
1192
|
-
|
1193
|
-
'html_url' => "https://github.com/#{name_with_owner}/pull/#{id}"
|
1194
|
-
})
|
1364
|
+
def mock_pullreq_response(id, name_with_owner = 'defunkt/hub', host = 'github.com')
|
1365
|
+
%({"pull": { "html_url": "https://#{host}/#{name_with_owner}/pull/#{id}" }})
|
1195
1366
|
end
|
1196
1367
|
|
1197
|
-
def mock_pull_response(label)
|
1198
|
-
|
1368
|
+
def mock_pull_response(label, priv = false)
|
1369
|
+
%({"pull": { "head": { "label": "#{label}", "repository": {"private":#{!!priv}} }}})
|
1199
1370
|
end
|
1200
1371
|
|
1201
1372
|
def improved_help_text
|