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 getRef(String needle) throws IOException {
95 RefCache curr = read();
96 for (String prefix : SEARCH_PATH) {
97 Ref ref = curr.ids.get(prefix + needle);
98 if (ref != null) {
99 ref = resolve(ref, 0, curr.ids);
100 return ref;
101 }
102 }
103 return null;
104 }
105
106 private Ref getOneRef(String refName) throws IOException {
107 RefCache curr = read();
108 Ref ref = curr.ids.get(refName);
109 if (ref != null)
110 return resolve(ref, 0, curr.ids);
111 return ref;
112 }
113
114 @Override
115 public List<Ref> getAdditionalRefs() {
116 return Collections.emptyList();
117 }
118
119 @Override
120 public Map<String, Ref> getRefs(String prefix) throws IOException {
121 RefCache curr = read();
122 RefList<Ref> packed = RefList.emptyList();
123 RefList<Ref> loose = curr.ids;
124 RefList.Builder<Ref> sym = new RefList.Builder<Ref>(curr.sym.size());
125
126 for (int idx = 0; idx < curr.sym.size(); idx++) {
127 Ref ref = curr.sym.get(idx);
128 String name = ref.getName();
129 ref = resolve(ref, 0, loose);
130 if (ref != null && ref.getObjectId() != null) {
131 sym.add(ref);
132 } else {
133
134
135
136 int toRemove = loose.find(name);
137 if (0 <= toRemove)
138 loose = loose.remove(toRemove);
139 }
140 }
141
142 return new RefMap(prefix, packed, loose, sym.toRefList());
143 }
144
145 private Ref resolve(Ref ref, int depth, RefList<Ref> loose)
146 throws IOException {
147 if (!ref.isSymbolic())
148 return ref;
149
150 Ref dst = ref.getTarget();
151
152 if (MAX_SYMBOLIC_REF_DEPTH <= depth)
153 return null;
154
155 dst = loose.get(dst.getName());
156 if (dst == null)
157 return ref;
158
159 dst = resolve(dst, depth + 1, loose);
160 if (dst == null)
161 return null;
162 return new SymbolicRef(ref.getName(), dst);
163 }
164
165 @Override
166 public Ref peel(Ref ref) throws IOException {
167 final Ref oldLeaf = ref.getLeaf();
168 if (oldLeaf.isPeeled() || oldLeaf.getObjectId() == null)
169 return ref;
170
171 Ref newLeaf = doPeel(oldLeaf);
172
173 RefCache cur = read();
174 int idx = cur.ids.find(oldLeaf.getName());
175 if (0 <= idx && cur.ids.get(idx) == oldLeaf) {
176 RefList<Ref> newList = cur.ids.set(idx, newLeaf);
177 cache.compareAndSet(cur, new RefCache(newList, cur));
178 cachePeeledState(oldLeaf, newLeaf);
179 }
180
181 return recreate(ref, newLeaf);
182 }
183
184 private Ref doPeel(final Ref leaf) throws MissingObjectException,
185 IOException {
186 try (RevWalk rw = new RevWalk(repository)) {
187 RevObject obj = rw.parseAny(leaf.getObjectId());
188 if (obj instanceof RevTag) {
189 return new ObjectIdRef.PeeledTag(
190 leaf.getStorage(),
191 leaf.getName(),
192 leaf.getObjectId(),
193 rw.peel(obj).copy());
194 } else {
195 return new ObjectIdRef.PeeledNonTag(
196 leaf.getStorage(),
197 leaf.getName(),
198 leaf.getObjectId());
199 }
200 }
201 }
202
203 private static Ref recreate(Ref old, Ref leaf) {
204 if (old.isSymbolic()) {
205 Ref dst = recreate(old.getTarget(), leaf);
206 return new SymbolicRef(old.getName(), dst);
207 }
208 return leaf;
209 }
210
211 @Override
212 public RefUpdate newUpdate(String refName, boolean detach)
213 throws IOException {
214 boolean detachingSymbolicRef = false;
215 Ref ref = getOneRef(refName);
216 if (ref == null)
217 ref = new ObjectIdRef.Unpeeled(NEW, refName, null);
218 else
219 detachingSymbolicRef = detach && ref.isSymbolic();
220
221 if (detachingSymbolicRef) {
222 ref = new ObjectIdRef.Unpeeled(NEW, refName, ref.getObjectId());
223 }
224
225 DfsRefUpdate update = new DfsRefUpdate(this, ref);
226 if (detachingSymbolicRef)
227 update.setDetachingSymbolicRef();
228 return update;
229 }
230
231 @Override
232 public RefRename newRename(String fromName, String toName)
233 throws IOException {
234 RefUpdate src = newUpdate(fromName, true);
235 RefUpdate dst = newUpdate(toName, true);
236 return new DfsRefRename(src, dst);
237 }
238
239 @Override
240 public boolean isNameConflicting(String refName) throws IOException {
241 RefList<Ref> all = read().ids;
242
243
244 int lastSlash = refName.lastIndexOf('/');
245 while (0 < lastSlash) {
246 String needle = refName.substring(0, lastSlash);
247 if (all.contains(needle))
248 return true;
249 lastSlash = refName.lastIndexOf('/', lastSlash - 1);
250 }
251
252
253 String prefix = refName + '/';
254 int idx = -(all.find(prefix) + 1);
255 if (idx < all.size() && all.get(idx).getName().startsWith(prefix))
256 return true;
257 return false;
258 }
259
260 @Override
261 public void create() {
262
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 protected abstract boolean compareAndPut(Ref oldRef, Ref newRef)
327 throws IOException;
328
329
330
331
332
333
334
335
336
337
338 protected abstract boolean compareAndRemove(Ref oldRef) throws IOException;
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354 protected void cachePeeledState(Ref oldLeaf, Ref newLeaf) {
355 try {
356 compareAndPut(oldLeaf, newLeaf);
357 } catch (IOException e) {
358
359 }
360 }
361
362
363 public static class RefCache {
364 final RefList<Ref> ids;
365
366 final RefList<Ref> sym;
367
368
369
370
371
372
373
374
375
376
377
378
379 public RefCache(RefList<Ref> ids, RefList<Ref> sym) {
380 this.ids = ids;
381 this.sym = sym;
382 }
383
384 RefCache(RefList<Ref> ids, RefCache old) {
385 this(ids, old.sym);
386 }
387
388
389 public int size() {
390 return ids.size();
391 }
392
393
394
395
396
397
398
399
400 public Ref get(String name) {
401 return ids.get(name);
402 }
403
404
405
406
407
408
409
410
411
412
413 public RefCache put(Ref ref) {
414 RefList<Ref> newIds = this.ids.put(ref);
415 RefList<Ref> newSym = this.sym;
416 if (ref.isSymbolic()) {
417 newSym = newSym.put(ref);
418 } else {
419 int p = newSym.find(ref.getName());
420 if (0 <= p)
421 newSym = newSym.remove(p);
422 }
423 return new RefCache(newIds, newSym);
424 }
425
426
427
428
429
430
431
432
433
434
435 public RefCache remove(String refName) {
436 RefList<Ref> newIds = this.ids;
437 int p = newIds.find(refName);
438 if (0 <= p)
439 newIds = newIds.remove(p);
440
441 RefList<Ref> newSym = this.sym;
442 p = newSym.find(refName);
443 if (0 <= p)
444 newSym = newSym.remove(p);
445 return new RefCache(newIds, newSym);
446 }
447 }
448 }