1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 package org.eclipse.jgit.internal.storage.dfs;
45
46 import static org.eclipse.jgit.lib.Ref.Storage.NEW;
47
48 import java.io.IOException;
49 import java.util.Collections;
50 import java.util.List;
51 import java.util.Map;
52 import java.util.concurrent.atomic.AtomicReference;
53
54 import org.eclipse.jgit.errors.MissingObjectException;
55 import org.eclipse.jgit.lib.ObjectIdRef;
56 import org.eclipse.jgit.lib.Ref;
57 import org.eclipse.jgit.lib.RefDatabase;
58 import org.eclipse.jgit.lib.RefRename;
59 import org.eclipse.jgit.lib.RefUpdate;
60 import org.eclipse.jgit.lib.SymbolicRef;
61 import org.eclipse.jgit.revwalk.RevObject;
62 import org.eclipse.jgit.revwalk.RevTag;
63 import org.eclipse.jgit.revwalk.RevWalk;
64 import org.eclipse.jgit.util.RefList;
65 import org.eclipse.jgit.util.RefMap;
66
67
68 public abstract class DfsRefDatabase extends RefDatabase {
69 private final DfsRepository repository;
70
71 private final AtomicReference<RefCache> cache;
72
73
74
75
76
77
78
79 protected DfsRefDatabase(DfsRepository repository) {
80 this.repository = repository;
81 this.cache = new AtomicReference<>();
82 }
83
84
85 protected DfsRepository getRepository() {
86 return repository;
87 }
88
89 boolean exists() throws IOException {
90 return 0 < read().size();
91 }
92
93 @Override
94 public Ref exactRef(String name) throws IOException {
95 RefCache curr = read();
96 Ref ref = curr.ids.get(name);
97 return ref != null ? resolve(ref, 0, curr.ids) : null;
98 }
99
100 @Override
101 public Ref getRef(String needle) throws IOException {
102 RefCache curr = read();
103 for (String prefix : SEARCH_PATH) {
104 Ref ref = curr.ids.get(prefix + needle);
105 if (ref != null) {
106 ref = resolve(ref, 0, curr.ids);
107 return ref;
108 }
109 }
110 return null;
111 }
112
113 @Override
114 public List<Ref> getAdditionalRefs() {
115 return Collections.emptyList();
116 }
117
118 @Override
119 public Map<String, Ref> getRefs(String prefix) throws IOException {
120 RefCache curr = read();
121 RefList<Ref> packed = RefList.emptyList();
122 RefList<Ref> loose = curr.ids;
123 RefList.Builder<Ref> sym = new RefList.Builder<>(curr.sym.size());
124
125 for (int idx = 0; idx < curr.sym.size(); idx++) {
126 Ref ref = curr.sym.get(idx);
127 String name = ref.getName();
128 ref = resolve(ref, 0, loose);
129 if (ref != null && ref.getObjectId() != null) {
130 sym.add(ref);
131 } else {
132
133
134
135 int toRemove = loose.find(name);
136 if (0 <= toRemove)
137 loose = loose.remove(toRemove);
138 }
139 }
140
141 return new RefMap(prefix, packed, loose, sym.toRefList());
142 }
143
144 private Ref resolve(Ref ref, int depth, RefList<Ref> loose)
145 throws IOException {
146 if (!ref.isSymbolic())
147 return ref;
148
149 Ref dst = ref.getTarget();
150
151 if (MAX_SYMBOLIC_REF_DEPTH <= depth)
152 return null;
153
154 dst = loose.get(dst.getName());
155 if (dst == null)
156 return ref;
157
158 dst = resolve(dst, depth + 1, loose);
159 if (dst == null)
160 return null;
161 return new SymbolicRef(ref.getName(), dst);
162 }
163
164 @Override
165 public Ref peel(Ref ref) throws IOException {
166 final Ref oldLeaf = ref.getLeaf();
167 if (oldLeaf.isPeeled() || oldLeaf.getObjectId() == null)
168 return ref;
169
170 Ref newLeaf = doPeel(oldLeaf);
171
172 RefCache cur = read();
173 int idx = cur.ids.find(oldLeaf.getName());
174 if (0 <= idx && cur.ids.get(idx) == oldLeaf) {
175 RefList<Ref> newList = cur.ids.set(idx, newLeaf);
176 cache.compareAndSet(cur, new RefCache(newList, cur));
177 cachePeeledState(oldLeaf, newLeaf);
178 }
179
180 return recreate(ref, newLeaf);
181 }
182
183 private Ref doPeel(final Ref leaf) throws MissingObjectException,
184 IOException {
185 try (RevWalk rw = new RevWalk(repository)) {
186 RevObject obj = rw.parseAny(leaf.getObjectId());
187 if (obj instanceof RevTag) {
188 return new ObjectIdRef.PeeledTag(
189 leaf.getStorage(),
190 leaf.getName(),
191 leaf.getObjectId(),
192 rw.peel(obj).copy());
193 } else {
194 return new ObjectIdRef.PeeledNonTag(
195 leaf.getStorage(),
196 leaf.getName(),
197 leaf.getObjectId());
198 }
199 }
200 }
201
202 private static Ref recreate(Ref old, Ref leaf) {
203 if (old.isSymbolic()) {
204 Ref dst = recreate(old.getTarget(), leaf);
205 return new SymbolicRef(old.getName(), dst);
206 }
207 return leaf;
208 }
209
210 @Override
211 public RefUpdate newUpdate(String refName, boolean detach)
212 throws IOException {
213 boolean detachingSymbolicRef = false;
214 Ref ref = exactRef(refName);
215 if (ref == null)
216 ref = new ObjectIdRef.Unpeeled(NEW, refName, null);
217 else
218 detachingSymbolicRef = detach && ref.isSymbolic();
219
220 DfsRefUpdate update = new DfsRefUpdate(this, ref);
221 if (detachingSymbolicRef)
222 update.setDetachingSymbolicRef();
223 return update;
224 }
225
226 @Override
227 public RefRename newRename(String fromName, String toName)
228 throws IOException {
229 RefUpdate src = newUpdate(fromName, true);
230 RefUpdate dst = newUpdate(toName, true);
231 return new DfsRefRename(src, dst);
232 }
233
234 @Override
235 public boolean isNameConflicting(String refName) throws IOException {
236 RefList<Ref> all = read().ids;
237
238
239 int lastSlash = refName.lastIndexOf('/');
240 while (0 < lastSlash) {
241 String needle = refName.substring(0, lastSlash);
242 if (all.contains(needle))
243 return true;
244 lastSlash = refName.lastIndexOf('/', lastSlash - 1);
245 }
246
247
248 String prefix = refName + '/';
249 int idx = -(all.find(prefix) + 1);
250 if (idx < all.size() && all.get(idx).getName().startsWith(prefix))
251 return true;
252 return false;
253 }
254
255 @Override
256 public void create() {
257
258 }
259
260 @Override
261 public void refresh() {
262 clearCache();
263 }
264
265 @Override
266 public void close() {
267 clearCache();
268 }
269
270 void clearCache() {
271 cache.set(null);
272 }
273
274 void stored(Ref ref) {
275 RefCache oldCache, newCache;
276 do {
277 oldCache = cache.get();
278 if (oldCache == null)
279 return;
280 newCache = oldCache.put(ref);
281 } while (!cache.compareAndSet(oldCache, newCache));
282 }
283
284 void removed(String refName) {
285 RefCache oldCache, newCache;
286 do {
287 oldCache = cache.get();
288 if (oldCache == null)
289 return;
290 newCache = oldCache.remove(refName);
291 } while (!cache.compareAndSet(oldCache, newCache));
292 }
293
294 private RefCache read() throws IOException {
295 RefCache c = cache.get();
296 if (c == null) {
297 c = scanAllRefs();
298 cache.set(c);
299 }
300 return c;
301 }
302
303
304
305
306
307
308
309
310 protected abstract RefCache scanAllRefs() throws IOException;
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335 protected abstract boolean compareAndPut(Ref oldRef, Ref newRef)
336 throws IOException;
337
338
339
340
341
342
343
344
345
346
347 protected abstract boolean compareAndRemove(Ref oldRef) throws IOException;
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363 protected void cachePeeledState(Ref oldLeaf, Ref newLeaf) {
364 try {
365 compareAndPut(oldLeaf, newLeaf);
366 } catch (IOException e) {
367
368 }
369 }
370
371
372 public static class RefCache {
373 final RefList<Ref> ids;
374
375 final RefList<Ref> sym;
376
377
378
379
380
381
382
383
384
385
386
387
388 public RefCache(RefList<Ref> ids, RefList<Ref> sym) {
389 this.ids = ids;
390 this.sym = sym;
391 }
392
393 RefCache(RefList<Ref> ids, RefCache old) {
394 this(ids, old.sym);
395 }
396
397
398 public int size() {
399 return ids.size();
400 }
401
402
403
404
405
406
407
408
409 public Ref get(String name) {
410 return ids.get(name);
411 }
412
413
414
415
416
417
418
419
420
421
422 public RefCache put(Ref ref) {
423 RefList<Ref> newIds = this.ids.put(ref);
424 RefList<Ref> newSym = this.sym;
425 if (ref.isSymbolic()) {
426 newSym = newSym.put(ref);
427 } else {
428 int p = newSym.find(ref.getName());
429 if (0 <= p)
430 newSym = newSym.remove(p);
431 }
432 return new RefCache(newIds, newSym);
433 }
434
435
436
437
438
439
440
441
442
443
444 public RefCache remove(String refName) {
445 RefList<Ref> newIds = this.ids;
446 int p = newIds.find(refName);
447 if (0 <= p)
448 newIds = newIds.remove(p);
449
450 RefList<Ref> newSym = this.sym;
451 p = newSym.find(refName);
452 if (0 <= p)
453 newSym = newSym.remove(p);
454 return new RefCache(newIds, newSym);
455 }
456 }
457 }