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 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!