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