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.transport;
45
46 import static java.nio.charset.StandardCharsets.UTF_8;
47 import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH;
48 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SYMREF;
49
50 import java.io.IOException;
51 import java.nio.ByteBuffer;
52 import java.nio.CharBuffer;
53 import java.nio.charset.CharacterCodingException;
54 import java.nio.charset.CharsetEncoder;
55 import java.nio.charset.CoderResult;
56 import java.util.Collection;
57 import java.util.HashMap;
58 import java.util.HashSet;
59 import java.util.LinkedHashSet;
60 import java.util.Map;
61 import java.util.Set;
62
63 import org.eclipse.jgit.lib.AnyObjectId;
64 import org.eclipse.jgit.lib.Constants;
65 import org.eclipse.jgit.lib.ObjectId;
66 import org.eclipse.jgit.lib.Ref;
67 import org.eclipse.jgit.lib.RefComparator;
68 import org.eclipse.jgit.lib.Repository;
69
70
71
72
73
74 public abstract class RefAdvertiser {
75
76 public static class PacketLineOutRefAdvertiser extends RefAdvertiser {
77 private final CharsetEncoder utf8 = UTF_8.newEncoder();
78 private final PacketLineOut pckOut;
79
80 private byte[] binArr = new byte[256];
81 private ByteBuffer binBuf = ByteBuffer.wrap(binArr);
82
83 private char[] chArr = new char[256];
84 private CharBuffer chBuf = CharBuffer.wrap(chArr);
85
86
87
88
89
90
91
92 public PacketLineOutRefAdvertiser(PacketLineOut out) {
93 pckOut = out;
94 }
95
96 @Override
97 public void advertiseId(AnyObjectId id, String refName)
98 throws IOException {
99 id.copyTo(binArr, 0);
100 binArr[OBJECT_ID_STRING_LENGTH] = ' ';
101 binBuf.position(OBJECT_ID_STRING_LENGTH + 1);
102 append(refName);
103 if (first) {
104 first = false;
105 if (!capablities.isEmpty()) {
106 append('\0');
107 for (String cap : capablities) {
108 append(' ');
109 append(cap);
110 }
111 }
112 }
113 append('\n');
114 pckOut.writePacket(binArr, 0, binBuf.position());
115 }
116
117 private void append(String str) throws CharacterCodingException {
118 int n = str.length();
119 if (n > chArr.length) {
120 chArr = new char[n + 256];
121 chBuf = CharBuffer.wrap(chArr);
122 }
123 str.getChars(0, n, chArr, 0);
124 chBuf.position(0).limit(n);
125 utf8.reset();
126 for (;;) {
127 CoderResult cr = utf8.encode(chBuf, binBuf, true);
128 if (cr.isOverflow()) {
129 grow();
130 } else if (cr.isUnderflow()) {
131 break;
132 } else {
133 cr.throwException();
134 }
135 }
136 }
137
138 private void append(int b) {
139 if (!binBuf.hasRemaining()) {
140 grow();
141 }
142 binBuf.put((byte) b);
143 }
144
145 private void grow() {
146 int cnt = binBuf.position();
147 byte[] tmp = new byte[binArr.length << 1];
148 System.arraycopy(binArr, 0, tmp, 0, cnt);
149 binArr = tmp;
150 binBuf = ByteBuffer.wrap(binArr);
151 binBuf.position(cnt);
152 }
153
154 @Override
155 protected void writeOne(CharSequence line) throws IOException {
156 pckOut.writeString(line.toString());
157 }
158
159 @Override
160 protected void end() throws IOException {
161 pckOut.end();
162 }
163 }
164
165 private final StringBuilder tmpLine = new StringBuilder(100);
166
167 private final char[] tmpId = new char[Constants.OBJECT_ID_STRING_LENGTH];
168
169 final Set<String> capablities = new LinkedHashSet<>();
170
171 private final Set<ObjectId> sent = new HashSet<>();
172
173 private Repository repository;
174
175 private boolean derefTags;
176
177 boolean first = true;
178
179 private boolean useProtocolV2;
180
181
182 private final Map<String, String> symrefs = new HashMap<>();
183
184
185
186
187
188
189
190 public void init(Repository src) {
191 repository = src;
192 }
193
194
195
196
197
198
199
200 public void setUseProtocolV2(boolean b) {
201 useProtocolV2 = b;
202 }
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218 public void setDerefTags(boolean deref) {
219 derefTags = deref;
220 }
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238 public void advertiseCapability(String name) {
239 capablities.add(name);
240 }
241
242
243
244
245
246
247
248
249
250
251 public void advertiseCapability(String name, String value) {
252 if (value != null) {
253 capablities.add(name + '=' + value);
254 }
255 }
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273 public void addSymref(String from, String to) {
274 if (useProtocolV2) {
275 symrefs.put(from, to);
276 } else {
277 advertiseCapability(OPTION_SYMREF, from + ':' + to);
278 }
279 }
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294 @Deprecated
295 public Set<ObjectId> send(Map<String, Ref> refs) throws IOException {
296 return send(refs.values());
297 }
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312 public Set<ObjectId> send(Collection<Ref> refs) throws IOException {
313 for (Ref ref : RefComparator.sort(refs)) {
314
315
316 ObjectId objectId = ref.getObjectId();
317 if (objectId == null) {
318 continue;
319 }
320
321 if (useProtocolV2) {
322 String symrefPart = symrefs.containsKey(ref.getName())
323 ? (" symref-target:" + symrefs.get(ref.getName()))
324 : "";
325 String peelPart = "";
326 if (derefTags) {
327 if (!ref.isPeeled() && repository != null) {
328 ref = repository.getRefDatabase().peel(ref);
329 }
330 ObjectId peeledObjectId = ref.getPeeledObjectId();
331 if (peeledObjectId != null) {
332 peelPart = " peeled:" + peeledObjectId.getName();
333 }
334 }
335 writeOne(objectId.getName() + " " + ref.getName() + symrefPart
336 + peelPart + "\n");
337 continue;
338 }
339
340 advertiseAny(objectId, ref.getName());
341
342 if (!derefTags)
343 continue;
344
345 if (!ref.isPeeled()) {
346 if (repository == null)
347 continue;
348 ref = repository.getRefDatabase().peel(ref);
349 }
350
351 if (ref.getPeeledObjectId() != null)
352 advertiseAny(ref.getPeeledObjectId(), ref.getName() + "^{}");
353 }
354 return sent;
355 }
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371 public void advertiseHave(AnyObjectId id) throws IOException {
372 advertiseAnyOnce(id, ".have");
373 }
374
375
376
377
378
379
380 public boolean isEmpty() {
381 return first;
382 }
383
384 private void advertiseAnyOnce(AnyObjectId obj, String refName)
385 throws IOException {
386 if (!sent.contains(obj))
387 advertiseAny(obj, refName);
388 }
389
390 private void advertiseAny(AnyObjectId obj, String refName)
391 throws IOException {
392 sent.add(obj.toObjectId());
393 advertiseId(obj, refName);
394 }
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411 public void advertiseId(AnyObjectId id, String refName)
412 throws IOException {
413 tmpLine.setLength(0);
414 id.copyTo(tmpId, tmpLine);
415 tmpLine.append(' ');
416 tmpLine.append(refName);
417 if (first) {
418 first = false;
419 if (!capablities.isEmpty()) {
420 tmpLine.append('\0');
421 for (String capName : capablities) {
422 tmpLine.append(' ');
423 tmpLine.append(capName);
424 }
425 tmpLine.append(' ');
426 }
427 }
428 tmpLine.append('\n');
429 writeOne(tmpLine);
430 }
431
432
433
434
435
436
437
438
439
440
441
442 protected abstract void writeOne(CharSequence line) throws IOException;
443
444
445
446
447
448
449
450
451 protected abstract void end() throws IOException;
452 }