gitdocs 0.5.0.pre3 → 0.5.0.pre5
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 +8 -8
- data/CHANGELOG +12 -0
- data/lib/gitdocs/repository.rb +80 -52
- data/lib/gitdocs/version.rb +1 -1
- data/test/repository_test.rb +227 -29
- data/test/runner_test.rb +85 -113
- data/test/test_helper.rb +0 -50
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YzYxMWM5MTg3ZWYzMzllZDNhY2NiNDdlNTA2NmNmMWEyNTVjMzFkNQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
Njc0MTAxYjFlODI1ZjE5NjdmZGE0YmNmODc2MmJkMDc4NWQ5MzY5Ng==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NDFkODRiMDJjMGU3MWNkZGY4NjdhZWQ4OTM3N2EyNjRiNjM5MjAwYmQ5NWQ1
|
10
|
+
MTBlZTA5NGFkNWU1ODM2MWJhYmY1NWI0NDg3ZmVhMGI1YTBjODY0MTE1YjQ4
|
11
|
+
Njc1YjIyN2ZjMjAyN2M3YjZiNWM1OGE2ZDViMmZjOTgxZjkwMTY=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NzRjMzZmZDRhZjUzZDAyODNhMzRjNjFmNGRjZmViZmM5OTk1MzkzMWZmYzA2
|
14
|
+
ODMxMmNiY2U0NTVkOGEwODIyM2UxZTJkMTZiN2I2MWE0YzZkMjNjOTM4Mjkx
|
15
|
+
YjUzMzNjMTIzZmUzMzIzZGQ1ZDRkYjQ5OTQxODNiNzE0NTgyNzQ=
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
0.5.0.pre5 (May 14th 2014)
|
2
|
+
|
3
|
+
* Cleanup behavior on empty commits
|
4
|
+
* Improve pre-commit working directory change
|
5
|
+
* More rugged / grit conversions
|
6
|
+
* Update remote check to rugged
|
7
|
+
* Re-write empty directory tagging to use Find
|
8
|
+
|
9
|
+
0.5.0.pre4 (April 15th 2014)
|
10
|
+
|
11
|
+
* Fix a performance bug related to using Rugged for the "push" (@acant)
|
12
|
+
|
1
13
|
0.5.0.pre3 (3/30/2014)
|
2
14
|
|
3
15
|
* Eliminate sleep from sync integration test (@acant)
|
data/lib/gitdocs/repository.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
|
+
require 'find'
|
2
3
|
|
3
4
|
# Wrapper for accessing the shared git repositories.
|
4
|
-
# Rugged
|
5
|
+
# Rugged or Grit will be used, in that order of preference, depending
|
5
6
|
# upon the features which are available with each option.
|
6
7
|
#
|
7
8
|
# @note If a repository is invalid then query methods will return nil, and
|
8
9
|
# command methods will raise exceptions.
|
9
10
|
#
|
10
11
|
class Gitdocs::Repository
|
11
|
-
include ShellTools
|
12
12
|
attr_reader :invalid_reason
|
13
13
|
|
14
14
|
# Initialize the repository on the specified path. If the path is not valid
|
@@ -26,9 +26,10 @@ class Gitdocs::Repository
|
|
26
26
|
@branch_name = path_or_share.branch_name
|
27
27
|
end
|
28
28
|
|
29
|
-
@rugged
|
30
|
-
@grit
|
31
|
-
|
29
|
+
@rugged = Rugged::Repository.new(path)
|
30
|
+
@grit = Grit::Repo.new(path)
|
31
|
+
Grit::Git.git_timeout = 120
|
32
|
+
@invalid_reason = nil
|
32
33
|
rescue Rugged::OSError
|
33
34
|
@invalid_reason = :directory_missing
|
34
35
|
rescue Rugged::RepositoryError
|
@@ -129,6 +130,7 @@ class Gitdocs::Repository
|
|
129
130
|
Rugged::Branch.each_name(@rugged, :local).sort
|
130
131
|
end
|
131
132
|
|
133
|
+
# @return [nil] if there are no commits present
|
132
134
|
# @return [String] oid of the HEAD of the working directory
|
133
135
|
def current_oid
|
134
136
|
@rugged.head.target
|
@@ -150,33 +152,45 @@ class Gitdocs::Repository
|
|
150
152
|
return nil unless valid?
|
151
153
|
return :no_remote unless has_remote?
|
152
154
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
155
|
+
begin
|
156
|
+
@rugged.remotes.each { |x| @grit.remote_fetch(x.name) }
|
157
|
+
rescue Grit::Git::GitTimeout
|
158
|
+
return "Fetch timed out for #{root}"
|
159
|
+
rescue Grit::Git::CommandFailed => e
|
160
|
+
return e.err
|
161
|
+
end
|
162
|
+
|
163
|
+
return :ok if remote_branch.nil? || remote_branch.tip.oid == current_oid
|
164
|
+
|
165
|
+
@grit.git.merge({ raise: true, chdir: root }, "#{@remote_name}/#{@branch_name}")
|
166
|
+
:ok
|
167
|
+
rescue Grit::Git::GitTimeout
|
168
|
+
"Merged command timed out for #{root}"
|
169
|
+
rescue Grit::Git::CommandFailed => e
|
170
|
+
# HACK: The rugged in-memory index will not have been updated after the
|
171
|
+
# Grit merge command. Reload it before checking for conflicts.
|
172
|
+
@rugged.index.reload
|
173
|
+
return e.err unless @rugged.index.conflicts?
|
174
|
+
|
175
|
+
conflicted_paths = @rugged.index.map do |index_entry|
|
176
|
+
filename, extension = index_entry[:path].scan(/(.*?)(|\.[^\.]+)$/).first
|
177
|
+
|
178
|
+
author = ' original' if index_entry[:stage] == 1
|
179
|
+
short_oid = index_entry[:oid][0..6]
|
180
|
+
new_filename = "#{filename} (#{short_oid}#{author})#{extension}"
|
181
|
+
File.open(File.join(root, new_filename), 'wb') do |f|
|
182
|
+
f.write(Rugged::Blob.lookup(@rugged, index_entry[:oid]).content)
|
174
183
|
end
|
175
184
|
|
176
|
-
|
177
|
-
else
|
178
|
-
out # return the output on error
|
185
|
+
index_entry[:path]
|
179
186
|
end
|
187
|
+
|
188
|
+
conflicted_paths.uniq!
|
189
|
+
conflicted_paths.each { |path| FileUtils.remove(File.join(root, path), force: true) }
|
190
|
+
|
191
|
+
# NOTE: leave the commit until the next push
|
192
|
+
|
193
|
+
conflicted_paths
|
180
194
|
end
|
181
195
|
|
182
196
|
# Commit and push the repository
|
@@ -191,19 +205,34 @@ class Gitdocs::Repository
|
|
191
205
|
return :no_remote unless has_remote?
|
192
206
|
|
193
207
|
#add and commit
|
194
|
-
|
195
|
-
.
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
208
|
+
Find.find(root).each do |path|
|
209
|
+
Find.prune if File.basename(path) == '.git'
|
210
|
+
if File.directory?(path) && Dir.entries(path).count == 2
|
211
|
+
FileUtils.touch(File.join(path, '.gitignore'))
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
# Check if there are uncommitted changes
|
216
|
+
dirty =
|
217
|
+
if current_oid.nil?
|
218
|
+
Dir.glob(File.join(root, '*')).any?
|
219
|
+
else
|
220
|
+
@rugged.diff_workdir(current_oid, include_untracked: true).deltas.any?
|
221
|
+
end
|
222
|
+
|
223
|
+
# Commit any changes in the working directory.
|
224
|
+
if dirty
|
225
|
+
Dir.chdir(root) do
|
226
|
+
@rugged.index.add_all
|
227
|
+
@rugged.index.update_all
|
228
|
+
end
|
229
|
+
@rugged.index.write
|
230
|
+
@grit.commit_index(message)
|
200
231
|
end
|
201
|
-
@rugged.index.write
|
202
|
-
@grit.commit_index(message) if @rugged.index.count
|
203
232
|
|
204
|
-
|
233
|
+
return :nothing if current_oid.nil?
|
205
234
|
|
206
|
-
if last_synced_oid.nil? || remote_branch.nil? || remote_branch.tip.oid !=
|
235
|
+
if last_synced_oid.nil? || remote_branch.nil? || remote_branch.tip.oid != current_oid
|
207
236
|
begin
|
208
237
|
@grit.git.push({ raise: true }, @remote_name, @branch_name)
|
209
238
|
:ok
|
@@ -330,7 +359,18 @@ class Gitdocs::Repository
|
|
330
359
|
private
|
331
360
|
|
332
361
|
def has_remote?
|
333
|
-
|
362
|
+
@rugged.remotes.any?
|
363
|
+
end
|
364
|
+
|
365
|
+
# HACK: This will return nil if there are no commits in the remote branch.
|
366
|
+
# It is not the response that I would expect but it mostly gets the job
|
367
|
+
# done. This should probably be reviewed when upgrading to the next version
|
368
|
+
# of Rugged.
|
369
|
+
#
|
370
|
+
# @return [nil] if the remote branch does not exist
|
371
|
+
# @return [Rugged::Remote]
|
372
|
+
def remote_branch
|
373
|
+
Rugged::Branch.lookup(@rugged, "#{@remote_name}/#{@branch_name}", :remote)
|
334
374
|
end
|
335
375
|
|
336
376
|
def head_walker
|
@@ -339,16 +379,4 @@ class Gitdocs::Repository
|
|
339
379
|
walker.push(@rugged.head.target)
|
340
380
|
walker
|
341
381
|
end
|
342
|
-
|
343
|
-
# sh_string("git config branch.`git branch | grep '^\*' | sed -e 's/\* //'`.remote", "origin")
|
344
|
-
def sh_string(cmd, default = nil)
|
345
|
-
val = sh("cd #{root} ; #{cmd}").strip rescue nil
|
346
|
-
val.nil? || val.empty? ? default : val
|
347
|
-
end
|
348
|
-
|
349
|
-
# Run in shell, return both status and output
|
350
|
-
# @see #sh
|
351
|
-
def sh_with_code(cmd)
|
352
|
-
ShellTools.sh_with_code(cmd, root)
|
353
|
-
end
|
354
382
|
end
|
data/lib/gitdocs/version.rb
CHANGED
data/test/repository_test.rb
CHANGED
@@ -8,7 +8,7 @@ describe Gitdocs::Repository do
|
|
8
8
|
|
9
9
|
before do
|
10
10
|
FileUtils.rm_rf('tmp/unit')
|
11
|
-
|
11
|
+
mkdir
|
12
12
|
repo = Rugged::Repository.init_at(local_repo_path)
|
13
13
|
repo.config['user.email'] = 'afish@example.com'
|
14
14
|
repo.config['user.name'] = 'Art T. Fish'
|
@@ -21,7 +21,6 @@ describe Gitdocs::Repository do
|
|
21
21
|
let(:remote_repo) { Rugged::Repository.init_at('tmp/unit/remote', :bare) }
|
22
22
|
let(:local_repo) { Rugged::Repository.new(local_repo_path) }
|
23
23
|
|
24
|
-
|
25
24
|
describe 'initialize' do
|
26
25
|
subject { repository }
|
27
26
|
|
@@ -118,7 +117,6 @@ describe Gitdocs::Repository do
|
|
118
117
|
it { subject.must_equal [] }
|
119
118
|
end
|
120
119
|
|
121
|
-
|
122
120
|
describe 'term found' do
|
123
121
|
let(:term) { 'foo' }
|
124
122
|
before do
|
@@ -205,19 +203,55 @@ describe Gitdocs::Repository do
|
|
205
203
|
it { subject.must_equal :no_remote }
|
206
204
|
end
|
207
205
|
|
208
|
-
describe '
|
209
|
-
before
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
206
|
+
describe 'with a valid remote with no initial commits' do
|
207
|
+
before { create_local_repo_with_remote }
|
208
|
+
|
209
|
+
describe 'when there is an error fetching' do
|
210
|
+
before do
|
211
|
+
Grit::Repo.any_instance.stubs(:remote_fetch)
|
212
|
+
.raises(Grit::Git::CommandFailed.new('', 1, 'fetch error output'))
|
213
|
+
end
|
214
|
+
it { subject.must_equal 'fetch error output' }
|
215
|
+
end
|
216
|
+
|
217
|
+
describe 'when there are no local commits' do
|
218
|
+
it { subject.must_equal :ok }
|
219
|
+
end
|
220
|
+
|
221
|
+
describe 'when there is a local commit' do
|
222
|
+
before { write_and_commit('file1', 'beef', 'conflict commit', author1) }
|
223
|
+
it { subject.must_equal :ok }
|
224
|
+
it { subject ; commit_count(local_repo).must_equal 1 }
|
225
|
+
end
|
226
|
+
|
227
|
+
describe 'then new remote commits are fetched' do
|
228
|
+
before do
|
229
|
+
bare_commit(
|
230
|
+
remote_repo,
|
231
|
+
'file2', 'deadbeef',
|
232
|
+
'second commit',
|
233
|
+
'author@example.com', 'A U Thor'
|
234
|
+
)
|
235
|
+
end
|
236
|
+
|
237
|
+
describe 'when there is an error merging' do
|
238
|
+
before do
|
239
|
+
Grit::Git.any_instance.stubs(:merge)
|
240
|
+
.raises(Grit::Git::CommandFailed.new('', 1, 'merge error output'))
|
241
|
+
end
|
242
|
+
it { subject.must_equal 'merge error output' }
|
243
|
+
end
|
244
|
+
|
245
|
+
describe ' and merged' do
|
246
|
+
it { subject.must_equal :ok }
|
247
|
+
it { subject ; local_file_exist?('file2').must_equal true }
|
248
|
+
it { subject ; commit_count(local_repo).must_equal 1 }
|
249
|
+
end
|
215
250
|
end
|
216
|
-
it { subject.must_equal "Fetching origin\n" }
|
217
251
|
end
|
218
252
|
|
219
|
-
describe 'with a valid remote' do
|
220
|
-
before {
|
253
|
+
describe 'with a valid remote with commits' do
|
254
|
+
before { create_local_repo_with_remote_with_commit }
|
221
255
|
|
222
256
|
describe 'when there is nothing to pull' do
|
223
257
|
it { subject.must_equal :ok }
|
@@ -236,13 +270,19 @@ describe Gitdocs::Repository do
|
|
236
270
|
|
237
271
|
it { subject.must_equal ['file1'] }
|
238
272
|
it { subject ; commit_count(local_repo).must_equal 2 }
|
239
|
-
it { subject ;
|
240
|
-
it { subject ;
|
241
|
-
it { subject ;
|
242
|
-
it { subject ;
|
273
|
+
it { subject ; local_file_count.must_equal 3 }
|
274
|
+
it { subject ; local_file_content('file1 (f6ea049 original)').must_equal 'foobar' }
|
275
|
+
it { subject ; local_file_content('file1 (18ed963)').must_equal 'beef' }
|
276
|
+
it { subject ; local_file_content('file1 (7bfce5c)').must_equal 'dead' }
|
243
277
|
end
|
244
278
|
|
245
|
-
describe 'when
|
279
|
+
describe 'when there is a non-conflicted local commit' do
|
280
|
+
before { write_and_commit('file1', 'beef', 'conflict commit', author1) }
|
281
|
+
it { subject.must_equal :ok }
|
282
|
+
it { subject ; commit_count(local_repo).must_equal 2 }
|
283
|
+
end
|
284
|
+
|
285
|
+
describe 'when new remote commits are pulled and merged' do
|
246
286
|
before do
|
247
287
|
bare_commit(
|
248
288
|
remote_repo,
|
@@ -252,7 +292,7 @@ describe Gitdocs::Repository do
|
|
252
292
|
)
|
253
293
|
end
|
254
294
|
it { subject.must_equal :ok }
|
255
|
-
it { subject ;
|
295
|
+
it { subject ; local_file_exist?('file2').must_equal true }
|
256
296
|
it { subject ; commit_count(local_repo).must_equal 2 }
|
257
297
|
end
|
258
298
|
end
|
@@ -278,7 +318,7 @@ describe Gitdocs::Repository do
|
|
278
318
|
it { subject.must_equal :no_remote }
|
279
319
|
end
|
280
320
|
|
281
|
-
describe 'remote exists' do
|
321
|
+
describe 'remote exists with no commits' do
|
282
322
|
before { create_local_repo_with_remote }
|
283
323
|
|
284
324
|
describe 'last sync is nil' do
|
@@ -292,6 +332,116 @@ describe Gitdocs::Repository do
|
|
292
332
|
)
|
293
333
|
end
|
294
334
|
it { subject.must_equal :nothing }
|
335
|
+
it { subject ; commit_count(local_repo).must_equal 0 }
|
336
|
+
end
|
337
|
+
|
338
|
+
describe 'and there is a conflicted file to push' do
|
339
|
+
before do
|
340
|
+
bare_commit(remote_repo, 'file1', 'dead', 'commit', 'A U Thor', 'author@example.com')
|
341
|
+
write('file1', 'beef')
|
342
|
+
end
|
343
|
+
it { subject ; commit_count(local_repo).must_equal 1 }
|
344
|
+
it { subject ; commit_count(remote_repo).must_equal 1 }
|
345
|
+
it { subject.must_equal :nothing }
|
346
|
+
end
|
347
|
+
|
348
|
+
describe 'and there are empty directories to push' do
|
349
|
+
before do
|
350
|
+
mkdir('directory')
|
351
|
+
mkdir('.hidden_empty')
|
352
|
+
end
|
353
|
+
it { subject.must_equal :ok }
|
354
|
+
it { subject ; commit_count(local_repo).must_equal 1 }
|
355
|
+
it { subject ; commit_count(remote_repo).must_equal 1 }
|
356
|
+
it { subject ; head_commit(remote_repo).message.must_equal "message\n" }
|
357
|
+
it { subject ; head_tree_files(remote_repo).count.must_equal 2 }
|
358
|
+
it { subject ; head_tree_files(remote_repo).must_include 'directory' }
|
359
|
+
it { subject ; head_tree_files(remote_repo).must_include '.hidden_empty' }
|
360
|
+
it { subject ; Dir.glob("#{local_repo_path}/**/.gitignore", File::FNM_DOTMATCH).count.must_equal 2 }
|
361
|
+
end
|
362
|
+
|
363
|
+
describe 'and there is a directory with a hidden file' do
|
364
|
+
before do
|
365
|
+
mkdir('directory')
|
366
|
+
write('directory/.hidden', '')
|
367
|
+
end
|
368
|
+
it { subject.must_equal :ok }
|
369
|
+
it { subject ; local_file_exist?('directory', '.gitignore').must_equal false }
|
370
|
+
it { subject ; commit_count(local_repo).must_equal 1 }
|
371
|
+
it { subject ; commit_count(remote_repo).must_equal 1 }
|
372
|
+
it { subject ; head_commit(remote_repo).message.must_equal "message\n" }
|
373
|
+
it { subject ; head_tree_files(remote_repo).count.must_equal 1 }
|
374
|
+
it { subject ; head_tree_files(remote_repo).must_include 'directory' }
|
375
|
+
end
|
376
|
+
|
377
|
+
describe 'and there is a new file to push' do
|
378
|
+
before { write('file2', 'foobar') }
|
379
|
+
it { subject.must_equal :ok }
|
380
|
+
it { subject ; commit_count(local_repo).must_equal 1 }
|
381
|
+
it { subject ; commit_count(remote_repo).must_equal 1 }
|
382
|
+
it { subject ; head_commit(remote_repo).message.must_equal "message\n" }
|
383
|
+
it { subject ; head_tree_files(remote_repo).count.must_equal 1 }
|
384
|
+
it { subject ; head_tree_files(remote_repo).must_include 'file2' }
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
describe 'last sync is not nil' do
|
389
|
+
let(:last_oid) { 'oid' }
|
390
|
+
|
391
|
+
describe 'and this is an error on the push' do
|
392
|
+
before do
|
393
|
+
write('file2', 'foobar')
|
394
|
+
|
395
|
+
# Simulate an error occurring during the push
|
396
|
+
Grit::Git.any_instance.stubs(:push).raises(
|
397
|
+
Grit::Git::CommandFailed.new('', 1, 'error message')
|
398
|
+
)
|
399
|
+
end
|
400
|
+
it { subject.must_equal 'error message' }
|
401
|
+
end
|
402
|
+
|
403
|
+
describe 'and this is nothing to push' do
|
404
|
+
it { subject.must_equal :nothing }
|
405
|
+
it { subject ; commit_count(local_repo).must_equal 0 }
|
406
|
+
end
|
407
|
+
|
408
|
+
describe 'and there is a conflicted commit to push' do
|
409
|
+
before do
|
410
|
+
bare_commit(remote_repo, 'file1', 'dead', 'commit', 'A U Thor', 'author@example.com')
|
411
|
+
write('file1', 'beef')
|
412
|
+
end
|
413
|
+
it { subject ; commit_count(local_repo).must_equal 1 }
|
414
|
+
it { subject ; commit_count(remote_repo).must_equal 1 }
|
415
|
+
it { subject.must_equal :conflict }
|
416
|
+
end
|
417
|
+
|
418
|
+
describe 'and there is a commit to push' do
|
419
|
+
before { write('file2', 'foobar') }
|
420
|
+
it { subject.must_equal :ok }
|
421
|
+
it { subject ; commit_count(local_repo).must_equal 1 }
|
422
|
+
it { subject ; commit_count(remote_repo).must_equal 1 }
|
423
|
+
it { subject ; head_commit(remote_repo).message.must_equal "message\n" }
|
424
|
+
it { subject ; head_tree_files(remote_repo).count.must_equal 1 }
|
425
|
+
it { subject ; head_tree_files(remote_repo).must_include 'file2' }
|
426
|
+
end
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
describe 'remote exists' do
|
431
|
+
before { create_local_repo_with_remote_with_commit }
|
432
|
+
|
433
|
+
describe 'last sync is nil' do
|
434
|
+
let(:last_oid) { nil }
|
435
|
+
|
436
|
+
describe 'and there is an error on push' do
|
437
|
+
# Simulate an error occurring during the push
|
438
|
+
before do
|
439
|
+
Grit::Git.any_instance.stubs(:push).raises(
|
440
|
+
Grit::Git::CommandFailed.new('', 1, '')
|
441
|
+
)
|
442
|
+
end
|
443
|
+
it { subject.must_equal :nothing }
|
444
|
+
it { subject ; commit_count(local_repo).must_equal 1 }
|
295
445
|
end
|
296
446
|
|
297
447
|
describe 'and there is a conflicted file to push' do
|
@@ -304,9 +454,29 @@ describe Gitdocs::Repository do
|
|
304
454
|
it { subject.must_equal :nothing }
|
305
455
|
end
|
306
456
|
|
307
|
-
describe 'and there
|
308
|
-
before
|
457
|
+
describe 'and there are empty directories to push' do
|
458
|
+
before do
|
459
|
+
mkdir('directory')
|
460
|
+
mkdir('.hidden_empty')
|
461
|
+
end
|
462
|
+
it { subject.must_equal :ok }
|
463
|
+
it { subject ; commit_count(local_repo).must_equal 2 }
|
464
|
+
it { subject ; commit_count(remote_repo).must_equal 2 }
|
465
|
+
it { subject ; head_commit(remote_repo).message.must_equal "message\n" }
|
466
|
+
it { subject ; head_tree_files(remote_repo).count.must_equal 3 }
|
467
|
+
it { subject ; head_tree_files(remote_repo).must_include 'file1' }
|
468
|
+
it { subject ; head_tree_files(remote_repo).must_include 'directory' }
|
469
|
+
it { subject ; head_tree_files(remote_repo).must_include '.hidden_empty' }
|
470
|
+
it { subject ; Dir.glob("#{local_repo_path}/**/.gitignore", File::FNM_DOTMATCH).count.must_equal 2 }
|
471
|
+
end
|
472
|
+
|
473
|
+
describe 'and there is a directory with a hidden file' do
|
474
|
+
before do
|
475
|
+
mkdir('directory')
|
476
|
+
write('directory/.hidden', '')
|
477
|
+
end
|
309
478
|
it { subject.must_equal :ok }
|
479
|
+
it { subject ; local_file_exist?('directory', '.gitignore').must_equal false }
|
310
480
|
it { subject ; commit_count(local_repo).must_equal 2 }
|
311
481
|
it { subject ; commit_count(remote_repo).must_equal 2 }
|
312
482
|
it { subject ; head_commit(remote_repo).message.must_equal "message\n" }
|
@@ -354,6 +524,7 @@ describe Gitdocs::Repository do
|
|
354
524
|
|
355
525
|
describe 'and this is nothing to push' do
|
356
526
|
it { subject.must_equal :nothing }
|
527
|
+
it { subject ; commit_count(local_repo).must_equal 1 }
|
357
528
|
end
|
358
529
|
|
359
530
|
describe 'and there is a conflicted commit to push' do
|
@@ -505,20 +676,26 @@ describe Gitdocs::Repository do
|
|
505
676
|
|
506
677
|
describe 'file does not include the revision' do
|
507
678
|
let(:ref) { @commit0 }
|
508
|
-
it { subject ;
|
679
|
+
it { subject ; local_file_content('directory', 'file2').must_equal 'beef' }
|
509
680
|
end
|
510
681
|
|
511
682
|
describe 'file does include the revision' do
|
512
683
|
let(:ref) { @commit2 }
|
513
|
-
it { subject ;
|
684
|
+
it { subject ; local_file_content('directory', 'file2').must_equal "bar\n" }
|
514
685
|
end
|
515
686
|
end
|
516
687
|
|
517
688
|
##############################################################################
|
518
689
|
|
519
690
|
private
|
520
|
-
|
521
691
|
def create_local_repo_with_remote
|
692
|
+
FileUtils.rm_rf(local_repo_path)
|
693
|
+
repo = Rugged::Repository.clone_at(remote_repo.path, local_repo_path)
|
694
|
+
repo.config['user.email'] = 'afish@example.com'
|
695
|
+
repo.config['user.name'] = 'Art T. Fish'
|
696
|
+
end
|
697
|
+
|
698
|
+
def create_local_repo_with_remote_with_commit
|
522
699
|
bare_commit(
|
523
700
|
remote_repo,
|
524
701
|
'file1', 'foobar',
|
@@ -531,13 +708,17 @@ describe Gitdocs::Repository do
|
|
531
708
|
repo.config['user.name'] = 'Art T. Fish'
|
532
709
|
end
|
533
710
|
|
711
|
+
def mkdir(*path)
|
712
|
+
FileUtils.mkdir_p(File.join(local_repo_path, *path))
|
713
|
+
end
|
714
|
+
|
534
715
|
def write(filename, content)
|
535
|
-
|
716
|
+
mkdir(File.dirname(filename))
|
536
717
|
File.write(File.join(local_repo_path, filename), content)
|
537
718
|
end
|
538
719
|
|
539
720
|
def write_and_commit(filename, content, commit_msg, author)
|
540
|
-
|
721
|
+
mkdir(File.dirname(filename))
|
541
722
|
File.write(File.join(local_repo_path, filename), content)
|
542
723
|
`cd #{local_repo_path} ; git add #{filename}; git commit -m '#{commit_msg}' --author='#{author}'`
|
543
724
|
`cd #{local_repo_path} ; git rev-parse HEAD`.strip
|
@@ -565,21 +746,38 @@ describe Gitdocs::Repository do
|
|
565
746
|
walker = Rugged::Walker.new(repo)
|
566
747
|
walker.push(repo.head.target)
|
567
748
|
walker.count
|
749
|
+
rescue Rugged::ReferenceError
|
750
|
+
# The repo does not have a head => no commits.
|
751
|
+
0
|
568
752
|
end
|
569
753
|
|
570
754
|
def head_commit(repo)
|
571
755
|
walker = Rugged::Walker.new(repo)
|
572
756
|
walker.push(repo.head.target)
|
573
757
|
walker.first
|
758
|
+
rescue Rugged::ReferenceError
|
759
|
+
# The repo does not have a head => no commits => no head commit.
|
760
|
+
nil
|
574
761
|
end
|
575
762
|
|
576
763
|
def head_tree_files(repo)
|
577
764
|
head_commit(repo).tree.map { |x| x[:name] }
|
578
765
|
end
|
579
766
|
|
580
|
-
|
581
|
-
|
767
|
+
# NOTE: This method is ignoring hidden files.
|
768
|
+
def local_file_count
|
769
|
+
files = Dir.chdir(local_repo_path) do
|
582
770
|
Dir.glob('*')
|
583
771
|
end
|
772
|
+
files.count
|
773
|
+
end
|
774
|
+
|
775
|
+
def local_file_exist?(*path_elements)
|
776
|
+
File.exist?(File.join(local_repo_path, *path_elements))
|
777
|
+
end
|
778
|
+
|
779
|
+
def local_file_content(*path_elements)
|
780
|
+
local_file_exist?(*path_elements).must_equal true
|
781
|
+
File.read(File.join(local_repo_path, *path_elements))
|
584
782
|
end
|
585
783
|
end
|
data/test/runner_test.rb
CHANGED
@@ -1,142 +1,114 @@
|
|
1
1
|
require File.expand_path('../test_helper', __FILE__)
|
2
2
|
|
3
3
|
describe 'gitdocs runner' do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
assert_equal 'testing', File.read(File.join(clone3, 'test'))
|
14
|
-
end
|
15
|
-
end
|
4
|
+
let(:runner) { Gitdocs::Runner.new(share)}
|
5
|
+
|
6
|
+
let(:share) { stub(polling_interval: 1, notification: true) }
|
7
|
+
let(:notifier) { stub }
|
8
|
+
let(:repository) { stub(root: 'root_path') }
|
9
|
+
before do
|
10
|
+
Gitdocs::Notifier.stubs(:new).with(true).returns(notifier)
|
11
|
+
Gitdocs::Repository.stubs(:new).with(share).returns(repository)
|
12
|
+
end
|
16
13
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
sleep 3
|
21
|
-
File.open(File.join(clone1, 'test.txt'), 'w') { |f| f << "testing\n1" }
|
22
|
-
File.open(File.join(clone2, 'test.txt'), 'w') { |f| f << "testing\n2" }
|
23
|
-
sleep 3
|
24
|
-
assert_includes 2..3, Dir[File.join(clone2, '*.txt')].to_a.size
|
25
|
-
assert_includes 2..3, Dir[File.join(clone3, '*.txt')].to_a.size
|
26
|
-
end
|
27
|
-
end
|
14
|
+
describe '#root' do
|
15
|
+
subject { runner.root }
|
16
|
+
it { subject.must_equal 'root_path' }
|
28
17
|
end
|
29
18
|
|
30
|
-
describe '
|
31
|
-
|
19
|
+
describe '#sync_changes' do
|
20
|
+
subject { runner.sync_changes }
|
32
21
|
|
33
|
-
|
34
|
-
let(:notifier) { stub }
|
35
|
-
let(:repository) { stub(root: 'root_path') }
|
36
|
-
before do
|
37
|
-
Gitdocs::Notifier.stubs(:new).with(true).returns(notifier)
|
38
|
-
Gitdocs::Repository.stubs(:new).with(share).returns(repository)
|
39
|
-
end
|
22
|
+
before { repository.expects(:pull).returns(pull_result) }
|
40
23
|
|
41
|
-
describe '
|
42
|
-
|
43
|
-
it { subject.must_equal
|
24
|
+
describe 'when invalid' do
|
25
|
+
let(:pull_result) { nil }
|
26
|
+
it { subject.must_equal nil }
|
44
27
|
end
|
45
28
|
|
46
|
-
describe '
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
describe 'when invalid' do
|
52
|
-
let(:pull_result) { nil }
|
53
|
-
it { subject.must_equal nil }
|
54
|
-
end
|
55
|
-
|
56
|
-
describe 'when no remote present' do
|
57
|
-
let(:pull_result) { :no_remote }
|
58
|
-
it { subject.must_equal nil }
|
59
|
-
end
|
29
|
+
describe 'when no remote present' do
|
30
|
+
let(:pull_result) { :no_remote }
|
31
|
+
it { subject.must_equal nil }
|
32
|
+
end
|
60
33
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
end
|
70
|
-
it { subject.must_equal nil }
|
34
|
+
describe 'when merge is conflicted' do
|
35
|
+
let(:pull_result) { ['file'] }
|
36
|
+
before do
|
37
|
+
notifier.expects(:warn).with(
|
38
|
+
'There were some conflicts',
|
39
|
+
"* file"
|
40
|
+
)
|
41
|
+
runner.expects(:push_changes)
|
71
42
|
end
|
43
|
+
it { subject.must_equal nil }
|
44
|
+
end
|
72
45
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
end
|
85
|
-
it { subject.must_equal nil }
|
86
|
-
it { subject ; runner.instance_variable_get(:@last_synced_revision).must_equal :next_oid }
|
46
|
+
describe 'when merge is ok' do
|
47
|
+
let(:pull_result) { :ok }
|
48
|
+
before do
|
49
|
+
runner.instance_variable_set(:@last_synced_revision, :oid)
|
50
|
+
repository.stubs(:current_oid).returns(:next_oid)
|
51
|
+
repository.stubs(:author_count).with(:oid).returns('Alice' => 1, 'Bob' => 2)
|
52
|
+
notifier.expects(:info).with(
|
53
|
+
'Updated with 3 changes',
|
54
|
+
"In 'root_path':\n* Alice (1 change)\n* Bob (2 changes)"
|
55
|
+
)
|
56
|
+
runner.expects(:push_changes)
|
87
57
|
end
|
58
|
+
it { subject.must_equal nil }
|
59
|
+
it { subject ; runner.instance_variable_get(:@last_synced_revision).must_equal :next_oid }
|
88
60
|
end
|
61
|
+
end
|
89
62
|
|
90
|
-
|
91
|
-
|
63
|
+
describe '#push_changes' do
|
64
|
+
subject { runner.push_changes }
|
92
65
|
|
93
|
-
|
66
|
+
before { repository.expects(:push).returns(push_result) }
|
94
67
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
68
|
+
describe 'when invalid' do
|
69
|
+
let(:push_result) { nil }
|
70
|
+
it { subject.must_equal nil }
|
71
|
+
end
|
99
72
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
73
|
+
describe 'when no remote present' do
|
74
|
+
let(:push_result) { :no_remote }
|
75
|
+
it { subject.must_equal nil }
|
76
|
+
end
|
104
77
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
78
|
+
describe 'when nothing happened' do
|
79
|
+
let(:push_result) { :nothing }
|
80
|
+
it { subject.must_equal nil }
|
81
|
+
end
|
109
82
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
end
|
116
|
-
it { subject.must_equal nil }
|
83
|
+
describe 'when there is an error' do
|
84
|
+
let(:push_result) { 'error' }
|
85
|
+
before do
|
86
|
+
notifier.expects(:error)
|
87
|
+
.with('BAD Could not push changes in root_path', 'error')
|
117
88
|
end
|
89
|
+
it { subject.must_equal nil }
|
90
|
+
end
|
118
91
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
end
|
125
|
-
it { subject.must_equal nil }
|
92
|
+
describe 'when push is conflicted' do
|
93
|
+
let(:push_result) { :conflict }
|
94
|
+
before do
|
95
|
+
notifier.expects(:warn)
|
96
|
+
.with('There was a conflict in root_path, retrying', '')
|
126
97
|
end
|
98
|
+
it { subject.must_equal nil }
|
99
|
+
end
|
127
100
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
end
|
137
|
-
it { subject.must_equal nil }
|
138
|
-
it { subject ; runner.instance_variable_get(:@last_synced_revision).must_equal :next_oid }
|
101
|
+
describe 'when merge is ok' do
|
102
|
+
let(:push_result) { :ok }
|
103
|
+
before do
|
104
|
+
runner.instance_variable_set(:@last_synced_revision, :oid)
|
105
|
+
repository.stubs(:current_oid).returns(:next_oid)
|
106
|
+
repository.stubs(:author_count).with(:oid).returns('Alice' => 1, 'Bob' => 2)
|
107
|
+
notifier.expects(:info)
|
108
|
+
.with('Pushed 3 changes', "'root_path' has been pushed")
|
139
109
|
end
|
110
|
+
it { subject.must_equal nil }
|
111
|
+
it { subject ; runner.instance_variable_get(:@last_synced_revision).must_equal :next_oid }
|
140
112
|
end
|
141
113
|
end
|
142
114
|
end
|
data/test/test_helper.rb
CHANGED
@@ -2,54 +2,4 @@ require 'rubygems'
|
|
2
2
|
require 'minitest/autorun'
|
3
3
|
$LOAD_PATH.unshift File.expand_path('../../lib')
|
4
4
|
require 'gitdocs'
|
5
|
-
require 'fakeweb'
|
6
5
|
require 'mocha/setup'
|
7
|
-
|
8
|
-
FakeWeb.allow_net_connect = false
|
9
|
-
|
10
|
-
## Kernel Extensions
|
11
|
-
require 'stringio'
|
12
|
-
|
13
|
-
class MiniTest::Spec
|
14
|
-
def with_clones(count = 3)
|
15
|
-
FileUtils.rm_rf('/tmp/gitdocs')
|
16
|
-
master_path = '/tmp/gitdocs/master'
|
17
|
-
FileUtils.mkdir_p('/tmp/gitdocs/master')
|
18
|
-
ShellTools.capture { `git init /tmp/gitdocs/master --bare` }
|
19
|
-
sub_paths = count.times.map do |c|
|
20
|
-
ShellTools.capture { `cd /tmp/gitdocs && git clone file://#{master_path} #{c}` }
|
21
|
-
ShellTools.capture { `cd /tmp/gitdocs/#{c} && git config --local user.email "afish@example.com" && git config --local user.name "Art T. Fish"` }
|
22
|
-
conf_path = "/tmp/gitdocs/config/#{c}"
|
23
|
-
FileUtils.mkdir_p(conf_path)
|
24
|
-
["/tmp/gitdocs/#{c}", conf_path]
|
25
|
-
end
|
26
|
-
pids = sub_paths.map do |(path, conf_path)|
|
27
|
-
fork do
|
28
|
-
unless ENV['DEBUG']
|
29
|
-
STDOUT.reopen(File.open('/dev/null', 'w'))
|
30
|
-
STDERR.reopen(File.open('/dev/null', 'w'))
|
31
|
-
end
|
32
|
-
begin
|
33
|
-
puts 'RUNNING!'
|
34
|
-
Gitdocs.start(config_path: conf_path) do |conf|
|
35
|
-
conf.global.update_attributes(load_browser_on_startup: false, start_web_frontend: false)
|
36
|
-
conf.add_path(path, polling_interval: 0.1, notification: false)
|
37
|
-
end
|
38
|
-
rescue
|
39
|
-
puts 'RATHER BAD ~~~~~'
|
40
|
-
puts $ERROR_INFO.message
|
41
|
-
puts $ERROR_INFO.backtrace.join("\n ")
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
begin
|
47
|
-
sleep 1
|
48
|
-
yield sub_paths.map { |sp| sp.first }
|
49
|
-
ensure
|
50
|
-
pids.each { |pid| Process.kill('INT', pid) rescue nil }
|
51
|
-
end
|
52
|
-
ensure
|
53
|
-
FileUtils.rm_rf('/tmp/gitdocs') unless ENV['DEBUG']
|
54
|
-
end
|
55
|
-
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gitdocs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.0.
|
4
|
+
version: 0.5.0.pre5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Josh Hull
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-05-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: joshbuddy-guard
|