git-fastclone 1.0.16 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cfb5c1a85a5af61f83695373c9e087a382c7dbf0
4
- data.tar.gz: b07120a322ee4ca8ac13d0cd98c1251967a70744
3
+ metadata.gz: 6a48b08fe2a5e4f22ce995ab3c7801c93fb0562f
4
+ data.tar.gz: 43cc697ec456e6d699415fee25977f824677aff6
5
5
  SHA512:
6
- metadata.gz: 1b5cfe0a83e6232ae2475d284cfca371d840a0a950bcb4de30bca64f2d94f2a55362d242ac6ec8698c5b6d84304c08d1f1e58d990b892cd930faffabff4c89a5
7
- data.tar.gz: d5f62ae9d1e60ede4d3e51c991593d4b6874433f8f3c4c55abf17387d1ebdeff19fb9171449e134ba23cef5b5e664a6376fb3f54c5c590d31d1300b6b658c817
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
- "#{url.gsub(%r{^.*://}, '').gsub(/^[^@]*@/, '').tr('/', '-').tr(':', '-')}"
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, :reference_mutex, :reference_updated,
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 parse_inputs
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: "#{mirror}", url: "#{url}", path: "#{File.join(abs_clone_path, src_dir)}")
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: "#{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: "#{File.join(abs_clone_path, pwd)}", mirror: "#{mirror}", path: "#{submodule_path}")
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: "#{url}", mirror: "#{mirror}")
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)
@@ -1,3 +1,4 @@
1
+ # Version string for git-fastclone
1
2
  module GitFastCloneVersion
2
- VERSION = '1.0.16'
3
+ VERSION = '1.1.0'.freeze
3
4
  end
@@ -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) { {:branch => placeholder_arg} }
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, {:branch=>nil}])
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.16
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-01-28 00:00:00.000000000 Z
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.4.8
81
+ rubygems_version: 2.2.2
82
82
  signing_key:
83
83
  specification_version: 4
84
84
  summary: git-clone --recursive on steroids!