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.reftree;
45
46 import static org.eclipse.jgit.lib.Constants.HEAD;
47 import static org.eclipse.jgit.lib.Ref.Storage.LOOSE;
48 import static org.eclipse.jgit.lib.Ref.Storage.PACKED;
49
50 import java.io.IOException;
51 import java.util.ArrayList;
52 import java.util.Collection;
53 import java.util.Collections;
54 import java.util.HashMap;
55 import java.util.List;
56 import java.util.Map;
57
58 import org.eclipse.jgit.annotations.Nullable;
59 import org.eclipse.jgit.lib.BatchRefUpdate;
60 import org.eclipse.jgit.lib.Config;
61 import org.eclipse.jgit.lib.ObjectId;
62 import org.eclipse.jgit.lib.ObjectIdRef;
63 import org.eclipse.jgit.lib.Ref;
64 import org.eclipse.jgit.lib.Ref.Storage;
65 import org.eclipse.jgit.lib.RefDatabase;
66 import org.eclipse.jgit.lib.RefRename;
67 import org.eclipse.jgit.lib.RefUpdate;
68 import org.eclipse.jgit.lib.Repository;
69 import org.eclipse.jgit.lib.SymbolicRef;
70 import org.eclipse.jgit.revwalk.RevObject;
71 import org.eclipse.jgit.revwalk.RevTag;
72 import org.eclipse.jgit.revwalk.RevWalk;
73 import org.eclipse.jgit.util.RefList;
74 import org.eclipse.jgit.util.RefMap;
75
76
77
78
79
80
81
82
83
84
85
86
87
88 public class RefTreeDatabase extends RefDatabase {
89 private final Repository repo;
90 private final RefDatabase bootstrap;
91 private final String txnCommitted;
92
93 @Nullable
94 private final String txnNamespace;
95 private volatile Scanner.Result refs;
96
97
98
99
100
101
102
103
104
105
106
107 public RefTreeDatabase(Repository repo, RefDatabase bootstrap) {
108 Config cfg = repo.getConfig();
109 String committed = cfg.getString("reftree", null, "committedRef");
110 if (committed == null || committed.isEmpty()) {
111 committed = "refs/txn/committed";
112 }
113
114 this.repo = repo;
115 this.bootstrap = bootstrap;
116 this.txnNamespace = initNamespace(committed);
117 this.txnCommitted = committed;
118 }
119
120
121
122
123
124
125
126
127
128
129
130
131
132 public RefTreeDatabase(Repository repo, RefDatabase bootstrap,
133 String txnCommitted) {
134 this.repo = repo;
135 this.bootstrap = bootstrap;
136 this.txnNamespace = initNamespace(txnCommitted);
137 this.txnCommitted = txnCommitted;
138 }
139
140 private static String initNamespace(String committed) {
141 int s = committed.lastIndexOf('/');
142 if (s < 0) {
143 return null;
144 }
145 return committed.substring(0, s + 1);
146 }
147
148 Repository getRepository() {
149 return repo;
150 }
151
152
153
154
155
156
157
158 public RefDatabase getBootstrap() {
159 return bootstrap;
160 }
161
162
163
164
165
166
167 public String getTxnCommitted() {
168 return txnCommitted;
169 }
170
171
172
173
174
175
176
177 @Nullable
178 public String getTxnNamespace() {
179 return txnNamespace;
180 }
181
182
183 @Override
184 public void create() throws IOException {
185 bootstrap.create();
186 }
187
188
189 @Override
190 public boolean performsAtomicTransactions() {
191 return true;
192 }
193
194
195 @Override
196 public void refresh() {
197 bootstrap.refresh();
198 }
199
200
201 @Override
202 public void close() {
203 refs = null;
204 bootstrap.close();
205 }
206
207
208 @Override
209 public Ref exactRef(String name) throws IOException {
210 if (!repo.isBare() && name.indexOf('/') < 0 && !HEAD.equals(name)) {
211
212 return bootstrap.exactRef(name);
213 } else if (conflictsWithBootstrap(name)) {
214 return null;
215 }
216
217 boolean partial = false;
218 Ref src = bootstrap.exactRef(txnCommitted);
219 Scanner.Result c = refs;
220 if (c == null || !c.refTreeId.equals(idOf(src))) {
221 c = Scanner.scanRefTree(repo, src, prefixOf(name), false);
222 partial = true;
223 }
224
225 Ref r = c.all.get(name);
226 if (r != null && r.isSymbolic()) {
227 r = c.sym.get(name);
228 if (partial && r.getObjectId() == null) {
229
230
231
232 return getRefs(ALL).get(name);
233 }
234 }
235 return r;
236 }
237
238 private static String prefixOf(String name) {
239 int s = name.lastIndexOf('/');
240 if (s >= 0) {
241 return name.substring(0, s);
242 }
243 return "";
244 }
245
246
247 @Override
248 public Map<String, Ref> getRefs(String prefix) throws IOException {
249 if (!prefix.isEmpty() && prefix.charAt(prefix.length() - 1) != '/') {
250 return new HashMap<>(0);
251 }
252
253 Ref src = bootstrap.exactRef(txnCommitted);
254 Scanner.Result c = refs;
255 if (c == null || !c.refTreeId.equals(idOf(src))) {
256 c = Scanner.scanRefTree(repo, src, prefix, true);
257 if (prefix.isEmpty()) {
258 refs = c;
259 }
260 }
261 return new RefMap(prefix, RefList.<Ref> emptyList(), c.all, c.sym);
262 }
263
264 private static ObjectId idOf(@Nullable Ref src) {
265 return src != null && src.getObjectId() != null
266 ? src.getObjectId()
267 : ObjectId.zeroId();
268 }
269
270
271 @Override
272 public List<Ref> getAdditionalRefs() throws IOException {
273 Collection<Ref> txnRefs;
274 if (txnNamespace != null) {
275 txnRefs = bootstrap.getRefsByPrefix(txnNamespace);
276 } else {
277 Ref r = bootstrap.exactRef(txnCommitted);
278 if (r != null && r.getObjectId() != null) {
279 txnRefs = Collections.singleton(r);
280 } else {
281 txnRefs = Collections.emptyList();
282 }
283 }
284
285 List<Ref> otherRefs = bootstrap.getAdditionalRefs();
286 List<Ref> all = new ArrayList<>(txnRefs.size() + otherRefs.size());
287 all.addAll(txnRefs);
288 all.addAll(otherRefs);
289 return all;
290 }
291
292
293 @Override
294 public Ref" href="../../../../../../org/eclipse/jgit/lib/Ref.html#Ref">Ref peel(Ref ref) throws IOException {
295 Ref i = ref.getLeaf();
296 ObjectId id = i.getObjectId();
297 if (i.isPeeled() || id == null) {
298 return ref;
299 }
300 try (RevWalkvwalk/RevWalk.html#RevWalk">RevWalk rw = new RevWalk(repo)) {
301 RevObject obj = rw.parseAny(id);
302 if (obj instanceof RevTag) {
303 ObjectId p = rw.peel(obj).copy();
304 i = new ObjectIdRef.PeeledTag(PACKED, i.getName(), id, p);
305 } else {
306 i = new ObjectIdRef.PeeledNonTag(PACKED, i.getName(), id);
307 }
308 }
309 return recreate(ref, i);
310 }
311
312 private 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) {
313 if (old.isSymbolic()) {
314 Ref dst = recreate(old.getTarget(), leaf);
315 return new SymbolicRef(old.getName(), dst);
316 }
317 return leaf;
318 }
319
320
321 @Override
322 public boolean isNameConflicting(String name) throws IOException {
323 return conflictsWithBootstrap(name)
324 || !getConflictingNames(name).isEmpty();
325 }
326
327
328 @Override
329 public BatchRefUpdate newBatchUpdate() {
330 return new RefTreeBatch(this);
331 }
332
333
334 @Override
335 public RefUpdate newUpdate(String name, boolean detach) throws IOException {
336 if (!repo.isBare() && name.indexOf('/') < 0 && !HEAD.equals(name)) {
337 return bootstrap.newUpdate(name, detach);
338 }
339 if (conflictsWithBootstrap(name)) {
340 return new AlwaysFailUpdate(this, name);
341 }
342
343 Ref r = exactRef(name);
344 if (r == null) {
345 r = new ObjectIdRef.Unpeeled(Storage.NEW, name, null);
346 }
347
348 boolean detaching = detach && r.isSymbolic();
349 if (detaching) {
350 r = new ObjectIdRef.Unpeeled(LOOSE, name, r.getObjectId());
351 }
352
353 RefTreeUpdate u = new RefTreeUpdate(this, r);
354 if (detaching) {
355 u.setDetachingSymbolicRef();
356 }
357 return u;
358 }
359
360
361 @Override
362 public RefRename newRename(String fromName, String toName)
363 throws IOException {
364 RefUpdate from = newUpdate(fromName, true);
365 RefUpdate to = newUpdate(toName, true);
366 return new RefTreeRename(this, from, to);
367 }
368
369 boolean conflictsWithBootstrap(String name) {
370 if (txnNamespace != null && name.startsWith(txnNamespace)) {
371 return true;
372 } else if (txnCommitted.equals(name)) {
373 return true;
374 }
375
376 if (name.indexOf('/') < 0 && !HEAD.equals(name)) {
377 return true;
378 }
379
380 if (name.length() > txnCommitted.length()
381 && name.charAt(txnCommitted.length()) == '/'
382 && name.startsWith(txnCommitted)) {
383 return true;
384 }
385 return false;
386 }
387 }