gitdocs 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +6 -14
- data/.codeclimate.yml +26 -0
- data/.rubocop.yml +8 -2
- data/.travis.yml +8 -0
- data/CHANGELOG +13 -0
- data/Gemfile +1 -1
- data/README.md +7 -6
- data/Rakefile +31 -5
- data/bin/gitdocs +1 -0
- data/config.ru +6 -4
- data/gitdocs.gemspec +22 -19
- data/lib/gitdocs.rb +54 -16
- data/lib/gitdocs/browser_app.rb +34 -41
- data/lib/gitdocs/cli.rb +41 -32
- data/lib/gitdocs/configuration.rb +40 -101
- data/lib/gitdocs/git_notifier.rb +111 -0
- data/lib/gitdocs/initializer.rb +83 -0
- data/lib/gitdocs/manager.rb +90 -60
- data/lib/gitdocs/migration/004_add_index_for_path.rb +1 -1
- data/lib/gitdocs/notifier.rb +70 -104
- data/lib/gitdocs/rendering_helper.rb +3 -0
- data/lib/gitdocs/repository.rb +324 -307
- data/lib/gitdocs/repository/committer.rb +77 -0
- data/lib/gitdocs/repository/path.rb +157 -140
- data/lib/gitdocs/search.rb +40 -25
- data/lib/gitdocs/settings_app.rb +5 -3
- data/lib/gitdocs/share.rb +64 -0
- data/lib/gitdocs/synchronizer.rb +40 -0
- data/lib/gitdocs/version.rb +1 -1
- data/lib/gitdocs/views/_header.haml +2 -2
- data/lib/gitdocs/views/dir.haml +3 -3
- data/lib/gitdocs/views/edit.haml +1 -1
- data/lib/gitdocs/views/file.haml +1 -1
- data/lib/gitdocs/views/home.haml +3 -3
- data/lib/gitdocs/views/layout.haml +13 -13
- data/lib/gitdocs/views/revisions.haml +3 -3
- data/lib/gitdocs/views/search.haml +1 -1
- data/lib/gitdocs/views/settings.haml +6 -6
- data/test/integration/cli/full_sync_test.rb +83 -0
- data/test/integration/cli/share_management_test.rb +29 -0
- data/test/integration/cli/status_test.rb +14 -0
- data/test/integration/test_helper.rb +185 -151
- data/test/integration/{browse_test.rb → web/browse_test.rb} +11 -29
- data/test/integration/web/share_management_test.rb +46 -0
- data/test/support/git_factory.rb +276 -0
- data/test/unit/browser_app_test.rb +346 -0
- data/test/unit/configuration_test.rb +8 -70
- data/test/unit/git_notifier_test.rb +116 -0
- data/test/unit/gitdocs_test.rb +90 -0
- data/test/unit/manager_test.rb +36 -0
- data/test/unit/notifier_test.rb +60 -124
- data/test/unit/repository_committer_test.rb +111 -0
- data/test/unit/repository_path_test.rb +92 -76
- data/test/unit/repository_test.rb +243 -356
- data/test/unit/search_test.rb +15 -0
- data/test/unit/settings_app_test.rb +80 -0
- data/test/unit/share_test.rb +97 -0
- data/test/unit/test_helper.rb +17 -3
- metadata +114 -108
- data/lib/gitdocs/runner.rb +0 -108
- data/lib/gitdocs/server.rb +0 -62
- data/test/integration/full_sync_test.rb +0 -66
- data/test/integration/share_management_test.rb +0 -95
- data/test/integration/status_test.rb +0 -21
- data/test/unit/runner_test.rb +0 -122
@@ -2,37 +2,35 @@
|
|
2
2
|
require File.expand_path('../test_helper', __FILE__)
|
3
3
|
|
4
4
|
describe Gitdocs::Repository do
|
5
|
-
let(:local_repo_path) { 'tmp/unit/local' }
|
6
|
-
let(:author1) { 'Art T. Fish <afish@example.com>' }
|
7
|
-
let(:author2) { 'A U Thor <author@example.com>' }
|
8
|
-
|
9
5
|
before do
|
10
|
-
FileUtils.rm_rf(
|
11
|
-
|
12
|
-
|
13
|
-
repo.config['user.email'] = 'afish@example.com'
|
14
|
-
repo.config['user.name'] = 'Art T. Fish'
|
6
|
+
FileUtils.rm_rf(GitFactory.working_directory)
|
7
|
+
GitFactory.init(:local)
|
8
|
+
GitFactory.init_bare(:remote)
|
15
9
|
end
|
16
10
|
|
17
11
|
let(:repository) { Gitdocs::Repository.new(path_or_share) }
|
18
|
-
# Default
|
12
|
+
# Default Share for the repository object, which can be overridden by the
|
19
13
|
# tests when necessary.
|
20
|
-
let(:path_or_share)
|
21
|
-
|
22
|
-
|
14
|
+
let(:path_or_share) do
|
15
|
+
stub(
|
16
|
+
path: expand_path(:local),
|
17
|
+
remote_name: 'origin',
|
18
|
+
branch_name: 'master'
|
19
|
+
)
|
20
|
+
end
|
23
21
|
|
24
22
|
describe 'initialize' do
|
25
23
|
subject { repository }
|
26
24
|
|
27
25
|
describe 'with a missing path' do
|
28
|
-
let(:path_or_share) {
|
26
|
+
let(:path_or_share) { expand_path(:missing) }
|
29
27
|
it { subject.must_be_kind_of Gitdocs::Repository }
|
30
28
|
it { subject.valid?.must_equal false }
|
31
29
|
it { subject.invalid_reason.must_equal :directory_missing }
|
32
30
|
end
|
33
31
|
|
34
32
|
describe 'with a path that is not a repository' do
|
35
|
-
let(:path_or_share) {
|
33
|
+
let(:path_or_share) { expand_path(:not_a_repo) }
|
36
34
|
before { FileUtils.mkdir_p(path_or_share) }
|
37
35
|
it { subject.must_be_kind_of Gitdocs::Repository }
|
38
36
|
it { subject.valid?.must_equal false }
|
@@ -40,6 +38,7 @@ describe Gitdocs::Repository do
|
|
40
38
|
end
|
41
39
|
|
42
40
|
describe 'with a string path that is a repository' do
|
41
|
+
let(:path_or_share) { expand_path(:local) }
|
43
42
|
it { subject.must_be_kind_of Gitdocs::Repository }
|
44
43
|
it { subject.valid?.must_equal true }
|
45
44
|
it { subject.invalid_reason.must_be_nil }
|
@@ -48,35 +47,26 @@ describe Gitdocs::Repository do
|
|
48
47
|
end
|
49
48
|
|
50
49
|
describe 'with a share that is a repository' do
|
51
|
-
let(:path_or_share) do
|
52
|
-
stub(
|
53
|
-
path: local_repo_path,
|
54
|
-
remote_name: 'remote',
|
55
|
-
branch_name: 'branch'
|
56
|
-
)
|
57
|
-
end
|
58
50
|
it { subject.must_be_kind_of Gitdocs::Repository }
|
59
51
|
it { subject.valid?.must_equal true }
|
60
52
|
it { subject.invalid_reason.must_be_nil }
|
61
53
|
it { subject.instance_variable_get(:@rugged).wont_be_nil }
|
62
54
|
it { subject.instance_variable_get(:@grit).wont_be_nil }
|
63
|
-
it { subject.instance_variable_get(:@
|
64
|
-
it { subject.instance_variable_get(:@
|
55
|
+
it { subject.instance_variable_get(:@remote_name).must_equal 'origin' }
|
56
|
+
it { subject.instance_variable_get(:@branch_name).must_equal 'master' }
|
65
57
|
end
|
66
58
|
end
|
67
59
|
|
68
60
|
describe '.clone' do
|
69
|
-
subject { Gitdocs::Repository.clone(
|
70
|
-
|
71
|
-
let(:path) { 'tmp/unit/clone' }
|
72
|
-
let(:remote) { 'tmp/unit/remote' }
|
61
|
+
subject { Gitdocs::Repository.clone('tmp/unit/clone', remote) }
|
73
62
|
|
74
63
|
describe 'with invalid remote' do
|
64
|
+
let(:remote) { expand_path(:invalid) }
|
75
65
|
it { assert_raises(RuntimeError) { subject } }
|
76
66
|
end
|
77
67
|
|
78
68
|
describe 'with valid remote' do
|
79
|
-
|
69
|
+
let(:remote) { expand_path(:remote) }
|
80
70
|
it { subject.must_be_kind_of Gitdocs::Repository }
|
81
71
|
it { subject.valid?.must_equal true }
|
82
72
|
end
|
@@ -91,7 +81,7 @@ describe Gitdocs::Repository do
|
|
91
81
|
end
|
92
82
|
|
93
83
|
describe 'when valid' do
|
94
|
-
it { subject.must_equal
|
84
|
+
it { subject.must_equal expand_path(:local) }
|
95
85
|
end
|
96
86
|
end
|
97
87
|
|
@@ -129,7 +119,7 @@ describe Gitdocs::Repository do
|
|
129
119
|
end
|
130
120
|
|
131
121
|
describe 'has commits' do
|
132
|
-
before { @head_oid =
|
122
|
+
before { @head_oid = commit('touch_me', '') }
|
133
123
|
it { subject.must_equal @head_oid }
|
134
124
|
end
|
135
125
|
end
|
@@ -137,14 +127,6 @@ describe Gitdocs::Repository do
|
|
137
127
|
describe '#dirty?' do
|
138
128
|
subject { repository.dirty? }
|
139
129
|
|
140
|
-
let(:path_or_share) do
|
141
|
-
stub(
|
142
|
-
path: local_repo_path,
|
143
|
-
remote_name: 'origin',
|
144
|
-
branch_name: 'master'
|
145
|
-
)
|
146
|
-
end
|
147
|
-
|
148
130
|
describe 'when invalid' do
|
149
131
|
let(:path_or_share) { 'tmp/unit/missing' }
|
150
132
|
it { subject.must_equal false }
|
@@ -161,20 +143,20 @@ describe Gitdocs::Repository do
|
|
161
143
|
end
|
162
144
|
|
163
145
|
describe 'and new empty directory' do
|
164
|
-
before { mkdir('directory') }
|
146
|
+
before { GitFactory.mkdir(:local, 'directory') }
|
165
147
|
it { subject.must_equal true }
|
166
148
|
end
|
167
149
|
end
|
168
150
|
|
169
151
|
describe 'when commits exist' do
|
170
|
-
before {
|
152
|
+
before { commit('file1', 'foobar') }
|
171
153
|
|
172
154
|
describe 'and no changes' do
|
173
155
|
it { subject.must_equal false }
|
174
156
|
end
|
175
157
|
|
176
158
|
describe 'add empty directory' do
|
177
|
-
before { mkdir('directory') }
|
159
|
+
before { GitFactory.mkdir(:local, 'directory') }
|
178
160
|
it { subject.must_equal false }
|
179
161
|
end
|
180
162
|
|
@@ -189,7 +171,7 @@ describe Gitdocs::Repository do
|
|
189
171
|
end
|
190
172
|
|
191
173
|
describe 'delete file' do
|
192
|
-
before {
|
174
|
+
before { GitFactory.rm(:local, 'file1') }
|
193
175
|
it { subject.must_equal true }
|
194
176
|
end
|
195
177
|
end
|
@@ -198,14 +180,6 @@ describe Gitdocs::Repository do
|
|
198
180
|
describe '#need_sync' do
|
199
181
|
subject { repository.need_sync? }
|
200
182
|
|
201
|
-
let(:path_or_share) do
|
202
|
-
stub(
|
203
|
-
path: local_repo_path,
|
204
|
-
remote_name: 'origin',
|
205
|
-
branch_name: 'master'
|
206
|
-
)
|
207
|
-
end
|
208
|
-
|
209
183
|
describe 'when invalid' do
|
210
184
|
let(:path_or_share) { 'tmp/unit/missing' }
|
211
185
|
it { subject.must_equal false }
|
@@ -216,38 +190,33 @@ describe Gitdocs::Repository do
|
|
216
190
|
end
|
217
191
|
|
218
192
|
describe 'when no remote commits' do
|
219
|
-
before {
|
193
|
+
before { clone_remote }
|
220
194
|
|
221
195
|
describe 'no local commits' do
|
222
196
|
it { subject.must_equal false }
|
223
197
|
end
|
224
198
|
|
225
199
|
describe 'local commits' do
|
226
|
-
before {
|
200
|
+
before { commit('file1', 'beef') }
|
227
201
|
it { subject.must_equal true }
|
228
202
|
end
|
229
203
|
end
|
230
204
|
|
231
205
|
describe 'when remote commits' do
|
232
|
-
before {
|
206
|
+
before { clone_remote_with_commit }
|
233
207
|
|
234
208
|
describe 'no local commits' do
|
235
209
|
it { subject.must_equal false }
|
236
210
|
end
|
237
211
|
|
238
212
|
describe 'new local commit' do
|
239
|
-
before {
|
213
|
+
before { commit('file2', 'beef') }
|
240
214
|
it { subject.must_equal true }
|
241
215
|
end
|
242
216
|
|
243
217
|
describe 'new remote commit' do
|
244
218
|
before do
|
245
|
-
bare_commit(
|
246
|
-
remote_repo,
|
247
|
-
'file3', 'dead',
|
248
|
-
'second commit',
|
249
|
-
'author@example.com', 'A U Thor'
|
250
|
-
)
|
219
|
+
bare_commit('file3', 'dead')
|
251
220
|
repository.fetch
|
252
221
|
end
|
253
222
|
|
@@ -256,14 +225,9 @@ describe Gitdocs::Repository do
|
|
256
225
|
|
257
226
|
describe 'new local and remote commit' do
|
258
227
|
before do
|
259
|
-
bare_commit(
|
260
|
-
remote_repo,
|
261
|
-
'file3', 'dead',
|
262
|
-
'second commit',
|
263
|
-
'author@example.com', 'A U Thor'
|
264
|
-
)
|
228
|
+
bare_commit('file3', 'dead')
|
265
229
|
repository.fetch
|
266
|
-
|
230
|
+
commit('file4', 'beef')
|
267
231
|
end
|
268
232
|
|
269
233
|
it { subject.must_equal true }
|
@@ -278,7 +242,9 @@ describe Gitdocs::Repository do
|
|
278
242
|
|
279
243
|
describe 'timeout' do
|
280
244
|
before do
|
281
|
-
Grit::Repo
|
245
|
+
Grit::Repo
|
246
|
+
.any_instance
|
247
|
+
.stubs(:remote_fetch)
|
282
248
|
.raises(Grit::Git::GitTimeout.new)
|
283
249
|
end
|
284
250
|
it { subject ; @grep_result.must_equal([]) }
|
@@ -287,7 +253,9 @@ describe Gitdocs::Repository do
|
|
287
253
|
|
288
254
|
describe 'command failure' do
|
289
255
|
before do
|
290
|
-
Grit::Repo
|
256
|
+
Grit::Repo
|
257
|
+
.any_instance
|
258
|
+
.stubs(:remote_fetch)
|
291
259
|
.raises(Grit::Git::CommandFailed.new('', 1, 'grep error output'))
|
292
260
|
end
|
293
261
|
it { subject ; @grep_result.must_equal([]) }
|
@@ -296,10 +264,10 @@ describe Gitdocs::Repository do
|
|
296
264
|
|
297
265
|
describe 'success' do
|
298
266
|
before do
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
267
|
+
commit('file1', 'foo')
|
268
|
+
commit('file2', 'beef')
|
269
|
+
commit('file3', 'foobar')
|
270
|
+
commit('file4', "foo\ndead\nbeef\nfoobar")
|
303
271
|
end
|
304
272
|
it { subject ; @grep_result.must_equal(['file1 foo', 'file3 foobar', 'file4 foo', 'file4 foobar']) }
|
305
273
|
it { subject.must_equal("file1:foo\nfile3:foobar\nfile4:foo\nfile4:foobar\n") }
|
@@ -319,37 +287,45 @@ describe Gitdocs::Repository do
|
|
319
287
|
end
|
320
288
|
|
321
289
|
describe 'with remote' do
|
322
|
-
before {
|
290
|
+
before { clone_remote }
|
323
291
|
|
324
292
|
describe 'and times out' do
|
325
293
|
before do
|
326
|
-
Grit::Repo
|
294
|
+
Grit::Repo
|
295
|
+
.any_instance
|
296
|
+
.stubs(:remote_fetch)
|
327
297
|
.raises(Grit::Git::GitTimeout.new)
|
328
298
|
end
|
329
|
-
it
|
299
|
+
it do
|
300
|
+
assert_raises(
|
301
|
+
Gitdocs::Repository::FetchError,
|
302
|
+
"Fetch timed out for #{expand_path(:local)}"
|
303
|
+
) { subject }
|
304
|
+
end
|
330
305
|
end
|
331
306
|
|
332
307
|
describe 'and command fails' do
|
333
308
|
before do
|
334
|
-
Grit::Repo
|
309
|
+
Grit::Repo
|
310
|
+
.any_instance
|
311
|
+
.stubs(:remote_fetch)
|
335
312
|
.raises(Grit::Git::CommandFailed.new('', 1, 'fetch error output'))
|
336
313
|
end
|
337
|
-
it
|
314
|
+
it do
|
315
|
+
assert_raises(
|
316
|
+
Gitdocs::Repository::FetchError,
|
317
|
+
'fetch error output'
|
318
|
+
) { subject }
|
319
|
+
end
|
338
320
|
end
|
339
321
|
|
340
322
|
describe 'and success' do
|
341
|
-
before
|
342
|
-
bare_commit(
|
343
|
-
remote_repo,
|
344
|
-
'file1', 'deadbeef',
|
345
|
-
'commit', 'author@example.com', 'A U Thor'
|
346
|
-
)
|
347
|
-
end
|
323
|
+
before { bare_commit('file1', 'deadbeef') }
|
348
324
|
it { subject.must_equal :ok }
|
349
325
|
|
350
326
|
describe 'side effects' do
|
351
327
|
before { subject }
|
352
|
-
it {
|
328
|
+
it { GitInspector.remote_oid(:local).wont_be_nil }
|
353
329
|
end
|
354
330
|
end
|
355
331
|
end
|
@@ -358,13 +334,7 @@ describe Gitdocs::Repository do
|
|
358
334
|
describe '#merge' do
|
359
335
|
subject { repository.merge }
|
360
336
|
|
361
|
-
|
362
|
-
stub(
|
363
|
-
path: local_repo_path,
|
364
|
-
remote_name: 'origin',
|
365
|
-
branch_name: 'master'
|
366
|
-
)
|
367
|
-
end
|
337
|
+
before { repository.stubs(:author_count).returns(:author_counts) }
|
368
338
|
|
369
339
|
describe 'when invalid' do
|
370
340
|
let(:path_or_share) { 'tmp/unit/missing' }
|
@@ -376,52 +346,53 @@ describe Gitdocs::Repository do
|
|
376
346
|
end
|
377
347
|
|
378
348
|
describe 'has remote but nothing to merge' do
|
379
|
-
before {
|
349
|
+
before { clone_remote }
|
380
350
|
it { subject.must_equal :ok }
|
381
351
|
end
|
382
352
|
|
383
353
|
describe 'has remote and times out' do
|
384
354
|
before do
|
385
|
-
|
386
|
-
bare_commit(
|
387
|
-
remote_repo,
|
388
|
-
'file1', 'deadbeef',
|
389
|
-
'commit', 'author@example.com', 'A U Thor'
|
390
|
-
)
|
355
|
+
clone_remote
|
356
|
+
bare_commit('file1', 'deadbeef')
|
391
357
|
repository.fetch
|
392
358
|
|
393
|
-
Grit::Git
|
359
|
+
Grit::Git
|
360
|
+
.any_instance
|
361
|
+
.stubs(:merge)
|
394
362
|
.raises(Grit::Git::GitTimeout.new)
|
395
363
|
end
|
396
|
-
it
|
364
|
+
it do
|
365
|
+
assert_raises(
|
366
|
+
Gitdocs::Repository::MergeError,
|
367
|
+
"Merge timed out for #{expand_path(:local)}"
|
368
|
+
) { subject }
|
369
|
+
end
|
397
370
|
end
|
398
371
|
|
399
372
|
describe 'and fails, but does not conflict' do
|
400
373
|
before do
|
401
|
-
|
402
|
-
bare_commit(
|
403
|
-
remote_repo,
|
404
|
-
'file1', 'deadbeef',
|
405
|
-
'commit', 'author@example.com', 'A U Thor'
|
406
|
-
)
|
374
|
+
clone_remote
|
375
|
+
bare_commit('file1', 'deadbeef')
|
407
376
|
repository.fetch
|
408
377
|
|
409
|
-
Grit::Git
|
378
|
+
Grit::Git
|
379
|
+
.any_instance
|
380
|
+
.stubs(:merge)
|
410
381
|
.raises(Grit::Git::CommandFailed.new('', 1, 'merge error output'))
|
411
382
|
end
|
412
|
-
it
|
383
|
+
it do
|
384
|
+
assert_raises(
|
385
|
+
Gitdocs::Repository::MergeError,
|
386
|
+
'merge error output'
|
387
|
+
) { subject }
|
388
|
+
end
|
413
389
|
end
|
414
390
|
|
415
391
|
describe 'and there is a conflict' do
|
416
392
|
before do
|
417
|
-
|
418
|
-
bare_commit(
|
419
|
-
|
420
|
-
'file1', 'dead',
|
421
|
-
'second commit',
|
422
|
-
'author@example.com', 'A U Thor'
|
423
|
-
)
|
424
|
-
write_and_commit('file1', 'beef', 'conflict commit', author1)
|
393
|
+
clone_remote_with_commit
|
394
|
+
bare_commit('file1', 'dead')
|
395
|
+
commit('file1', 'beef')
|
425
396
|
repository.fetch
|
426
397
|
end
|
427
398
|
|
@@ -429,30 +400,20 @@ describe Gitdocs::Repository do
|
|
429
400
|
|
430
401
|
describe 'side effects' do
|
431
402
|
before { subject }
|
432
|
-
it { commit_count(
|
433
|
-
it {
|
434
|
-
it {
|
435
|
-
it {
|
436
|
-
it {
|
403
|
+
it { GitInspector.commit_count(:local).must_equal 2 }
|
404
|
+
it { GitInspector.file_count(:local).must_equal 3 }
|
405
|
+
it { GitInspector.file_content(:local, 'file1 (f6ea049 original)').must_equal 'foobar' }
|
406
|
+
it { GitInspector.file_content(:local, 'file1 (18ed963)').must_equal 'beef' }
|
407
|
+
it { GitInspector.file_content(:local, 'file1 (7bfce5c)').must_equal 'dead' }
|
437
408
|
end
|
438
409
|
end
|
439
410
|
|
440
411
|
describe 'and there is a conflict, with additional files' do
|
441
412
|
before do
|
442
|
-
|
443
|
-
bare_commit(
|
444
|
-
|
445
|
-
|
446
|
-
'second commit',
|
447
|
-
'author@example.com', 'A U Thor'
|
448
|
-
)
|
449
|
-
bare_commit(
|
450
|
-
remote_repo,
|
451
|
-
'file2', 'foo',
|
452
|
-
'second commit',
|
453
|
-
'author@example.com', 'A U Thor'
|
454
|
-
)
|
455
|
-
write_and_commit('file1', 'beef', 'conflict commit', author1)
|
413
|
+
clone_remote_with_commit
|
414
|
+
bare_commit('file1', 'dead')
|
415
|
+
bare_commit('file2', 'foo')
|
416
|
+
commit('file1', 'beef')
|
456
417
|
repository.fetch
|
457
418
|
end
|
458
419
|
|
@@ -460,46 +421,41 @@ describe Gitdocs::Repository do
|
|
460
421
|
|
461
422
|
describe 'side effects' do
|
462
423
|
before { subject }
|
463
|
-
it { commit_count(
|
464
|
-
it {
|
465
|
-
it {
|
466
|
-
it {
|
467
|
-
it {
|
424
|
+
it { GitInspector.commit_count(:local).must_equal 2 }
|
425
|
+
it { GitInspector.file_count(:local).must_equal 3 }
|
426
|
+
it { GitInspector.file_content(:local, 'file1 (f6ea049 original)').must_equal 'foobar' }
|
427
|
+
it { GitInspector.file_content(:local, 'file1 (18ed963)').must_equal 'beef' }
|
428
|
+
it { GitInspector.file_content(:local, 'file2').must_equal 'foo' }
|
468
429
|
end
|
469
430
|
end
|
470
431
|
|
471
432
|
describe 'and there are non-conflicted local commits' do
|
472
433
|
before do
|
473
|
-
|
474
|
-
|
434
|
+
clone_remote_with_commit
|
435
|
+
commit('file1', 'beef')
|
475
436
|
repository.fetch
|
476
437
|
end
|
477
|
-
it { subject.must_equal :
|
438
|
+
it { subject.must_equal :author_counts }
|
478
439
|
|
479
440
|
describe 'side effects' do
|
480
441
|
before { subject }
|
481
|
-
it {
|
482
|
-
it { commit_count(
|
442
|
+
it { GitInspector.file_count(:local).must_equal 1 }
|
443
|
+
it { GitInspector.commit_count(:local).must_equal 2 }
|
483
444
|
end
|
484
445
|
end
|
485
446
|
|
486
447
|
describe 'when new remote commits are merged' do
|
487
448
|
before do
|
488
|
-
|
489
|
-
bare_commit(
|
490
|
-
remote_repo,
|
491
|
-
'file2', 'deadbeef',
|
492
|
-
'second commit',
|
493
|
-
'author@example.com', 'A U Thor'
|
494
|
-
)
|
449
|
+
clone_remote_with_commit
|
450
|
+
bare_commit('file2', 'deadbeef')
|
495
451
|
repository.fetch
|
496
452
|
end
|
497
|
-
it { subject.must_equal :
|
453
|
+
it { subject.must_equal :author_counts }
|
498
454
|
|
499
455
|
describe 'side effects' do
|
500
456
|
before { subject }
|
501
|
-
it {
|
502
|
-
it { commit_count(
|
457
|
+
it { GitInspector.file_exist?(:local, 'file2').must_equal true }
|
458
|
+
it { GitInspector.commit_count(:local).must_equal 2 }
|
503
459
|
end
|
504
460
|
end
|
505
461
|
end
|
@@ -507,83 +463,24 @@ describe Gitdocs::Repository do
|
|
507
463
|
describe '#commit' do
|
508
464
|
subject { repository.commit }
|
509
465
|
|
510
|
-
let(:path_or_share) do
|
511
|
-
stub(
|
512
|
-
path: local_repo_path,
|
513
|
-
remote_name: 'origin',
|
514
|
-
branch_name: 'master'
|
515
|
-
)
|
516
|
-
end
|
517
|
-
|
518
466
|
describe 'when invalid' do
|
519
467
|
let(:path_or_share) { 'tmp/unit/missing' }
|
520
468
|
it { subject.must_be_nil }
|
521
469
|
end
|
522
470
|
|
523
|
-
|
524
|
-
|
525
|
-
describe 'no previous commits' do
|
526
|
-
describe 'nothing to commit' do
|
527
|
-
it { subject.must_equal false }
|
528
|
-
end
|
529
|
-
|
530
|
-
describe 'changes to commit' do
|
531
|
-
before do
|
532
|
-
write('file1', 'foobar')
|
533
|
-
mkdir('directory')
|
534
|
-
end
|
535
|
-
it { subject.must_equal true }
|
536
|
-
|
537
|
-
describe 'side effects' do
|
538
|
-
before { subject }
|
539
|
-
it { local_file_exist?('directory/.gitignore').must_equal true }
|
540
|
-
it { commit_count(local_repo).must_equal 1 }
|
541
|
-
it { head_commit(local_repo).message.must_equal "Auto-commit from gitdocs\n" }
|
542
|
-
it { local_repo_clean?.must_equal true }
|
543
|
-
end
|
544
|
-
end
|
545
|
-
end
|
546
|
-
|
547
|
-
describe 'previous commits' do
|
471
|
+
describe 'when valid' do
|
548
472
|
before do
|
549
|
-
|
550
|
-
|
551
|
-
end
|
552
|
-
|
553
|
-
describe 'nothing to commit' do
|
554
|
-
it { subject.must_equal false }
|
555
|
-
end
|
556
|
-
|
557
|
-
describe 'changes to commit' do
|
558
|
-
before do
|
559
|
-
write('file1', 'foobar')
|
560
|
-
rm_rf('file2')
|
561
|
-
write('file3', 'foobar')
|
562
|
-
mkdir('directory')
|
563
|
-
end
|
564
|
-
it { subject.must_equal true }
|
565
|
-
|
566
|
-
describe 'side effects' do
|
567
|
-
before { subject }
|
568
|
-
it { local_file_exist?('directory/.gitignore').must_equal true }
|
569
|
-
it { commit_count(local_repo).must_equal 3 }
|
570
|
-
it { head_commit(local_repo).message.must_equal "Auto-commit from gitdocs\n" }
|
571
|
-
it { local_repo_clean?.must_equal true }
|
572
|
-
end
|
473
|
+
Gitdocs::Repository::Committer.expects(:new).returns(committer = mock)
|
474
|
+
committer.expects(:commit).returns(:result)
|
573
475
|
end
|
476
|
+
it { subject.must_equal(:result) }
|
574
477
|
end
|
575
478
|
end
|
576
479
|
|
577
480
|
describe '#push' do
|
578
481
|
subject { repository.push }
|
579
482
|
|
580
|
-
|
581
|
-
stub(
|
582
|
-
path: local_repo_path,
|
583
|
-
remote_name: 'origin',
|
584
|
-
branch_name: 'master'
|
585
|
-
)
|
586
|
-
end
|
483
|
+
before { repository.stubs(:author_count).returns(:author_counts) }
|
587
484
|
|
588
485
|
describe 'when invalid' do
|
589
486
|
let(:path_or_share) { 'tmp/unit/missing' }
|
@@ -595,81 +492,85 @@ describe Gitdocs::Repository do
|
|
595
492
|
end
|
596
493
|
|
597
494
|
describe 'remote exists with no commits' do
|
598
|
-
before {
|
495
|
+
before { clone_remote }
|
599
496
|
|
600
497
|
describe 'and no local commits' do
|
601
498
|
it { subject.must_equal :nothing }
|
602
499
|
|
603
500
|
describe 'side effects' do
|
604
501
|
before { subject }
|
605
|
-
it { commit_count(
|
502
|
+
it { GitInspector.commit_count(:remote).must_equal 0 }
|
606
503
|
end
|
607
504
|
end
|
608
505
|
|
609
506
|
describe 'and a local commit' do
|
610
|
-
before {
|
507
|
+
before { commit('file2', 'foobar') }
|
611
508
|
|
612
509
|
describe 'and the push fails' do
|
613
510
|
# Simulate an error occurring during the push
|
614
511
|
before do
|
615
|
-
Grit::Git
|
512
|
+
Grit::Git
|
513
|
+
.any_instance
|
514
|
+
.stubs(:push)
|
616
515
|
.raises(Grit::Git::CommandFailed.new('', 1, 'error message'))
|
617
516
|
end
|
618
517
|
it { subject.must_equal 'error message' }
|
619
518
|
end
|
620
519
|
|
621
520
|
describe 'and the push succeeds' do
|
622
|
-
it { subject.must_equal :
|
521
|
+
it { subject.must_equal :author_counts }
|
623
522
|
|
624
523
|
describe 'side effects' do
|
625
524
|
before { subject }
|
626
|
-
it { commit_count(
|
525
|
+
it { GitInspector.commit_count(:remote).must_equal 1 }
|
627
526
|
end
|
628
527
|
end
|
629
528
|
end
|
630
529
|
end
|
631
530
|
|
632
531
|
describe 'remote exists with commits' do
|
633
|
-
before {
|
532
|
+
before { clone_remote_with_commit }
|
634
533
|
|
635
534
|
describe 'and no local commits' do
|
636
535
|
it { subject.must_equal :nothing }
|
637
536
|
|
638
537
|
describe 'side effects' do
|
639
538
|
before { subject }
|
640
|
-
it { commit_count(
|
539
|
+
it { GitInspector.commit_count(:remote).must_equal 1 }
|
641
540
|
end
|
642
541
|
end
|
643
542
|
|
644
543
|
describe 'and a local commit' do
|
645
|
-
before {
|
544
|
+
before { commit('file2', 'foobar') }
|
646
545
|
|
647
546
|
describe 'and the push fails' do
|
648
547
|
# Simulate an error occurring during the push
|
649
548
|
before do
|
650
|
-
Grit::Git
|
549
|
+
Grit::Git
|
550
|
+
.any_instance
|
551
|
+
.stubs(:push)
|
651
552
|
.raises(Grit::Git::CommandFailed.new('', 1, 'error message'))
|
652
553
|
end
|
653
554
|
it { subject.must_equal 'error message' }
|
654
555
|
end
|
655
556
|
|
656
557
|
describe 'and the push conflicts' do
|
657
|
-
before { bare_commit(
|
558
|
+
before { bare_commit('file2', 'dead') }
|
658
559
|
|
659
560
|
it { subject.must_equal :conflict }
|
660
561
|
|
661
562
|
describe 'side effects' do
|
662
563
|
before { subject }
|
663
|
-
it { commit_count(
|
564
|
+
it { GitInspector.commit_count(:remote).must_equal 2 }
|
664
565
|
end
|
665
566
|
end
|
666
567
|
|
667
568
|
describe 'and the push succeeds' do
|
668
|
-
it { subject.must_equal :
|
569
|
+
it { subject.must_equal :author_counts }
|
669
570
|
|
670
571
|
describe 'side effects' do
|
671
572
|
before { subject }
|
672
|
-
it { commit_count(
|
573
|
+
it { GitInspector.commit_count(:remote).must_equal 2 }
|
673
574
|
end
|
674
575
|
end
|
675
576
|
end
|
@@ -686,46 +587,99 @@ describe Gitdocs::Repository do
|
|
686
587
|
|
687
588
|
describe 'commits' do
|
688
589
|
before do
|
689
|
-
@intermediate_oid =
|
690
|
-
|
691
|
-
|
692
|
-
|
590
|
+
@intermediate_oid = commit('touch_me', 'first', 0)
|
591
|
+
commit('touch_me', 'second', 0)
|
592
|
+
commit('touch_me', 'third', 1)
|
593
|
+
commit('touch_me', 'fourth', 0)
|
693
594
|
end
|
694
595
|
|
695
596
|
describe 'all' do
|
696
597
|
let(:last_oid) { nil }
|
697
|
-
it { subject.must_equal(
|
598
|
+
it { subject.must_equal(GitFactory.authors[0] => 3, GitFactory.authors[1] => 1) }
|
698
599
|
end
|
699
600
|
|
700
601
|
describe 'some' do
|
701
602
|
let(:last_oid) { @intermediate_oid }
|
702
|
-
it { subject.must_equal(
|
603
|
+
it { subject.must_equal(GitFactory.authors[0] => 2, GitFactory.authors[1] => 1) }
|
703
604
|
end
|
704
605
|
|
705
606
|
describe 'missing oid' do
|
706
|
-
let(:last_oid) { 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
607
|
+
let(:last_oid) { 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' }
|
707
608
|
it { subject.must_equal({}) }
|
708
609
|
end
|
709
610
|
end
|
710
611
|
end
|
711
612
|
|
712
|
-
describe '#
|
713
|
-
subject { repository.
|
714
|
-
|
613
|
+
describe '#synchronize' do
|
614
|
+
subject { repository.synchronize(type) }
|
615
|
+
|
616
|
+
describe 'invalid repository' do
|
617
|
+
let(:type) { :noop }
|
618
|
+
it { subject.must_equal(merge: nil, push: nil) }
|
619
|
+
end
|
715
620
|
|
716
|
-
describe '
|
717
|
-
let(:
|
718
|
-
|
621
|
+
describe 'fetch only' do
|
622
|
+
let(:type) { 'fetch' }
|
623
|
+
|
624
|
+
describe 'fetch failure' do
|
625
|
+
before do
|
626
|
+
repository.expects(:fetch).raises(Gitdocs::Repository::FetchError)
|
627
|
+
end
|
628
|
+
it { subject.must_equal(merge: nil, push: nil) }
|
629
|
+
end
|
630
|
+
|
631
|
+
describe 'success' do
|
632
|
+
before { repository.expects(:fetch) }
|
633
|
+
it { subject.must_equal(merge: nil, push: nil) }
|
634
|
+
end
|
719
635
|
end
|
720
636
|
|
721
|
-
describe '
|
722
|
-
let(:
|
723
|
-
|
637
|
+
describe 'full' do
|
638
|
+
let(:type) { 'full' }
|
639
|
+
|
640
|
+
describe 'fetch failure' do
|
641
|
+
before do
|
642
|
+
repository.expects(:commit)
|
643
|
+
repository.expects(:fetch).raises(Gitdocs::Repository::FetchError)
|
644
|
+
end
|
645
|
+
it { subject.must_equal(merge: nil, push: nil) }
|
646
|
+
end
|
647
|
+
|
648
|
+
describe 'merge failure' do
|
649
|
+
before do
|
650
|
+
repository.expects(:commit)
|
651
|
+
repository.expects(:fetch)
|
652
|
+
repository.expects(:merge).raises(Gitdocs::Repository::MergeError, 'error')
|
653
|
+
end
|
654
|
+
it { subject.must_equal(merge: 'error', push: nil) }
|
655
|
+
end
|
656
|
+
|
657
|
+
describe 'success' do
|
658
|
+
before do
|
659
|
+
repository.expects(:commit)
|
660
|
+
repository.expects(:fetch)
|
661
|
+
repository.expects(:merge).returns('merge')
|
662
|
+
repository.expects(:push).returns('push')
|
663
|
+
end
|
664
|
+
it { subject.must_equal(merge: 'merge', push: 'push') }
|
665
|
+
end
|
724
666
|
end
|
667
|
+
end
|
725
668
|
|
726
|
-
|
727
|
-
|
728
|
-
|
669
|
+
describe '#write_commit_message' do
|
670
|
+
subject { repository.write_commit_message(:message) }
|
671
|
+
|
672
|
+
describe 'when invalid' do
|
673
|
+
let(:path_or_share) { 'tmp/unit/missing' }
|
674
|
+
it { subject.must_be_nil }
|
675
|
+
end
|
676
|
+
|
677
|
+
describe 'when valid' do
|
678
|
+
before do
|
679
|
+
Gitdocs::Repository::Committer.expects(:new).returns(committer = mock)
|
680
|
+
committer.expects(:write_commit_message).with(:message).returns(:result)
|
681
|
+
end
|
682
|
+
it { subject.must_equal(:result) }
|
729
683
|
end
|
730
684
|
end
|
731
685
|
|
@@ -733,10 +687,10 @@ describe Gitdocs::Repository do
|
|
733
687
|
subject { repository.commits_for('directory/file', 2) }
|
734
688
|
|
735
689
|
before do
|
736
|
-
|
737
|
-
|
738
|
-
@commit2 =
|
739
|
-
@commit3 =
|
690
|
+
commit('directory0/file0', '', 0)
|
691
|
+
commit('directory/file', 'foo', 0)
|
692
|
+
@commit2 = commit('directory/file', 'bar', 1)
|
693
|
+
@commit3 = commit('directory/file', 'beef', 1)
|
740
694
|
end
|
741
695
|
|
742
696
|
it { subject.map(&:oid).must_equal([@commit3, @commit2]) }
|
@@ -746,9 +700,9 @@ describe Gitdocs::Repository do
|
|
746
700
|
subject { repository.last_commit_for('directory/file') }
|
747
701
|
|
748
702
|
before do
|
749
|
-
|
750
|
-
|
751
|
-
@commit3 =
|
703
|
+
commit('directory/file', 'foo', 0)
|
704
|
+
commit('directory/file', 'bar', 1)
|
705
|
+
@commit3 = commit('directory/file', 'beef', 1)
|
752
706
|
end
|
753
707
|
|
754
708
|
it { subject.oid.must_equal(@commit3) }
|
@@ -758,9 +712,9 @@ describe Gitdocs::Repository do
|
|
758
712
|
subject { repository.blob_at('directory/file', @commit) }
|
759
713
|
|
760
714
|
before do
|
761
|
-
|
762
|
-
@commit =
|
763
|
-
|
715
|
+
commit('directory/file', 'foo')
|
716
|
+
@commit = commit('directory/file', 'bar', 1)
|
717
|
+
commit('directory/file', 'beef', 1)
|
764
718
|
end
|
765
719
|
|
766
720
|
it { subject.text.must_equal('bar') }
|
@@ -770,107 +724,40 @@ describe Gitdocs::Repository do
|
|
770
724
|
|
771
725
|
private
|
772
726
|
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
repo.config['user.name'] = 'Art T. Fish'
|
727
|
+
# @param (see GitFactory.expand_path)
|
728
|
+
# @return [String]
|
729
|
+
def expand_path(*args)
|
730
|
+
GitFactory.expand_path(*args)
|
778
731
|
end
|
779
732
|
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
'file1', 'foobar',
|
784
|
-
'initial commit',
|
785
|
-
'author@example.com', 'A U Thor'
|
786
|
-
)
|
787
|
-
FileUtils.rm_rf(local_repo_path)
|
788
|
-
repo = Rugged::Repository.clone_at(remote_repo.path, local_repo_path)
|
789
|
-
repo.config['user.email'] = 'afish@example.com'
|
790
|
-
repo.config['user.name'] = 'Art T. Fish'
|
733
|
+
# @return (see GitFactory.clone)
|
734
|
+
def clone_remote
|
735
|
+
GitFactory.clone(:remote, 'local')
|
791
736
|
end
|
792
737
|
|
793
|
-
|
794
|
-
|
738
|
+
# @return (see GitFactory.clone)
|
739
|
+
def clone_remote_with_commit
|
740
|
+
bare_commit('file1', 'foobar')
|
741
|
+
clone_remote
|
795
742
|
end
|
796
743
|
|
744
|
+
# @param [String] filename
|
745
|
+
# @param [String] content
|
746
|
+
#
|
747
|
+
# @return [void]
|
797
748
|
def write(filename, content)
|
798
|
-
|
799
|
-
File.write(File.join(local_repo_path, filename), content)
|
800
|
-
end
|
801
|
-
|
802
|
-
def rm_rf(filename)
|
803
|
-
FileUtils.rm_rf(File.join(local_repo_path, filename))
|
804
|
-
end
|
805
|
-
|
806
|
-
def write_and_commit(filename, content, commit_msg, author)
|
807
|
-
mkdir(File.dirname(filename))
|
808
|
-
File.write(File.join(local_repo_path, filename), content)
|
809
|
-
`cd #{local_repo_path} ; git add #{filename}; git commit -m '#{commit_msg}' --author='#{author}'`
|
810
|
-
`cd #{local_repo_path} ; git rev-parse HEAD`.strip
|
811
|
-
end
|
812
|
-
|
813
|
-
def bare_commit(repo, filename, content, message, email, name) # rubocop:disable ParameterLists
|
814
|
-
index = Rugged::Index.new
|
815
|
-
index.add(
|
816
|
-
path: filename,
|
817
|
-
oid: repo.write(content, :blob),
|
818
|
-
mode: 0100644
|
819
|
-
)
|
820
|
-
|
821
|
-
Rugged::Commit.create(
|
822
|
-
remote_repo,
|
823
|
-
tree: index.write_tree(repo),
|
824
|
-
author: { email: email, name: name, time: Time.now },
|
825
|
-
committer: { email: email, name: name, time: Time.now },
|
826
|
-
message: message,
|
827
|
-
parents: repo.empty? ? [] : [repo.head.target].compact,
|
828
|
-
update_ref: 'HEAD'
|
829
|
-
)
|
830
|
-
end
|
831
|
-
|
832
|
-
def commit_count(repo)
|
833
|
-
walker = Rugged::Walker.new(repo)
|
834
|
-
walker.push(repo.head.target)
|
835
|
-
walker.count
|
836
|
-
rescue Rugged::ReferenceError
|
837
|
-
# The repo does not have a head => no commits.
|
838
|
-
0
|
839
|
-
end
|
840
|
-
|
841
|
-
def head_commit(repo)
|
842
|
-
walker = Rugged::Walker.new(repo)
|
843
|
-
walker.push(repo.head.target)
|
844
|
-
walker.first
|
845
|
-
rescue Rugged::ReferenceError
|
846
|
-
# The repo does not have a head => no commits => no head commit.
|
847
|
-
nil
|
848
|
-
end
|
849
|
-
|
850
|
-
def head_tree_files(repo)
|
851
|
-
head_commit(repo).tree.map { |x| x[:name] }
|
852
|
-
end
|
853
|
-
|
854
|
-
# NOTE: This method is ignoring hidden files.
|
855
|
-
def local_file_count
|
856
|
-
files = Dir.chdir(local_repo_path) { Dir.glob('*') }
|
857
|
-
files.count
|
858
|
-
end
|
859
|
-
|
860
|
-
def local_repo_remote_branch
|
861
|
-
Rugged::Branch.lookup(local_repo, 'origin/master', :remote)
|
862
|
-
end
|
863
|
-
|
864
|
-
def local_repo_clean?
|
865
|
-
local_repo.diff_workdir(local_repo.head.target, include_untracked: true).deltas.empty?
|
749
|
+
GitFactory.write(:local, filename, content)
|
866
750
|
end
|
867
751
|
|
868
|
-
|
869
|
-
|
752
|
+
# @param (see GitFactory.commit)
|
753
|
+
# @return (see GitFactory.commit)
|
754
|
+
def commit(*args)
|
755
|
+
GitFactory.commit(:local, *args)
|
870
756
|
end
|
871
757
|
|
872
|
-
|
873
|
-
|
874
|
-
|
758
|
+
# @param (see GitFactory.bare_commit)
|
759
|
+
# @return (see GitFactory.bare_commit)
|
760
|
+
def bare_commit(*args)
|
761
|
+
GitFactory.bare_commit(:remote, *args)
|
875
762
|
end
|
876
763
|
end
|