git_cache 0.1.2 → 0.2.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/CHANGELOG.md +11 -0
- data/lib/git_cache/{repo_lock.rb → repo_state.rb} +12 -9
- data/lib/git_cache/version.rb +1 -1
- data/lib/git_cache.rb +154 -64
- metadata +5 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bf5b975bb07d2180af37c7d22c0823d9266adb7993485bbb4f5708ab3b769421
|
|
4
|
+
data.tar.gz: d2ff0b8e4b2e41ad1f19e36b81bab3ba6a9b08483a0495a1d270065f6e15473f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 90401416dea171255778bb4372c3af86f0a7235195db3de596f5fdf60542e23538685a27dcaa6c82b3583f187b7f0608ada0b3e63978aefdc3dccfd4bbf03132
|
|
7
|
+
data.tar.gz: 2d58c482b5d55949e9eab5a06ef789aaa184a760c80185b78680562448e7eaf913b8a4e317374b0c0b1278777965a5e0aa106a4c65ae18b6d8cad81975353e6d
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Release History
|
|
2
2
|
|
|
3
|
+
### v0.2.0 / 2026-10-06
|
|
4
|
+
|
|
5
|
+
Note: This release changes the internal cache format from v1 to v2. This release will start with a new empty cache, but will not interfere with older versions and older caches.
|
|
6
|
+
|
|
7
|
+
* FIXED: Raise GitCache::Error when the cache directory cannot be created
|
|
8
|
+
* FIXED: Move repo locks out of the directories they protect
|
|
9
|
+
* FIXED: Store cache data in a format version subdirectory of custom cache dirs
|
|
10
|
+
* FIXED: Write repo state atomically
|
|
11
|
+
* FIXED: Record the remote even if the first get fails
|
|
12
|
+
* FIXED: Do not mask a failed operation's error with a state write error
|
|
13
|
+
|
|
3
14
|
### v0.1.2 / 2026-08-20
|
|
4
15
|
|
|
5
16
|
* FIXED: Prevent git auto maintenance from racing with cache removals
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
class GitCache
|
|
4
4
|
##
|
|
5
|
-
# Associated with each repo (remote) is a
|
|
6
|
-
#
|
|
7
|
-
# repo
|
|
5
|
+
# Associated with each repo (remote) is a state file, `state.json` in the
|
|
6
|
+
# repo's base dir, that saves the status of the cache. It is read and
|
|
7
|
+
# written only while holding the repo's lock, which is a separate file
|
|
8
|
+
# outside the base dir. This is handled by the lock_repo method.
|
|
8
9
|
#
|
|
9
10
|
# This object represents the state of the repo, and is made available to
|
|
10
11
|
# the block passed to lock_repo. It has the following schema:
|
|
@@ -23,16 +24,18 @@ class GitCache
|
|
|
23
24
|
#
|
|
24
25
|
# @private
|
|
25
26
|
#
|
|
26
|
-
class
|
|
27
|
+
class RepoState
|
|
27
28
|
##
|
|
28
29
|
# @private
|
|
29
30
|
#
|
|
30
|
-
def initialize(
|
|
31
|
-
@data = ::JSON.parse(
|
|
31
|
+
def initialize(json, remote, timestamp)
|
|
32
|
+
@data = ::JSON.parse(json) rescue {} # rubocop:disable Style/RescueModifier
|
|
33
|
+
# Record the remote if the state lacks it (e.g. a new repo), so the
|
|
34
|
+
# repo is listed by GitCache#remotes even if nothing else is recorded.
|
|
35
|
+
@modified = @data["remote"].nil? && !remote.nil?
|
|
32
36
|
@data["remote"] ||= remote
|
|
33
37
|
@data["refs"] ||= {}
|
|
34
38
|
@data["sources"] ||= {}
|
|
35
|
-
@modified = false
|
|
36
39
|
@timestamp = timestamp || ::Time.now.to_i
|
|
37
40
|
end
|
|
38
41
|
|
|
@@ -51,8 +54,8 @@ class GitCache
|
|
|
51
54
|
##
|
|
52
55
|
# @private
|
|
53
56
|
#
|
|
54
|
-
def dump
|
|
55
|
-
::JSON.dump(@data
|
|
57
|
+
def dump
|
|
58
|
+
::JSON.dump(@data)
|
|
56
59
|
end
|
|
57
60
|
|
|
58
61
|
##
|
data/lib/git_cache/version.rb
CHANGED
data/lib/git_cache.rb
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "git_cache/error"
|
|
4
4
|
require "git_cache/repo_info"
|
|
5
|
-
require "git_cache/
|
|
5
|
+
require "git_cache/repo_state"
|
|
6
6
|
|
|
7
7
|
##
|
|
8
8
|
# This object provides cached access to remote git data. Given a remote
|
|
@@ -15,7 +15,9 @@ class GitCache
|
|
|
15
15
|
# Access a git cache.
|
|
16
16
|
#
|
|
17
17
|
# @param cache_dir [String] The path to the cache directory. Defaults to
|
|
18
|
-
# a specific directory in the user's XDG cache.
|
|
18
|
+
# a specific directory in the user's XDG cache. Cache data is stored
|
|
19
|
+
# in a subdirectory named for the cache format version, so that clients
|
|
20
|
+
# using incompatible formats can share a cache directory safely.
|
|
19
21
|
#
|
|
20
22
|
def initialize(cache_dir: nil)
|
|
21
23
|
require "digest"
|
|
@@ -23,7 +25,9 @@ class GitCache
|
|
|
23
25
|
require "json"
|
|
24
26
|
require "securerandom"
|
|
25
27
|
require "exec_service"
|
|
28
|
+
@using_default_cache_dir = cache_dir.nil?
|
|
26
29
|
@cache_dir = ::File.expand_path(cache_dir || default_cache_dir)
|
|
30
|
+
@data_dir = ::File.join(@cache_dir, FORMAT_VERSION)
|
|
27
31
|
@exec = ::ExecService.new(out: :capture, err: :capture)
|
|
28
32
|
end
|
|
29
33
|
|
|
@@ -74,14 +78,15 @@ class GitCache
|
|
|
74
78
|
path = ::GitCache.normalize_path(path)
|
|
75
79
|
commit ||= "HEAD"
|
|
76
80
|
timestamp ||= ::Time.now.to_i
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
name = ::GitCache.remote_dir_name(remote)
|
|
82
|
+
dir = repo_base_dir_for(name)
|
|
83
|
+
lock_repo(name, remote, timestamp, create: true) do |repo_state|
|
|
79
84
|
ensure_repo(dir, remote)
|
|
80
|
-
sha = ensure_commit(dir, commit,
|
|
85
|
+
sha = ensure_commit(dir, commit, repo_state, update)
|
|
81
86
|
if into
|
|
82
|
-
copy_files(dir, sha, path,
|
|
87
|
+
copy_files(dir, sha, path, repo_state, into)
|
|
83
88
|
else
|
|
84
|
-
ensure_source(dir, sha, path,
|
|
89
|
+
ensure_source(dir, sha, path, repo_state)
|
|
85
90
|
end
|
|
86
91
|
end
|
|
87
92
|
end
|
|
@@ -94,14 +99,13 @@ class GitCache
|
|
|
94
99
|
#
|
|
95
100
|
def remotes
|
|
96
101
|
result = []
|
|
97
|
-
|
|
98
|
-
::
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
end
|
|
102
|
+
repos_dir = ::File.join(@data_dir, REPOS_DIR_NAME)
|
|
103
|
+
return result unless ::File.directory?(repos_dir)
|
|
104
|
+
::Dir.children(repos_dir).each do |name|
|
|
105
|
+
next if name.start_with?(".")
|
|
106
|
+
next unless ::File.file?(::File.join(repos_dir, name, STATE_FILE_NAME))
|
|
107
|
+
remote = lock_repo(name, &:remote)
|
|
108
|
+
result << remote if remote
|
|
105
109
|
end
|
|
106
110
|
result.sort
|
|
107
111
|
end
|
|
@@ -114,10 +118,11 @@ class GitCache
|
|
|
114
118
|
# @return [RepoInfo,nil]
|
|
115
119
|
#
|
|
116
120
|
def repo_info(remote)
|
|
117
|
-
|
|
121
|
+
name = ::GitCache.remote_dir_name(remote)
|
|
122
|
+
dir = repo_base_dir_for(name)
|
|
118
123
|
return nil unless ::File.directory?(dir)
|
|
119
|
-
lock_repo(
|
|
120
|
-
RepoInfo.new(dir,
|
|
124
|
+
lock_repo(name, remote) do |repo_state|
|
|
125
|
+
RepoInfo.new(dir, repo_state.data)
|
|
121
126
|
end
|
|
122
127
|
end
|
|
123
128
|
|
|
@@ -129,8 +134,9 @@ class GitCache
|
|
|
129
134
|
# repositories are requested, they will be reloaded from the remote
|
|
130
135
|
# repository from scratch.
|
|
131
136
|
#
|
|
132
|
-
#
|
|
133
|
-
#
|
|
137
|
+
# This waits for any in-progress {#get} calls for these repos to finish.
|
|
138
|
+
# However, be careful not to remove repos whose shared sources are
|
|
139
|
+
# currently in use by other GitCache clients.
|
|
134
140
|
#
|
|
135
141
|
# @param remotes [Array<String>,:all,nil] The remotes to remove. If set
|
|
136
142
|
# to :all or nil, removes all repos.
|
|
@@ -139,8 +145,14 @@ class GitCache
|
|
|
139
145
|
def remove_repos(remotes)
|
|
140
146
|
remotes = self.remotes if remotes.nil? || remotes == :all
|
|
141
147
|
Array(remotes).map do |remote|
|
|
142
|
-
|
|
143
|
-
|
|
148
|
+
name = ::GitCache.remote_dir_name(remote)
|
|
149
|
+
dir = repo_base_dir_for(name)
|
|
150
|
+
next unless ::File.directory?(dir)
|
|
151
|
+
# Take the lock so we wait for any in-flight operation on this repo.
|
|
152
|
+
# The lock file lives outside the directory being removed, so it keeps
|
|
153
|
+
# excluding later clients after the removal.
|
|
154
|
+
flock_repo(name) do
|
|
155
|
+
next unless ::File.directory?(dir)
|
|
144
156
|
remove_dir(dir)
|
|
145
157
|
remote
|
|
146
158
|
end
|
|
@@ -161,17 +173,17 @@ class GitCache
|
|
|
161
173
|
# the given repo is not in the cache.
|
|
162
174
|
#
|
|
163
175
|
def remove_refs(remote, refs: nil)
|
|
164
|
-
|
|
165
|
-
return nil unless ::File.directory?(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
refs =
|
|
176
|
+
name = ::GitCache.remote_dir_name(remote)
|
|
177
|
+
return nil unless ::File.directory?(repo_base_dir_for(name))
|
|
178
|
+
lock_repo(name, remote) do |repo_state|
|
|
179
|
+
results = []
|
|
180
|
+
refs = repo_state.refs if refs.nil? || refs == :all
|
|
169
181
|
Array(refs).each do |ref|
|
|
170
|
-
ref_data =
|
|
182
|
+
ref_data = repo_state.delete_ref!(ref)
|
|
171
183
|
results << RefInfo.new(ref, ref_data) if ref_data
|
|
172
184
|
end
|
|
185
|
+
results.sort
|
|
173
186
|
end
|
|
174
|
-
results.sort
|
|
175
187
|
end
|
|
176
188
|
|
|
177
189
|
##
|
|
@@ -191,30 +203,49 @@ class GitCache
|
|
|
191
203
|
# if the given repo is not in the cache.
|
|
192
204
|
#
|
|
193
205
|
def remove_sources(remote, commits: nil)
|
|
194
|
-
|
|
206
|
+
name = ::GitCache.remote_dir_name(remote)
|
|
207
|
+
dir = repo_base_dir_for(name)
|
|
195
208
|
return nil unless ::File.directory?(dir)
|
|
196
|
-
|
|
197
|
-
|
|
209
|
+
lock_repo(name, remote) do |repo_state|
|
|
210
|
+
results = []
|
|
198
211
|
commits = nil if commits == :all
|
|
199
|
-
shas = Array(commits).map { |ref|
|
|
200
|
-
|
|
201
|
-
data =
|
|
212
|
+
shas = Array(commits).map { |ref| repo_state.lookup_ref(ref) }.compact.uniq if commits
|
|
213
|
+
repo_state.find_sources(shas: shas).each do |(sha, path)|
|
|
214
|
+
data = repo_state.delete_source!(sha, path)
|
|
202
215
|
results << SourceInfo.new(dir, sha, path, data)
|
|
203
216
|
end
|
|
204
217
|
results.map(&:sha).uniq.each do |sha|
|
|
205
|
-
unless
|
|
218
|
+
unless repo_state.source_exists?(sha)
|
|
206
219
|
remove_dir(::File.join(dir, sha))
|
|
207
220
|
end
|
|
208
221
|
end
|
|
222
|
+
results.sort
|
|
209
223
|
end
|
|
210
|
-
results.sort
|
|
211
224
|
end
|
|
212
225
|
|
|
213
226
|
private
|
|
214
227
|
|
|
215
|
-
|
|
228
|
+
# Cache layout, relative to the cache directory:
|
|
229
|
+
#
|
|
230
|
+
# <FORMAT_VERSION>/ Data dir. Bumping FORMAT_VERSION on
|
|
231
|
+
# incompatible layout changes isolates clients
|
|
232
|
+
# using different formats.
|
|
233
|
+
# locks/<name>.lock Lock file for the repo. Empty; used only as a
|
|
234
|
+
# flock target. Never deleted (see flock_repo).
|
|
235
|
+
# repos/<name>/ Base dir for the repo. Removing it (via a
|
|
236
|
+
# rename) removes the repo from the cache.
|
|
237
|
+
# state.json Repo state (see RepoState).
|
|
238
|
+
# repo/ Working clone of the remote.
|
|
239
|
+
# <sha>/ Shared sources for a commit.
|
|
240
|
+
#
|
|
241
|
+
# where <name> is the remote_dir_name of the remote.
|
|
242
|
+
#
|
|
243
|
+
FORMAT_VERSION = "v2"
|
|
244
|
+
LOCKS_DIR_NAME = "locks"
|
|
245
|
+
REPOS_DIR_NAME = "repos"
|
|
246
|
+
LOCK_FILE_SUFFIX = ".lock"
|
|
247
|
+
STATE_FILE_NAME = "state.json"
|
|
216
248
|
REPO_DIR_NAME = "repo"
|
|
217
|
-
LOCK_FILE_NAME = "repo.lock"
|
|
218
249
|
TRASH_DIR_PREFIX = ".trash-"
|
|
219
250
|
|
|
220
251
|
# Config applied to every git invocation. Auto maintenance would otherwise
|
|
@@ -226,16 +257,23 @@ class GitCache
|
|
|
226
257
|
# keys they do not recognize.
|
|
227
258
|
GIT_CONFIG_ARGS = ["-c", "maintenance.auto=false"].freeze
|
|
228
259
|
|
|
229
|
-
private_constant :
|
|
260
|
+
private_constant :FORMAT_VERSION, :LOCKS_DIR_NAME, :REPOS_DIR_NAME,
|
|
261
|
+
:LOCK_FILE_SUFFIX, :STATE_FILE_NAME, :REPO_DIR_NAME,
|
|
230
262
|
:TRASH_DIR_PREFIX, :GIT_CONFIG_ARGS
|
|
231
263
|
|
|
232
|
-
|
|
233
|
-
|
|
264
|
+
# Takes the remote_dir_name of a remote
|
|
265
|
+
def repo_base_dir_for(name)
|
|
266
|
+
::File.join(@data_dir, REPOS_DIR_NAME, name)
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# Takes the remote_dir_name of a remote
|
|
270
|
+
def repo_lock_path_for(name)
|
|
271
|
+
::File.join(@data_dir, LOCKS_DIR_NAME, "#{name}#{LOCK_FILE_SUFFIX}")
|
|
234
272
|
end
|
|
235
273
|
|
|
236
274
|
def default_cache_dir
|
|
237
275
|
require "simple_xdg"
|
|
238
|
-
::File.join(::SimpleXDG.new.cache_home, "git-cache"
|
|
276
|
+
::File.join(::SimpleXDG.new.cache_home, "git-cache")
|
|
239
277
|
end
|
|
240
278
|
|
|
241
279
|
def git(dir, cmd, error_message: nil)
|
|
@@ -305,30 +343,82 @@ class GitCache
|
|
|
305
343
|
nil
|
|
306
344
|
end
|
|
307
345
|
|
|
308
|
-
def
|
|
309
|
-
dir = repo_base_dir_for(remote)
|
|
346
|
+
def ensure_cache_subdir(dir)
|
|
310
347
|
::FileUtils.mkdir_p(dir)
|
|
311
|
-
|
|
348
|
+
rescue ::SystemCallError => e
|
|
349
|
+
message = "Unable to create git cache directory #{dir}: #{e.message}"
|
|
350
|
+
message += ". Set XDG_CACHE_HOME to a writable directory." if @using_default_cache_dir
|
|
351
|
+
raise Error, message
|
|
312
352
|
end
|
|
313
353
|
|
|
314
|
-
|
|
315
|
-
|
|
354
|
+
# Takes an exclusive lock on the given repo for the duration of the block,
|
|
355
|
+
# and returns the value of the block. Takes the remote_dir_name of a remote.
|
|
356
|
+
#
|
|
357
|
+
# The lock file lives outside the repo's base dir, so that removing the
|
|
358
|
+
# base dir does not also remove the lock. Lock files must never be deleted:
|
|
359
|
+
# a flock belongs to an inode, so if the file were deleted, a newcomer
|
|
360
|
+
# would create a new one and "acquire" it while an older client still
|
|
361
|
+
# holds the lock on the old one.
|
|
362
|
+
#
|
|
363
|
+
def flock_repo(name)
|
|
364
|
+
lock_path = repo_lock_path_for(name)
|
|
365
|
+
ensure_cache_subdir(::File.dirname(lock_path))
|
|
316
366
|
::File.open(lock_path, ::File::RDWR | ::File::CREAT) do |file|
|
|
317
367
|
file.flock(::File::LOCK_EX)
|
|
318
|
-
|
|
319
|
-
|
|
368
|
+
yield
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
# Takes an exclusive lock on the given repo, and yields its state as a
|
|
373
|
+
# {RepoState}, writing the state back afterward if it was modified. Returns
|
|
374
|
+
# the value of the block. Takes the remote_dir_name of a remote.
|
|
375
|
+
#
|
|
376
|
+
# If create is true, creates the repo's base dir if it does not exist.
|
|
377
|
+
# Otherwise, if the base dir does not exist (e.g. because it was removed
|
|
378
|
+
# while we were waiting for the lock), returns nil without yielding.
|
|
379
|
+
#
|
|
380
|
+
def lock_repo(name, remote = nil, timestamp = nil, create: false)
|
|
381
|
+
flock_repo(name) do
|
|
382
|
+
dir = repo_base_dir_for(name)
|
|
383
|
+
if create
|
|
384
|
+
ensure_cache_subdir(dir)
|
|
385
|
+
elsif !::File.directory?(dir)
|
|
386
|
+
next nil
|
|
387
|
+
end
|
|
388
|
+
state_path = ::File.join(dir, STATE_FILE_NAME)
|
|
389
|
+
content = ::File.file?(state_path) ? ::File.read(state_path) : ""
|
|
390
|
+
repo_state = RepoState.new(content, remote, timestamp)
|
|
391
|
+
completed = false
|
|
320
392
|
begin
|
|
321
|
-
yield
|
|
393
|
+
result = yield repo_state
|
|
394
|
+
completed = true
|
|
395
|
+
result
|
|
322
396
|
ensure
|
|
323
|
-
if
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
397
|
+
if repo_state.modified?
|
|
398
|
+
begin
|
|
399
|
+
write_state(state_path, repo_state)
|
|
400
|
+
rescue ::StandardError
|
|
401
|
+
# If the block failed, let its error propagate rather than this
|
|
402
|
+
# one. The atomic write leaves the previous state intact.
|
|
403
|
+
raise if completed
|
|
404
|
+
end
|
|
327
405
|
end
|
|
328
406
|
end
|
|
329
407
|
end
|
|
330
408
|
end
|
|
331
409
|
|
|
410
|
+
# Writes the repo state to a temp file and renames it into place, so a
|
|
411
|
+
# failure partway through the write leaves the previous state intact. Must
|
|
412
|
+
# be called while holding the repo's lock.
|
|
413
|
+
#
|
|
414
|
+
def write_state(state_path, repo_state)
|
|
415
|
+
temp_path = "#{state_path}.tmp-#{::SecureRandom.hex(8)}"
|
|
416
|
+
::File.write(temp_path, repo_state.dump)
|
|
417
|
+
::File.rename(temp_path, state_path)
|
|
418
|
+
ensure
|
|
419
|
+
::FileUtils.rm_f(temp_path)
|
|
420
|
+
end
|
|
421
|
+
|
|
332
422
|
def ensure_repo(dir, remote)
|
|
333
423
|
repo_dir = ::File.join(dir, REPO_DIR_NAME)
|
|
334
424
|
::FileUtils.mkdir_p(repo_dir)
|
|
@@ -343,20 +433,20 @@ class GitCache
|
|
|
343
433
|
end
|
|
344
434
|
end
|
|
345
435
|
|
|
346
|
-
def ensure_commit(dir, commit,
|
|
436
|
+
def ensure_commit(dir, commit, repo_state, update = false)
|
|
347
437
|
local_commit = "git-cache/#{commit}"
|
|
348
438
|
repo_dir = ::File.join(dir, REPO_DIR_NAME)
|
|
349
439
|
is_sha = ::GitCache.valid_sha?(commit)
|
|
350
|
-
update =
|
|
440
|
+
update = repo_state.ref_stale?(commit, update) unless is_sha
|
|
351
441
|
if (update && !is_sha) || !commit_exists?(repo_dir, local_commit)
|
|
352
442
|
git(repo_dir, ["fetch", "--depth=1", "--force", "origin", "#{commit}:#{local_commit}"],
|
|
353
443
|
error_message: "Unable to fetch commit: #{commit}")
|
|
354
|
-
|
|
444
|
+
repo_state.update_ref!(commit)
|
|
355
445
|
end
|
|
356
446
|
result = git(repo_dir, ["rev-parse", local_commit],
|
|
357
447
|
error_message: "Unable to retrieve commit: #{local_commit}")
|
|
358
448
|
sha = result.captured_out.strip
|
|
359
|
-
|
|
449
|
+
repo_state.access_ref!(commit, sha)
|
|
360
450
|
sha
|
|
361
451
|
end
|
|
362
452
|
|
|
@@ -365,11 +455,11 @@ class GitCache
|
|
|
365
455
|
result.success? && result.captured_out.strip == "commit"
|
|
366
456
|
end
|
|
367
457
|
|
|
368
|
-
def ensure_source(dir, sha, path,
|
|
458
|
+
def ensure_source(dir, sha, path, repo_state)
|
|
369
459
|
repo_path = ::File.join(dir, REPO_DIR_NAME)
|
|
370
460
|
source_path = ::File.join(dir, sha)
|
|
371
461
|
result =
|
|
372
|
-
if
|
|
462
|
+
if repo_state.source_exists?(sha, path)
|
|
373
463
|
::GitCache.safe_join(source_path, path)
|
|
374
464
|
else
|
|
375
465
|
chmod_recursive("u+w", source_path)
|
|
@@ -379,14 +469,14 @@ class GitCache
|
|
|
379
469
|
chmod_recursive("a-w", source_path) unless ::GitCache.sources_writable?
|
|
380
470
|
end
|
|
381
471
|
end
|
|
382
|
-
|
|
472
|
+
repo_state.access_source!(sha, path)
|
|
383
473
|
result
|
|
384
474
|
end
|
|
385
475
|
|
|
386
|
-
def copy_files(dir, sha, path,
|
|
476
|
+
def copy_files(dir, sha, path, repo_state, into)
|
|
387
477
|
repo_path = ::File.join(dir, REPO_DIR_NAME)
|
|
388
478
|
result = copy_from_repo(repo_path, into, sha, path)
|
|
389
|
-
|
|
479
|
+
repo_state.access_repo!
|
|
390
480
|
result
|
|
391
481
|
end
|
|
392
482
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: git_cache
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daniel Azuma
|
|
@@ -54,15 +54,15 @@ files:
|
|
|
54
54
|
- lib/git_cache.rb
|
|
55
55
|
- lib/git_cache/error.rb
|
|
56
56
|
- lib/git_cache/repo_info.rb
|
|
57
|
-
- lib/git_cache/
|
|
57
|
+
- lib/git_cache/repo_state.rb
|
|
58
58
|
- lib/git_cache/version.rb
|
|
59
59
|
homepage: https://github.com/dazuma/git_cache
|
|
60
60
|
licenses:
|
|
61
61
|
- MIT
|
|
62
62
|
metadata:
|
|
63
63
|
bug_tracker_uri: https://github.com/dazuma/git_cache/issues
|
|
64
|
-
changelog_uri: https://rubydoc.info/gems/git_cache/0.
|
|
65
|
-
documentation_uri: https://rubydoc.info/gems/git_cache/0.
|
|
64
|
+
changelog_uri: https://rubydoc.info/gems/git_cache/0.2.0/file/CHANGELOG.md
|
|
65
|
+
documentation_uri: https://rubydoc.info/gems/git_cache/0.2.0
|
|
66
66
|
homepage_uri: https://github.com/dazuma/git_cache
|
|
67
67
|
rdoc_options: []
|
|
68
68
|
require_paths:
|
|
@@ -78,7 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
78
78
|
- !ruby/object:Gem::Version
|
|
79
79
|
version: '0'
|
|
80
80
|
requirements: []
|
|
81
|
-
rubygems_version: 4.0.
|
|
81
|
+
rubygems_version: 4.0.20
|
|
82
82
|
specification_version: 4
|
|
83
83
|
summary: A local file system cache of data from git repositories.
|
|
84
84
|
test_files: []
|