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