git-fastclone 1.0.16 → 1.1.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 +4 -4
- data/lib/git-fastclone.rb +52 -11
- data/lib/git-fastclone/version.rb +2 -1
- data/spec/git_fastclone_runner_spec.rb +27 -2
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6a48b08fe2a5e4f22ce995ab3c7801c93fb0562f
|
4
|
+
data.tar.gz: 43cc697ec456e6d699415fee25977f824677aff6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6aea5e53d2b7c774fe5c03c6cfebff3eb95747281f18e52daa81685e4a298919d85bc288f39421d9079e1bf17351636edbb64b3fd41c7577b9ea958aca9b90e3
|
7
|
+
data.tar.gz: 465b9bd78a32f22994c584391a08d20277ff968913337e08ffdd6073f0ca9267eac0338f418aa39a45bbc39c0c32b7bfa4255ff3567b1064cccff879d7aa00d2
|
data/lib/git-fastclone.rb
CHANGED
@@ -16,6 +16,7 @@ require 'optparse'
|
|
16
16
|
require 'fileutils'
|
17
17
|
require 'logger'
|
18
18
|
require 'cocaine'
|
19
|
+
require 'timeout'
|
19
20
|
|
20
21
|
# Contains helper module UrlHelper and execution class GitFastClone::Runner
|
21
22
|
module GitFastClone
|
@@ -32,7 +33,7 @@ module GitFastClone
|
|
32
33
|
module_function :parse_update_info
|
33
34
|
|
34
35
|
def reference_repo_name(url)
|
35
|
-
|
36
|
+
url.gsub(%r{^.*://}, '').gsub(/^[^@]*@/, '').tr('/', '-').tr(':', '-').to_s
|
36
37
|
end
|
37
38
|
module_function :reference_repo_name
|
38
39
|
|
@@ -49,6 +50,12 @@ module GitFastClone
|
|
49
50
|
"#{reference_repo_dir(url, reference_dir, using_local_repo)}:submodules.txt"
|
50
51
|
end
|
51
52
|
module_function :reference_repo_submodule_file
|
53
|
+
|
54
|
+
def reference_repo_lock_file(url, reference_dir, using_local_repo)
|
55
|
+
lock_file_name = "#{reference_repo_dir(url, reference_dir, using_local_repo)}:lock"
|
56
|
+
File.open(lock_file_name, File::RDWR | File::CREAT, 0644)
|
57
|
+
end
|
58
|
+
module_function :reference_repo_lock_file
|
52
59
|
end
|
53
60
|
|
54
61
|
# Spawns one thread per submodule, and updates them in parallel. They will be
|
@@ -60,12 +67,13 @@ module GitFastClone
|
|
60
67
|
|
61
68
|
include GitFastClone::UrlHelper
|
62
69
|
|
63
|
-
DEFAULT_REFERENCE_REPO_DIR = '/var/tmp/git-fastclone/reference'
|
70
|
+
DEFAULT_REFERENCE_REPO_DIR = '/var/tmp/git-fastclone/reference'.freeze
|
64
71
|
|
65
|
-
DEFAULT_GIT_ALLOW_PROTOCOL = 'file:git:http:https:ssh'
|
72
|
+
DEFAULT_GIT_ALLOW_PROTOCOL = 'file:git:http:https:ssh'.freeze
|
66
73
|
|
67
|
-
attr_accessor :reference_dir, :prefetch_submodules, :
|
68
|
-
:options, :logger, :abs_clone_path, :using_local_repo, :verbose, :color
|
74
|
+
attr_accessor :reference_dir, :prefetch_submodules, :reference_updated, :reference_mutex,
|
75
|
+
:options, :logger, :abs_clone_path, :using_local_repo, :verbose, :color,
|
76
|
+
:flock_timeout_secs
|
69
77
|
|
70
78
|
def initialize
|
71
79
|
# Prefetch reference repos for submodules we've seen before
|
@@ -93,6 +101,8 @@ module GitFastClone
|
|
93
101
|
self.verbose = false
|
94
102
|
|
95
103
|
self.color = false
|
104
|
+
|
105
|
+
self.flock_timeout_secs = 0
|
96
106
|
end
|
97
107
|
|
98
108
|
def run
|
@@ -112,7 +122,7 @@ module GitFastClone
|
|
112
122
|
clone(url, options[:branch], path)
|
113
123
|
end
|
114
124
|
|
115
|
-
def
|
125
|
+
def parse_options
|
116
126
|
usage = 'Usage: git fastclone [options] <git-url> [path]'
|
117
127
|
|
118
128
|
# One option --branch=<branch> We're not as brittle as clone. That branch
|
@@ -137,7 +147,16 @@ module GitFastClone
|
|
137
147
|
opts.on('-c', '--color', 'Display colored output') do
|
138
148
|
self.color = true
|
139
149
|
end
|
150
|
+
|
151
|
+
opts.on('--lock-timeout N', 'Timeout in seconds to acquire a lock on any reference repo.
|
152
|
+
Default is 0 which waits indefinitely.') do |timeout_secs|
|
153
|
+
self.flock_timeout_secs = timeout_secs
|
154
|
+
end
|
140
155
|
end.parse!
|
156
|
+
end
|
157
|
+
|
158
|
+
def parse_inputs
|
159
|
+
parse_options
|
141
160
|
|
142
161
|
unless ARGV[0]
|
143
162
|
STDERR.puts usage
|
@@ -172,13 +191,15 @@ module GitFastClone
|
|
172
191
|
|
173
192
|
with_git_mirror(url) do |mirror|
|
174
193
|
Cocaine::CommandLine.new('git clone', '--quiet --reference :mirror :url :path')
|
175
|
-
.run(mirror:
|
194
|
+
.run(mirror: mirror.to_s,
|
195
|
+
url: url.to_s,
|
196
|
+
path: File.join(abs_clone_path, src_dir).to_s)
|
176
197
|
end
|
177
198
|
|
178
199
|
# Only checkout if we're changing branches to a non-default branch
|
179
200
|
if rev
|
180
201
|
Dir.chdir(File.join(abs_clone_path, src_dir)) do
|
181
|
-
Cocaine::CommandLine.new('git checkout', '--quiet :rev').run(rev:
|
202
|
+
Cocaine::CommandLine.new('git checkout', '--quiet :rev').run(rev: rev.to_s)
|
182
203
|
end
|
183
204
|
end
|
184
205
|
|
@@ -218,14 +239,34 @@ module GitFastClone
|
|
218
239
|
threads << Thread.new do
|
219
240
|
with_git_mirror(submodule_url) do |mirror|
|
220
241
|
Cocaine::CommandLine.new('cd', ':dir; git submodule update --quiet --reference :mirror :path')
|
221
|
-
.run(dir:
|
242
|
+
.run(dir: File.join(abs_clone_path, pwd).to_s,
|
243
|
+
mirror: mirror.to_s,
|
244
|
+
path: submodule_path.to_s)
|
222
245
|
end
|
223
246
|
|
224
247
|
update_submodules(File.join(pwd, submodule_path), submodule_url)
|
225
248
|
end
|
226
249
|
end
|
227
250
|
|
228
|
-
def with_reference_repo_lock(url)
|
251
|
+
def with_reference_repo_lock(url, &block)
|
252
|
+
# Sane POSIX implementations remove exclusive flocks when a process is terminated or killed
|
253
|
+
# We block here indefinitely. Waiting for other git-fastclone processes to release the lock.
|
254
|
+
# With the default timeout of 0 we will wait forever, this can be overridden on the command line.
|
255
|
+
lockfile = reference_repo_lock_file(url, reference_dir, using_local_repo)
|
256
|
+
Timeout.timeout(flock_timeout_secs) { lockfile.flock(File::LOCK_EX) }
|
257
|
+
with_reference_repo_thread_lock(url, &block)
|
258
|
+
ensure
|
259
|
+
# Not strictly necessary to do this unlock as an ensure. If ever exception is caught outside this
|
260
|
+
# primitive, ensure protection may come in handy.
|
261
|
+
lockfile.flock(File::LOCK_UN)
|
262
|
+
lockfile.close
|
263
|
+
end
|
264
|
+
|
265
|
+
def with_reference_repo_thread_lock(url)
|
266
|
+
# We also need thread level locking because pre-fetch means multiple threads can
|
267
|
+
# attempt to update the same repository from a single git-fastclone process
|
268
|
+
# file locks in posix are tracked per process, not per userland thread.
|
269
|
+
# This gives us the equivalent of pthread_mutex around these accesses.
|
229
270
|
reference_mutex[reference_repo_name(url)].synchronize do
|
230
271
|
yield
|
231
272
|
end
|
@@ -275,7 +316,7 @@ module GitFastClone
|
|
275
316
|
def store_updated_repo(url, mirror, repo_name, fail_hard)
|
276
317
|
unless Dir.exist?(mirror)
|
277
318
|
Cocaine::CommandLine.new('git clone', '--mirror :url :mirror')
|
278
|
-
.run(url:
|
319
|
+
.run(url: url.to_s, mirror: mirror.to_s)
|
279
320
|
end
|
280
321
|
|
281
322
|
Cocaine::CommandLine.new('cd', ':path; git remote update --prune').run(path: mirror)
|
@@ -22,6 +22,14 @@ describe GitFastClone::Runner do
|
|
22
22
|
let(:test_reference_repo_dir) { '/var/tmp/git-fastclone/reference/test_reference_dir' }
|
23
23
|
let(:placeholder_arg) { 'PH' }
|
24
24
|
|
25
|
+
let(:lockfile) do
|
26
|
+
lockfile = double
|
27
|
+
expect(lockfile).to receive(:flock).with(File::LOCK_EX).once
|
28
|
+
expect(lockfile).to receive(:flock).with(File::LOCK_UN).once
|
29
|
+
expect(lockfile).to receive(:close).once
|
30
|
+
lockfile
|
31
|
+
end
|
32
|
+
|
25
33
|
# Modified ARGV, watch out
|
26
34
|
ARGV = ['ssh://git@git.com/git-fastclone.git', 'test_reference_dir']
|
27
35
|
|
@@ -42,7 +50,7 @@ describe GitFastClone::Runner do
|
|
42
50
|
end
|
43
51
|
|
44
52
|
describe '.run' do
|
45
|
-
let(:options) { {:
|
53
|
+
let(:options) { { branch: placeholder_arg } }
|
46
54
|
|
47
55
|
it 'should run with the correct args' do
|
48
56
|
allow(subject).to receive(:parse_inputs) { [placeholder_arg, placeholder_arg, options] }
|
@@ -58,7 +66,7 @@ describe GitFastClone::Runner do
|
|
58
66
|
subject.options = {}
|
59
67
|
allow(FileUtils).to receive(:mkdir_p) {}
|
60
68
|
|
61
|
-
expect(subject.parse_inputs).to eq([test_url_valid, test_reference_dir, {:
|
69
|
+
expect(subject.parse_inputs).to eq([test_url_valid, test_reference_dir, { branch: nil }])
|
62
70
|
end
|
63
71
|
end
|
64
72
|
|
@@ -104,6 +112,7 @@ describe GitFastClone::Runner do
|
|
104
112
|
it 'should acquire a lock' do
|
105
113
|
allow(Mutex).to receive(:synchronize)
|
106
114
|
expect(Mutex).to respond_to(:synchronize)
|
115
|
+
expect(subject).to receive(:reference_repo_lock_file).and_return(lockfile)
|
107
116
|
|
108
117
|
subject.with_reference_repo_lock(test_url_valid) do
|
109
118
|
yielded << test_url_valid
|
@@ -111,6 +120,17 @@ describe GitFastClone::Runner do
|
|
111
120
|
|
112
121
|
expect(yielded).to eq([test_url_valid])
|
113
122
|
end
|
123
|
+
it 'should un-flock on thrown exception' do
|
124
|
+
allow(Mutex).to receive(:synchronize)
|
125
|
+
expect(Mutex).to respond_to(:synchronize)
|
126
|
+
expect(subject).to receive(:reference_repo_lock_file).and_return(lockfile)
|
127
|
+
|
128
|
+
expect do
|
129
|
+
subject.with_reference_repo_lock(test_url_valid) do
|
130
|
+
fail placeholder_arg
|
131
|
+
end
|
132
|
+
end.to raise_error(placeholder_arg)
|
133
|
+
end
|
114
134
|
end
|
115
135
|
|
116
136
|
describe '.update_submodule_reference' do
|
@@ -130,6 +150,7 @@ describe GitFastClone::Runner do
|
|
130
150
|
allow(subject).to receive(:reference_repo_name) {}
|
131
151
|
allow(subject).to receive(:reference_repo_submodule_file) {}
|
132
152
|
expect(File).to receive(:open)
|
153
|
+
expect(subject).to receive(:reference_repo_lock_file).and_return(lockfile)
|
133
154
|
|
134
155
|
subject.update_submodule_reference(placeholder_arg, [placeholder_arg, placeholder_arg])
|
135
156
|
end
|
@@ -141,6 +162,7 @@ describe GitFastClone::Runner do
|
|
141
162
|
it 'should grab the children immediately and then store' do
|
142
163
|
expect(subject).to receive(:prefetch).once
|
143
164
|
expect(subject).to receive(:store_updated_repo).once
|
165
|
+
expect(subject).to receive(:reference_repo_lock_file).and_return(lockfile)
|
144
166
|
|
145
167
|
allow(File).to receive(:exist?) { true }
|
146
168
|
subject.prefetch_submodules = true
|
@@ -153,6 +175,7 @@ describe GitFastClone::Runner do
|
|
153
175
|
it 'should store the updated repo' do
|
154
176
|
expect(subject).not_to receive(:prefetch)
|
155
177
|
expect(subject).to receive(:store_updated_repo).once
|
178
|
+
expect(subject).to receive(:reference_repo_lock_file).and_return(lockfile)
|
156
179
|
|
157
180
|
allow(File).to receive(:exist?) { true }
|
158
181
|
subject.prefetch_submodules = false
|
@@ -180,6 +203,7 @@ describe GitFastClone::Runner do
|
|
180
203
|
it 'should store' do
|
181
204
|
placeholder_hash[placeholder_arg] = false
|
182
205
|
expect(subject).to receive(:store_updated_repo)
|
206
|
+
expect(subject).to receive(:reference_repo_lock_file).and_return(lockfile)
|
183
207
|
|
184
208
|
allow(subject).to receive(:reference_repo_name) { placeholder_arg }
|
185
209
|
subject.reference_updated = placeholder_hash
|
@@ -244,6 +268,7 @@ describe GitFastClone::Runner do
|
|
244
268
|
it 'should yield properly' do
|
245
269
|
allow(subject).to receive(:update_reference_repo) {}
|
246
270
|
expect(subject).to receive(:reference_repo_dir)
|
271
|
+
expect(subject).to receive(:reference_repo_lock_file).and_return(lockfile)
|
247
272
|
|
248
273
|
subject.with_git_mirror(test_url_valid) do
|
249
274
|
yielded << test_url_valid
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: git-fastclone
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Tauraso
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-
|
12
|
+
date: 2016-03-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: cocaine
|
@@ -78,7 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
78
78
|
version: '0'
|
79
79
|
requirements: []
|
80
80
|
rubyforge_project:
|
81
|
-
rubygems_version: 2.
|
81
|
+
rubygems_version: 2.2.2
|
82
82
|
signing_key:
|
83
83
|
specification_version: 4
|
84
84
|
summary: git-clone --recursive on steroids!
|