1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.internal.storage.reftable;
12
13 import java.io.IOException;
14 import java.util.ArrayList;
15 import java.util.Collections;
16 import java.util.HashSet;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Set;
20 import java.util.TreeSet;
21 import java.util.concurrent.locks.ReentrantLock;
22 import java.util.stream.Collectors;
23
24 import org.eclipse.jgit.annotations.Nullable;
25 import org.eclipse.jgit.lib.ObjectId;
26 import org.eclipse.jgit.lib.Ref;
27 import org.eclipse.jgit.lib.RefDatabase;
28 import org.eclipse.jgit.lib.ReflogReader;
29 import org.eclipse.jgit.transport.ReceiveCommand;
30
31
32
33
34
35
36
37 public abstract class ReftableDatabase {
38
39 private final ReentrantLock lock = new ReentrantLock(true);
40
41 private Reftable mergedTables;
42
43
44
45
46
47
48
49
50
51
52 protected abstract MergedReftable openMergedReftable() throws IOException;
53
54
55
56
57
58
59
60 public long nextUpdateIndex() throws IOException {
61 lock.lock();
62 try {
63 return reader().maxUpdateIndex() + 1;
64 } finally {
65 lock.unlock();
66 }
67 }
68
69
70
71
72
73
74
75
76 public ReflogReader getReflogReader(String refname) throws IOException {
77 lock.lock();
78 try {
79 return new ReftableReflogReader(lock, reader(), refname);
80 } finally {
81 lock.unlock();
82 }
83 }
84
85
86
87
88
89
90
91
92 public static ReceiveCommand toCommand(Ref oldRef, Ref newRef) {
93 ObjectId oldId = toId(oldRef);
94 ObjectId newId = toId(newRef);
95 String name = oldRef != null ? oldRef.getName() : newRef.getName();
96
97 if (oldRef != null && oldRef.isSymbolic()) {
98 if (newRef != null) {
99 if (newRef.isSymbolic()) {
100 return ReceiveCommand.link(oldRef.getTarget().getName(),
101 newRef.getTarget().getName(), name);
102 }
103
104
105 return ReceiveCommand.unlink(oldRef.getTarget().getName(),
106 newId, name);
107 }
108 return ReceiveCommand.unlink(oldRef.getTarget().getName(),
109 ObjectId.zeroId(), name);
110 }
111
112 if (newRef != null && newRef.isSymbolic()) {
113 if (oldRef != null) {
114 if (oldRef.isSymbolic()) {
115 return ReceiveCommand.link(oldRef.getTarget().getName(),
116 newRef.getTarget().getName(), name);
117 }
118 return ReceiveCommand.link(oldId,
119 newRef.getTarget().getName(), name);
120 }
121 return ReceiveCommand.link(ObjectId.zeroId(),
122 newRef.getTarget().getName(), name);
123 }
124
125 return new ReceiveCommand(oldId, newId, name);
126 }
127
128 private static ObjectId toId(Ref ref) {
129 if (ref != null) {
130 ObjectId id = ref.getObjectId();
131 if (id != null) {
132 return id;
133 }
134 }
135 return ObjectId.zeroId();
136 }
137
138
139
140
141
142 public ReentrantLock getLock() {
143 return lock;
144 }
145
146
147
148
149
150
151
152 private Reftable reader() throws IOException {
153 if (!lock.isLocked()) {
154 throw new IllegalStateException(
155 "must hold lock to access merged table");
156 }
157 if (mergedTables == null) {
158 mergedTables = openMergedReftable();
159 }
160 return mergedTables;
161 }
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176 public boolean isNameConflicting(String refName, TreeSet<String> added,
177 Set<String> deleted) throws IOException {
178 lock.lock();
179 try {
180 Reftable table = reader();
181
182
183 int lastSlash = refName.lastIndexOf('/');
184 while (0 < lastSlash) {
185 String prefix = refName.substring(0, lastSlash);
186 if (!deleted.contains(prefix)
187 && (table.hasRef(prefix) || added.contains(prefix))) {
188 return true;
189 }
190 lastSlash = refName.lastIndexOf('/', lastSlash - 1);
191 }
192
193
194 String prefix = refName + '/';
195 RefCursor c = table.seekRefsWithPrefix(prefix);
196 while (c.next()) {
197 if (!deleted.contains(c.getRef().getName())) {
198 return true;
199 }
200 }
201
202 String it = added.ceiling(refName + '/');
203 if (it != null && it.startsWith(prefix)) {
204 return true;
205 }
206 return false;
207 } finally {
208 lock.unlock();
209 }
210 }
211
212
213
214
215
216
217
218
219
220
221
222
223
224 @Nullable
225 public Ref exactRef(String name) throws IOException {
226 lock.lock();
227 try {
228 Reftable table = reader();
229 Ref ref = table.exactRef(name);
230 if (ref != null && ref.isSymbolic()) {
231 return table.resolve(ref);
232 }
233 return ref;
234 } finally {
235 lock.unlock();
236 }
237 }
238
239
240
241
242
243
244
245
246
247
248
249 public List<Ref> getRefsByPrefix(String prefix) throws IOException {
250 List<Ref> all = new ArrayList<>();
251 lock.lock();
252 try {
253 Reftable table = reader();
254 try (RefCursor rc = RefDatabase.ALL.equals(prefix) ? table.allRefs()
255 : table.seekRefsWithPrefix(prefix)) {
256 while (rc.next()) {
257 Ref ref = table.resolve(rc.getRef());
258 if (ref != null && ref.getObjectId() != null) {
259 all.add(ref);
260 }
261 }
262 }
263 } finally {
264 lock.unlock();
265 }
266
267 return Collections.unmodifiableList(all);
268 }
269
270
271
272
273
274
275
276
277
278
279
280 public List<Ref> getRefsByPrefixWithExclusions(String include, Set<String> excludes) throws IOException {
281 if (excludes.isEmpty()) {
282 return getRefsByPrefix(include);
283 }
284 List<Ref> results = new ArrayList<>();
285 lock.lock();
286 try {
287 Reftable table = reader();
288 Iterator<String> excludeIterator =
289 excludes.stream().sorted().collect(Collectors.toList()).iterator();
290 String currentExclusion = excludeIterator.hasNext() ? excludeIterator.next() : null;
291 try (RefCursor rc = RefDatabase.ALL.equals(include) ? table.allRefs() : table.seekRefsWithPrefix(include)) {
292 while (rc.next()) {
293 Ref ref = table.resolve(rc.getRef());
294 if (ref == null || ref.getObjectId() == null) {
295 continue;
296 }
297
298
299 while (excludeIterator.hasNext() && !ref.getName().startsWith(currentExclusion)
300 && ref.getName().compareTo(currentExclusion) > 0) {
301 currentExclusion = excludeIterator.next();
302 }
303
304 if (currentExclusion != null && ref.getName().startsWith(currentExclusion)) {
305 rc.seekPastPrefix(currentExclusion);
306 continue;
307 }
308 results.add(ref);
309 }
310 }
311 } finally {
312 lock.unlock();
313 }
314
315 return Collections.unmodifiableList(results);
316 }
317
318
319
320
321
322 public boolean hasFastTipsWithSha1() throws IOException {
323 lock.lock();
324 try {
325 return reader().hasObjectMap();
326 } finally {
327 lock.unlock();
328 }
329 }
330
331
332
333
334
335
336
337
338
339
340
341
342 public Set<Ref> getTipsWithSha1(ObjectId id) throws IOException {
343 lock.lock();
344 try {
345 RefCursor cursor = reader().byObjectId(id);
346 Set<Ref> refs = new HashSet<>();
347 while (cursor.next()) {
348 refs.add(cursor.getRef());
349 }
350 return refs;
351 } finally {
352 lock.unlock();
353 }
354 }
355
356
357
358
359 public void clearCache() {
360 lock.lock();
361 try {
362 mergedTables = null;
363 } finally {
364 lock.unlock();
365 }
366 }
367 }