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 java.io.IOException;
47 import java.util.Arrays;
48 import java.util.Map;
49 import java.util.concurrent.locks.ReentrantLock;
50
51 import org.eclipse.jgit.annotations.Nullable;
52 import org.eclipse.jgit.internal.storage.reftable.MergedReftable;
53 import org.eclipse.jgit.internal.storage.reftable.RefCursor;
54 import org.eclipse.jgit.internal.storage.reftable.Reftable;
55 import org.eclipse.jgit.internal.storage.reftable.ReftableConfig;
56 import org.eclipse.jgit.lib.BatchRefUpdate;
57 import org.eclipse.jgit.lib.NullProgressMonitor;
58 import org.eclipse.jgit.lib.ObjectId;
59 import org.eclipse.jgit.lib.Ref;
60 import org.eclipse.jgit.revwalk.RevWalk;
61 import org.eclipse.jgit.transport.ReceiveCommand;
62 import org.eclipse.jgit.util.RefList;
63 import org.eclipse.jgit.util.RefMap;
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78 public class DfsReftableDatabase extends DfsRefDatabase {
79 private final ReentrantLock lock = new ReentrantLock(true);
80
81 private DfsReader ctx;
82
83 private ReftableStack tableStack;
84
85 private MergedReftable mergedTables;
86
87
88
89
90
91
92
93 protected DfsReftableDatabase(DfsRepository repo) {
94 super(repo);
95 }
96
97
98 @Override
99 public boolean performsAtomicTransactions() {
100 return true;
101 }
102
103
104 @Override
105 public BatchRefUpdate newBatchUpdate() {
106 DfsObjDatabase odb = getRepository().getObjectDatabase();
107 return new ReftableBatchRefUpdate(this, odb);
108 }
109
110
111
112
113
114
115 public ReftableConfig getReftableConfig() {
116 return new ReftableConfig(getRepository().getConfig());
117 }
118
119
120
121
122
123
124 protected ReentrantLock getLock() {
125 return lock;
126 }
127
128
129
130
131
132
133
134
135 protected boolean compactDuringCommit() {
136 return true;
137 }
138
139
140
141
142
143
144
145
146 protected Reftable reader() throws IOException {
147 lock.lock();
148 try {
149 if (mergedTables == null) {
150 mergedTables = new MergedReftable(stack().readers());
151 }
152 return mergedTables;
153 } finally {
154 lock.unlock();
155 }
156 }
157
158
159
160
161
162
163
164
165 protected ReftableStack stack() throws IOException {
166 lock.lock();
167 try {
168 if (tableStack == null) {
169 DfsObjDatabase odb = getRepository().getObjectDatabase();
170 if (ctx == null) {
171 ctx = odb.newReader();
172 }
173 tableStack = ReftableStack.open(ctx,
174 Arrays.asList(odb.getReftables()));
175 }
176 return tableStack;
177 } finally {
178 lock.unlock();
179 }
180 }
181
182
183 @Override
184 public boolean isNameConflicting(String refName) throws IOException {
185 lock.lock();
186 try {
187 Reftable table = reader();
188
189
190 int lastSlash = refName.lastIndexOf('/');
191 while (0 < lastSlash) {
192 if (table.hasRef(refName.substring(0, lastSlash))) {
193 return true;
194 }
195 lastSlash = refName.lastIndexOf('/', lastSlash - 1);
196 }
197
198
199 return table.hasRef(refName + '/');
200 } finally {
201 lock.unlock();
202 }
203 }
204
205
206 @Override
207 public Ref exactRef(String name) throws IOException {
208 lock.lock();
209 try {
210 Reftable table = reader();
211 Ref ref = table.exactRef(name);
212 if (ref != null && ref.isSymbolic()) {
213 return table.resolve(ref);
214 }
215 return ref;
216 } finally {
217 lock.unlock();
218 }
219 }
220
221
222 @Override
223 public Ref getRef(String needle) throws IOException {
224 for (String prefix : SEARCH_PATH) {
225 Ref ref = exactRef(prefix + needle);
226 if (ref != null) {
227 return ref;
228 }
229 }
230 return null;
231 }
232
233
234 @Override
235 public Map<String, Ref> getRefs(String prefix) throws IOException {
236 RefList.Builder<Ref> all = new RefList.Builder<>();
237 lock.lock();
238 try {
239 Reftable table = reader();
240 try (RefCursor rc = ALL.equals(prefix) ? table.allRefs()
241 : table.seekRef(prefix)) {
242 while (rc.next()) {
243 Ref ref = table.resolve(rc.getRef());
244 if (ref != null && ref.getObjectId() != null) {
245 all.add(ref);
246 }
247 }
248 }
249 } finally {
250 lock.unlock();
251 }
252
253 RefList<Ref> none = RefList.emptyList();
254 return new RefMap(prefix, all.toRefList(), none, none);
255 }
256
257
258 @Override
259 public Ref peel(Ref ref) throws IOException {
260 Ref oldLeaf = ref.getLeaf();
261 if (oldLeaf.isPeeled() || oldLeaf.getObjectId() == null) {
262 return ref;
263 }
264 return recreate(ref, doPeel(oldLeaf));
265 }
266
267 @Override
268 boolean exists() throws IOException {
269 DfsObjDatabase odb = getRepository().getObjectDatabase();
270 return odb.getReftables().length > 0;
271 }
272
273 @Override
274 void clearCache() {
275 lock.lock();
276 try {
277 if (tableStack != null) {
278 tableStack.close();
279 tableStack = null;
280 }
281 if (ctx != null) {
282 ctx.close();
283 ctx = null;
284 }
285 mergedTables = null;
286 } finally {
287 lock.unlock();
288 }
289 }
290
291
292 @Override
293 protected boolean compareAndPut(Ref oldRef, @Nullable Ref newRef)
294 throws IOException {
295 ReceiveCommand cmd = toCommand(oldRef, newRef);
296 try (RevWalk rw = new RevWalk(getRepository())) {
297 newBatchUpdate().setAllowNonFastForwards(true).addCommand(cmd)
298 .execute(rw, NullProgressMonitor.INSTANCE);
299 }
300 switch (cmd.getResult()) {
301 case OK:
302 return true;
303 case REJECTED_OTHER_REASON:
304 throw new IOException(cmd.getMessage());
305 case LOCK_FAILURE:
306 default:
307 return false;
308 }
309 }
310
311 private static ReceiveCommand toCommand(Ref oldRef, Ref newRef) {
312 ObjectId oldId = toId(oldRef);
313 ObjectId newId = toId(newRef);
314 String name = toName(oldRef, newRef);
315
316 if (oldRef != null && oldRef.isSymbolic()) {
317 if (newRef != null) {
318 if (newRef.isSymbolic()) {
319 return ReceiveCommand.link(oldRef.getTarget().getName(),
320 newRef.getTarget().getName(), name);
321 } else {
322 return ReceiveCommand.unlink(oldRef.getTarget().getName(),
323 newId, name);
324 }
325 } else {
326 return ReceiveCommand.unlink(oldRef.getTarget().getName(),
327 ObjectId.zeroId(), name);
328 }
329 }
330
331 if (newRef != null && newRef.isSymbolic()) {
332 if (oldRef != null) {
333 if (oldRef.isSymbolic()) {
334 return ReceiveCommand.link(oldRef.getTarget().getName(),
335 newRef.getTarget().getName(), name);
336 } else {
337 return ReceiveCommand.link(oldId,
338 newRef.getTarget().getName(), name);
339 }
340 } else {
341 return ReceiveCommand.link(ObjectId.zeroId(),
342 newRef.getTarget().getName(), name);
343 }
344 }
345
346 return new ReceiveCommand(oldId, newId, name);
347 }
348
349 private static ObjectId toId(Ref ref) {
350 if (ref != null) {
351 ObjectId id = ref.getObjectId();
352 if (id != null) {
353 return id;
354 }
355 }
356 return ObjectId.zeroId();
357 }
358
359 private static String toName(Ref oldRef, Ref newRef) {
360 return oldRef != null ? oldRef.getName() : newRef.getName();
361 }
362
363
364 @Override
365 protected boolean compareAndRemove(Ref oldRef) throws IOException {
366 return compareAndPut(oldRef, null);
367 }
368
369
370 @Override
371 protected RefCache scanAllRefs() throws IOException {
372 throw new UnsupportedOperationException();
373 }
374
375 @Override
376 void stored(Ref ref) {
377
378 }
379
380 @Override
381 void removed(String refName) {
382
383 }
384
385
386 @Override
387 protected void cachePeeledState(Ref oldLeaf, Ref newLeaf) {
388
389 }
390 }