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<RefCache>();
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<Ref>(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 if (detachingSymbolicRef) {
221 ref = new ObjectIdRef.Unpeeled(NEW, refName, ref.getObjectId());
222 }
223
224 DfsRefUpdate update = new DfsRefUpdate(this, ref);
225 if (detachingSymbolicRef)
226 update.setDetachingSymbolicRef();
227 return update;
228 }
229
230 @Override
231 public RefRename newRename(String fromName, String toName)
232 throws IOException {
233 RefUpdate src = newUpdate(fromName, true);
234 RefUpdate dst = newUpdate(toName, true);
235 return new DfsRefRename(src, dst);
236 }
237
238 @Override
239 public boolean isNameConflicting(String refName) throws IOException {
240 RefList<Ref> all = read().ids;
241
242
243 int lastSlash = refName.lastIndexOf('/');
244 while (0 < lastSlash) {
245 String needle = refName.substring(0, lastSlash);
246 if (all.contains(needle))
247 return true;
248 lastSlash = refName.lastIndexOf('/', lastSlash - 1);
249 }
250
251
252 String prefix = refName + '/';
253 int idx = -(all.find(prefix) + 1);
254 if (idx < all.size() && all.get(idx).getName().startsWith(prefix))
255 return true;
256 return false;
257 }
258
259 @Override
260 public void create() {
261
262 }
263
264 @Override
265 public void close() {
266 clearCache();
267 }
268
269 void clearCache() {
270 cache.set(null);
271 }
272
273 void stored(Ref ref) {
274 RefCache oldCache, newCache;
275 do {
276 oldCache = cache.get();
277 if (oldCache == null)
278 return;
279 newCache = oldCache.put(ref);
280 } while (!cache.compareAndSet(oldCache, newCache));
281 }
282
283 void removed(String refName) {
284 RefCache oldCache, newCache;
285 do {
286 oldCache = cache.get();
287 if (oldCache == null)
288 return;
289 newCache = oldCache.remove(refName);
290 } while (!cache.compareAndSet(oldCache, newCache));
291 }
292
293 private RefCache read() throws IOException {
294 RefCache c = cache.get();
295 if (c == null) {
296 c = scanAllRefs();
297 cache.set(c);
298 }
299 return c;
300 }
301
302
303
304
305
306
307
308
309 protected abstract RefCache scanAllRefs() throws IOException;
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325 protected abstract boolean compareAndPut(Ref oldRef, Ref newRef)
326 throws IOException;
327
328
329
330
331
332
333
334
335
336
337 protected abstract boolean compareAndRemove(Ref oldRef) throws IOException;
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353 protected void cachePeeledState(Ref oldLeaf, Ref newLeaf) {
354 try {
355 compareAndPut(oldLeaf, newLeaf);
356 } catch (IOException e) {
357
358 }
359 }
360
361
362 public static class RefCache {
363 final RefList<Ref> ids;
364
365 final RefList<Ref> sym;
366
367
368
369
370
371
372
373
374
375
376
377
378 public RefCache(RefList<Ref> ids, RefList<Ref> sym) {
379 this.ids = ids;
380 this.sym = sym;
381 }
382
383 RefCache(RefList<Ref> ids, RefCache old) {
384 this(ids, old.sym);
385 }
386
387
388 public int size() {
389 return ids.size();
390 }
391
392
393
394
395
396
397
398
399 public Ref get(String name) {
400 return ids.get(name);
401 }
402
403
404
405
406
407
408
409
410
411
412 public RefCache put(Ref ref) {
413 RefList<Ref> newIds = this.ids.put(ref);
414 RefList<Ref> newSym = this.sym;
415 if (ref.isSymbolic()) {
416 newSym = newSym.put(ref);
417 } else {
418 int p = newSym.find(ref.getName());
419 if (0 <= p)
420 newSym = newSym.remove(p);
421 }
422 return new RefCache(newIds, newSym);
423 }
424
425
426
427
428
429
430
431
432
433
434 public RefCache remove(String refName) {
435 RefList<Ref> newIds = this.ids;
436 int p = newIds.find(refName);
437 if (0 <= p)
438 newIds = newIds.remove(p);
439
440 RefList<Ref> newSym = this.sym;
441 p = newSym.find(refName);
442 if (0 <= p)
443 newSym = newSym.remove(p);
444 return new RefCache(newIds, newSym);
445 }
446 }
447 }