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