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.reftable;
45
46 import java.io.IOException;
47 import java.util.List;
48 import java.util.PriorityQueue;
49
50 import org.eclipse.jgit.lib.AnyObjectId;
51 import org.eclipse.jgit.lib.Ref;
52 import org.eclipse.jgit.lib.ReflogEntry;
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 public class MergedReftable extends Reftable {
70 private final Reftable[] tables;
71
72
73
74
75
76
77
78
79
80
81
82
83
84 public MergedReftable(List<Reftable> tableStack) {
85 tables = tableStack.toArray(new Reftable[0]);
86
87
88
89 for (Reftable t : tables) {
90 t.setIncludeDeletes(true);
91 }
92 }
93
94
95 @Override
96 public RefCursor allRefs() throws IOException {
97 MergedRefCursor m = new MergedRefCursor();
98 for (int i = 0; i < tables.length; i++) {
99 m.add(new RefQueueEntry(tables[i].allRefs(), i));
100 }
101 return m;
102 }
103
104
105 @Override
106 public RefCursor seekRef(String name) throws IOException {
107 MergedRefCursor m = new MergedRefCursor();
108 for (int i = 0; i < tables.length; i++) {
109 m.add(new RefQueueEntry(tables[i].seekRef(name), i));
110 }
111 return m;
112 }
113
114
115 @Override
116 public RefCursor byObjectId(AnyObjectId name) throws IOException {
117 MergedRefCursor m = new MergedRefCursor();
118 for (int i = 0; i < tables.length; i++) {
119 m.add(new RefQueueEntry(tables[i].byObjectId(name), i));
120 }
121 return m;
122 }
123
124
125 @Override
126 public LogCursor allLogs() throws IOException {
127 MergedLogCursor m = new MergedLogCursor();
128 for (int i = 0; i < tables.length; i++) {
129 m.add(new LogQueueEntry(tables[i].allLogs(), i));
130 }
131 return m;
132 }
133
134
135 @Override
136 public LogCursor seekLog(String refName, long updateIdx)
137 throws IOException {
138 MergedLogCursor m = new MergedLogCursor();
139 for (int i = 0; i < tables.length; i++) {
140 m.add(new LogQueueEntry(tables[i].seekLog(refName, updateIdx), i));
141 }
142 return m;
143 }
144
145
146 @Override
147 public void close() throws IOException {
148 for (Reftable t : tables) {
149 t.close();
150 }
151 }
152
153 int queueSize() {
154 return Math.max(1, tables.length);
155 }
156
157 private class MergedRefCursor extends RefCursor {
158 private final PriorityQueue<RefQueueEntry> queue;
159 private RefQueueEntry head;
160 private Ref ref;
161 private long updateIndex;
162
163 MergedRefCursor() {
164 queue = new PriorityQueue<>(queueSize(), RefQueueEntry::compare);
165 }
166
167 void add(RefQueueEntry t) throws IOException {
168
169
170
171
172 if (!t.rc.next()) {
173 t.rc.close();
174 } else if (head == null) {
175 RefQueueEntry p = queue.peek();
176 if (p == null || RefQueueEntry.compare(t, p) < 0) {
177 head = t;
178 } else {
179 head = queue.poll();
180 queue.add(t);
181 }
182 } else if (RefQueueEntry.compare(t, head) > 0) {
183 queue.add(t);
184 } else {
185 queue.add(head);
186 head = t;
187 }
188 }
189
190 @Override
191 public boolean next() throws IOException {
192 for (;;) {
193 RefQueueEntry t = poll();
194 if (t == null) {
195 return false;
196 }
197
198 ref = t.rc.getRef();
199 updateIndex = t.rc.getUpdateIndex();
200 boolean include = includeDeletes || !t.rc.wasDeleted();
201 skipShadowedRefs(ref.getName());
202 add(t);
203 if (include) {
204 return true;
205 }
206 }
207 }
208
209 private RefQueueEntry poll() {
210 RefQueueEntry e = head;
211 if (e != null) {
212 head = null;
213 return e;
214 }
215 return queue.poll();
216 }
217
218 private void skipShadowedRefs(String name) throws IOException {
219 for (;;) {
220 RefQueueEntry t = head != null ? head : queue.peek();
221 if (t != null && name.equals(t.name())) {
222 add(poll());
223 } else {
224 break;
225 }
226 }
227 }
228
229 @Override
230 public Ref getRef() {
231 return ref;
232 }
233
234 @Override
235 public long getUpdateIndex() {
236 return updateIndex;
237 }
238
239 @Override
240 public void close() {
241 if (head != null) {
242 head.rc.close();
243 head = null;
244 }
245 while (!queue.isEmpty()) {
246 queue.remove().rc.close();
247 }
248 }
249 }
250
251 private static class RefQueueEntry {
252 static int compare(RefQueueEntry a, RefQueueEntry b) {
253 int cmp = a.name().compareTo(b.name());
254 if (cmp == 0) {
255
256 cmp = Long.signum(b.updateIndex() - a.updateIndex());
257 }
258 if (cmp == 0) {
259
260 cmp = b.stackIdx - a.stackIdx;
261 }
262 return cmp;
263 }
264
265 final RefCursor rc;
266 final int stackIdx;
267
268 RefQueueEntry(RefCursor rc, int stackIdx) {
269 this.rc = rc;
270 this.stackIdx = stackIdx;
271 }
272
273 String name() {
274 return rc.getRef().getName();
275 }
276
277 long updateIndex() {
278 return rc.getUpdateIndex();
279 }
280 }
281
282 private class MergedLogCursor extends LogCursor {
283 private final PriorityQueue<LogQueueEntry> queue;
284 private String refName;
285 private long updateIndex;
286 private ReflogEntry entry;
287
288 MergedLogCursor() {
289 queue = new PriorityQueue<>(queueSize(), LogQueueEntry::compare);
290 }
291
292 void add(LogQueueEntry t) throws IOException {
293 if (t.lc.next()) {
294 queue.add(t);
295 } else {
296 t.lc.close();
297 }
298 }
299
300 @Override
301 public boolean next() throws IOException {
302 for (;;) {
303 LogQueueEntry t = queue.poll();
304 if (t == null) {
305 return false;
306 }
307
308 refName = t.lc.getRefName();
309 updateIndex = t.lc.getUpdateIndex();
310 entry = t.lc.getReflogEntry();
311 boolean include = includeDeletes || entry != null;
312 skipShadowed(refName, updateIndex);
313 add(t);
314 if (include) {
315 return true;
316 }
317 }
318 }
319
320 private void skipShadowed(String name, long index) throws IOException {
321 for (;;) {
322 LogQueueEntry t = queue.peek();
323 if (t != null && name.equals(t.name()) && index == t.index()) {
324 add(queue.remove());
325 } else {
326 break;
327 }
328 }
329 }
330
331 @Override
332 public String getRefName() {
333 return refName;
334 }
335
336 @Override
337 public long getUpdateIndex() {
338 return updateIndex;
339 }
340
341 @Override
342 public ReflogEntry getReflogEntry() {
343 return entry;
344 }
345
346 @Override
347 public void close() {
348 while (!queue.isEmpty()) {
349 queue.remove().lc.close();
350 }
351 }
352 }
353
354 private static class LogQueueEntry {
355 static int compare(LogQueueEntry a, LogQueueEntry b) {
356 int cmp = a.name().compareTo(b.name());
357 if (cmp == 0) {
358
359 cmp = Long.signum(b.index() - a.index());
360 }
361 if (cmp == 0) {
362
363 cmp = b.stackIdx - a.stackIdx;
364 }
365 return cmp;
366 }
367
368 final LogCursor lc;
369 final int stackIdx;
370
371 LogQueueEntry(LogCursor lc, int stackIdx) {
372 this.lc = lc;
373 this.stackIdx = stackIdx;
374 }
375
376 String name() {
377 return lc.getRefName();
378 }
379
380 long index() {
381 return lc.getUpdateIndex();
382 }
383 }
384 }