1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.transport;
12
13 import static java.nio.charset.StandardCharsets.UTF_8;
14 import static org.eclipse.jgit.lib.Constants.HEAD;
15 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_ATOMIC;
16 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_DELETE_REFS;
17 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_OFS_DELTA;
18 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_PUSH_OPTIONS;
19 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_QUIET;
20 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REPORT_STATUS;
21 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SIDE_BAND_64K;
22 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
23 import static org.eclipse.jgit.transport.SideBandOutputStream.CH_DATA;
24 import static org.eclipse.jgit.transport.SideBandOutputStream.CH_ERROR;
25 import static org.eclipse.jgit.transport.SideBandOutputStream.CH_PROGRESS;
26 import static org.eclipse.jgit.transport.SideBandOutputStream.MAX_BUF;
27
28 import java.io.EOFException;
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.io.OutputStream;
32 import java.io.UncheckedIOException;
33 import java.text.MessageFormat;
34 import java.util.ArrayList;
35 import java.util.Collections;
36 import java.util.HashSet;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Set;
40 import java.util.concurrent.TimeUnit;
41 import java.util.function.Function;
42 import java.util.stream.Collectors;
43
44 import org.eclipse.jgit.annotations.Nullable;
45 import org.eclipse.jgit.errors.InvalidObjectIdException;
46 import org.eclipse.jgit.errors.LargeObjectException;
47 import org.eclipse.jgit.errors.PackProtocolException;
48 import org.eclipse.jgit.errors.TooLargePackException;
49 import org.eclipse.jgit.errors.UnpackException;
50 import org.eclipse.jgit.internal.JGitText;
51 import org.eclipse.jgit.internal.storage.file.PackLock;
52 import org.eclipse.jgit.internal.submodule.SubmoduleValidator;
53 import org.eclipse.jgit.internal.submodule.SubmoduleValidator.SubmoduleValidationException;
54 import org.eclipse.jgit.internal.transport.connectivity.FullConnectivityChecker;
55 import org.eclipse.jgit.internal.transport.parser.FirstCommand;
56 import org.eclipse.jgit.lib.AnyObjectId;
57 import org.eclipse.jgit.lib.BatchRefUpdate;
58 import org.eclipse.jgit.lib.Config;
59 import org.eclipse.jgit.lib.ConfigConstants;
60 import org.eclipse.jgit.lib.Constants;
61 import org.eclipse.jgit.lib.GitmoduleEntry;
62 import org.eclipse.jgit.lib.NullProgressMonitor;
63 import org.eclipse.jgit.lib.ObjectChecker;
64 import org.eclipse.jgit.lib.ObjectDatabase;
65 import org.eclipse.jgit.lib.ObjectId;
66 import org.eclipse.jgit.lib.ObjectInserter;
67 import org.eclipse.jgit.lib.ObjectLoader;
68 import org.eclipse.jgit.lib.PersonIdent;
69 import org.eclipse.jgit.lib.ProgressMonitor;
70 import org.eclipse.jgit.lib.Ref;
71 import org.eclipse.jgit.lib.Repository;
72 import org.eclipse.jgit.revwalk.RevCommit;
73 import org.eclipse.jgit.revwalk.RevObject;
74 import org.eclipse.jgit.revwalk.RevWalk;
75 import org.eclipse.jgit.transport.ConnectivityChecker.ConnectivityCheckInfo;
76 import org.eclipse.jgit.transport.PacketLineIn.InputOverLimitIOException;
77 import org.eclipse.jgit.transport.ReceiveCommand.Result;
78 import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
79 import org.eclipse.jgit.util.io.InterruptTimer;
80 import org.eclipse.jgit.util.io.LimitedInputStream;
81 import org.eclipse.jgit.util.io.TimeoutInputStream;
82 import org.eclipse.jgit.util.io.TimeoutOutputStream;
83
84
85
86
87 public class ReceivePack {
88
89
90
91
92
93
94 @Deprecated
95 public static class FirstLine {
96 private final FirstCommand command;
97
98
99
100
101
102
103
104 public FirstLine(String line) {
105 command = FirstCommand.fromLine(line);
106 }
107
108
109 public String getLine() {
110 return command.getLine();
111 }
112
113
114 public Set<String> getCapabilities() {
115 return command.getCapabilities();
116 }
117 }
118
119
120 private final Repository db;
121
122
123 private final RevWalk walk;
124
125
126
127
128
129
130
131
132
133
134
135
136 private boolean biDirectionalPipe = true;
137
138
139 private boolean expectDataAfterPackFooter;
140
141
142 private ObjectChecker objectChecker;
143
144
145 private boolean allowCreates;
146
147
148 private boolean allowAnyDeletes;
149
150 private boolean allowBranchDeletes;
151
152
153 private boolean allowNonFastForwards;
154
155
156 private boolean allowPushOptions;
157
158
159
160
161
162 private boolean atomic;
163
164 private boolean allowOfsDelta;
165
166 private boolean allowQuiet = true;
167
168
169 private PersonIdent refLogIdent;
170
171
172 private AdvertiseRefsHook advertiseRefsHook;
173
174
175 private RefFilter refFilter;
176
177
178 private int timeout;
179
180
181 private InterruptTimer timer;
182
183 private TimeoutInputStream timeoutIn;
184
185
186
187 private OutputStream origOut;
188
189
190 private InputStream rawIn;
191
192
193 private OutputStream rawOut;
194
195
196 private OutputStream msgOut;
197
198 private SideBandOutputStream errOut;
199
200
201 private PacketLineIn pckIn;
202
203
204 private PacketLineOut pckOut;
205
206 private final MessageOutputWrapper msgOutWrapper = new MessageOutputWrapper();
207
208 private PackParser parser;
209
210
211 private Map<String, Ref> refs;
212
213
214 private Set<ObjectId> advertisedHaves;
215
216
217 private Set<String> enabledCapabilities;
218
219 String userAgent;
220
221 private Set<ObjectId> clientShallowCommits;
222
223 private List<ReceiveCommand> commands;
224
225 private long maxCommandBytes;
226
227 private long maxDiscardBytes;
228
229 private StringBuilder advertiseError;
230
231
232
233
234 private boolean sideBand;
235
236 private boolean quiet;
237
238
239 private PackLock packLock;
240
241 private boolean checkReferencedAreReachable;
242
243
244 private long maxObjectSizeLimit;
245
246
247 private long maxPackSizeLimit = -1;
248
249
250 private Long packSize;
251
252 private PushCertificateParser pushCertificateParser;
253
254 private SignedPushConfig signedPushConfig;
255
256 private PushCertificate pushCert;
257
258 private ReceivedPackStatistics stats;
259
260
261
262
263
264 protected ConnectivityChecker connectivityChecker = new FullConnectivityChecker();
265
266
267 private PreReceiveHook preReceive;
268
269 private ReceiveCommandErrorHandler receiveCommandErrorHandler = new ReceiveCommandErrorHandler() {
270
271 };
272
273 private UnpackErrorHandler unpackErrorHandler = new DefaultUnpackErrorHandler();
274
275
276 private PostReceiveHook postReceive;
277
278
279 private boolean reportStatus;
280
281
282 private boolean usePushOptions;
283 private List<String> pushOptions;
284
285
286
287
288
289
290
291 public ReceivePack(Repository into) {
292 db = into;
293 walk = new RevWalk(db);
294 walk.setRetainBody(false);
295
296 TransferConfig tc = db.getConfig().get(TransferConfig.KEY);
297 objectChecker = tc.newReceiveObjectChecker();
298
299 ReceiveConfig rc = db.getConfig().get(ReceiveConfig::new);
300 allowCreates = rc.allowCreates;
301 allowAnyDeletes = true;
302 allowBranchDeletes = rc.allowDeletes;
303 allowNonFastForwards = rc.allowNonFastForwards;
304 allowOfsDelta = rc.allowOfsDelta;
305 allowPushOptions = rc.allowPushOptions;
306 maxCommandBytes = rc.maxCommandBytes;
307 maxDiscardBytes = rc.maxDiscardBytes;
308 advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
309 refFilter = RefFilter.DEFAULT;
310 advertisedHaves = new HashSet<>();
311 clientShallowCommits = new HashSet<>();
312 signedPushConfig = rc.signedPush;
313 preReceive = PreReceiveHook.NULL;
314 postReceive = PostReceiveHook.NULL;
315 }
316
317
318 private static class ReceiveConfig {
319 final boolean allowCreates;
320
321 final boolean allowDeletes;
322
323 final boolean allowNonFastForwards;
324
325 final boolean allowOfsDelta;
326
327 final boolean allowPushOptions;
328
329 final long maxCommandBytes;
330
331 final long maxDiscardBytes;
332
333 final SignedPushConfig signedPush;
334
335 ReceiveConfig(Config config) {
336 allowCreates = true;
337 allowDeletes = !config.getBoolean("receive", "denydeletes", false);
338 allowNonFastForwards = !config.getBoolean("receive",
339 "denynonfastforwards", false);
340 allowOfsDelta = config.getBoolean("repack", "usedeltabaseoffset",
341 true);
342 allowPushOptions = config.getBoolean("receive", "pushoptions",
343 false);
344 maxCommandBytes = config.getLong("receive",
345 "maxCommandBytes",
346 3 << 20);
347 maxDiscardBytes = config.getLong("receive",
348 "maxCommandDiscardBytes",
349 -1);
350 signedPush = SignedPushConfig.KEY.parse(config);
351 }
352 }
353
354
355
356
357
358
359
360 class MessageOutputWrapper extends OutputStream {
361 @Override
362 public void write(int ch) {
363 if (msgOut != null) {
364 try {
365 msgOut.write(ch);
366 } catch (IOException e) {
367
368 }
369 }
370 }
371
372 @Override
373 public void write(byte[] b, int off, int len) {
374 if (msgOut != null) {
375 try {
376 msgOut.write(b, off, len);
377 } catch (IOException e) {
378
379 }
380 }
381 }
382
383 @Override
384 public void write(byte[] b) {
385 write(b, 0, b.length);
386 }
387
388 @Override
389 public void flush() {
390 if (msgOut != null) {
391 try {
392 msgOut.flush();
393 } catch (IOException e) {
394
395 }
396 }
397 }
398 }
399
400
401
402
403
404
405 public Repository getRepository() {
406 return db;
407 }
408
409
410
411
412
413
414 public RevWalk getRevWalk() {
415 return walk;
416 }
417
418
419
420
421
422
423
424 public Map<String, Ref> getAdvertisedRefs() {
425 return refs;
426 }
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445 public void setAdvertisedRefs(Map<String, Ref> allRefs,
446 Set<ObjectId> additionalHaves) {
447 refs = allRefs != null ? allRefs : getAllRefs();
448 refs = refFilter.filter(refs);
449 advertisedHaves.clear();
450
451 Ref head = refs.get(HEAD);
452 if (head != null && head.isSymbolic()) {
453 refs.remove(HEAD);
454 }
455
456 for (Ref ref : refs.values()) {
457 if (ref.getObjectId() != null) {
458 advertisedHaves.add(ref.getObjectId());
459 }
460 }
461 if (additionalHaves != null) {
462 advertisedHaves.addAll(additionalHaves);
463 } else {
464 advertisedHaves.addAll(db.getAdditionalHaves());
465 }
466 }
467
468
469
470
471
472
473
474
475 public final Set<ObjectId> getAdvertisedObjects() {
476 return advertisedHaves;
477 }
478
479
480
481
482
483
484
485
486
487 public boolean isCheckReferencedObjectsAreReachable() {
488 return checkReferencedAreReachable;
489 }
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512 public void setCheckReferencedObjectsAreReachable(boolean b) {
513 this.checkReferencedAreReachable = b;
514 }
515
516
517
518
519
520
521
522
523 public boolean isBiDirectionalPipe() {
524 return biDirectionalPipe;
525 }
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540 public void setBiDirectionalPipe(boolean twoWay) {
541 biDirectionalPipe = twoWay;
542 }
543
544
545
546
547
548
549 public boolean isExpectDataAfterPackFooter() {
550 return expectDataAfterPackFooter;
551 }
552
553
554
555
556
557
558
559
560 public void setExpectDataAfterPackFooter(boolean e) {
561 expectDataAfterPackFooter = e;
562 }
563
564
565
566
567
568
569
570
571
572 public boolean isCheckReceivedObjects() {
573 return objectChecker != null;
574 }
575
576
577
578
579
580
581
582
583
584 public void setCheckReceivedObjects(boolean check) {
585 if (check && objectChecker == null)
586 setObjectChecker(new ObjectChecker());
587 else if (!check && objectChecker != null)
588 setObjectChecker(null);
589 }
590
591
592
593
594
595
596
597
598
599 public void setObjectChecker(ObjectChecker impl) {
600 objectChecker = impl;
601 }
602
603
604
605
606
607
608 public boolean isAllowCreates() {
609 return allowCreates;
610 }
611
612
613
614
615
616
617
618 public void setAllowCreates(boolean canCreate) {
619 allowCreates = canCreate;
620 }
621
622
623
624
625
626
627 public boolean isAllowDeletes() {
628 return allowAnyDeletes;
629 }
630
631
632
633
634
635
636
637 public void setAllowDeletes(boolean canDelete) {
638 allowAnyDeletes = canDelete;
639 }
640
641
642
643
644
645
646
647 public boolean isAllowBranchDeletes() {
648 return allowBranchDeletes;
649 }
650
651
652
653
654
655
656
657
658
659
660 public void setAllowBranchDeletes(boolean canDelete) {
661 allowBranchDeletes = canDelete;
662 }
663
664
665
666
667
668
669
670
671 public boolean isAllowNonFastForwards() {
672 return allowNonFastForwards;
673 }
674
675
676
677
678
679
680
681
682
683 public void setAllowNonFastForwards(boolean canRewind) {
684 allowNonFastForwards = canRewind;
685 }
686
687
688
689
690
691
692
693
694
695 public boolean isAtomic() {
696 return atomic;
697 }
698
699
700
701
702
703
704
705
706
707
708 public void setAtomic(boolean atomic) {
709 this.atomic = atomic;
710 }
711
712
713
714
715
716
717 public PersonIdent getRefLogIdent() {
718 return refLogIdent;
719 }
720
721
722
723
724
725
726
727
728
729
730
731
732
733 public void setRefLogIdent(PersonIdent pi) {
734 refLogIdent = pi;
735 }
736
737
738
739
740
741
742 public AdvertiseRefsHook getAdvertiseRefsHook() {
743 return advertiseRefsHook;
744 }
745
746
747
748
749
750
751 public RefFilter getRefFilter() {
752 return refFilter;
753 }
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770 public void setAdvertiseRefsHook(AdvertiseRefsHook advertiseRefsHook) {
771 if (advertiseRefsHook != null)
772 this.advertiseRefsHook = advertiseRefsHook;
773 else
774 this.advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
775 }
776
777
778
779
780
781
782
783
784
785
786
787 public void setRefFilter(RefFilter refFilter) {
788 this.refFilter = refFilter != null ? refFilter : RefFilter.DEFAULT;
789 }
790
791
792
793
794
795
796 public int getTimeout() {
797 return timeout;
798 }
799
800
801
802
803
804
805
806
807
808 public void setTimeout(int seconds) {
809 timeout = seconds;
810 }
811
812
813
814
815
816
817
818
819 public void setMaxCommandBytes(long limit) {
820 maxCommandBytes = limit;
821 }
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840 public void setMaxCommandDiscardBytes(long limit) {
841 maxDiscardBytes = limit;
842 }
843
844
845
846
847
848
849
850
851
852
853 public void setMaxObjectSizeLimit(long limit) {
854 maxObjectSizeLimit = limit;
855 }
856
857
858
859
860
861
862
863
864
865
866 public void setMaxPackSizeLimit(long limit) {
867 if (limit < 0)
868 throw new IllegalArgumentException(
869 MessageFormat.format(JGitText.get().receivePackInvalidLimit,
870 Long.valueOf(limit)));
871 maxPackSizeLimit = limit;
872 }
873
874
875
876
877
878
879
880
881
882
883
884
885 public boolean isSideBand() throws RequestNotYetReadException {
886 checkRequestWasRead();
887 return enabledCapabilities.contains(CAPABILITY_SIDE_BAND_64K);
888 }
889
890
891
892
893
894
895
896 public boolean isAllowQuiet() {
897 return allowQuiet;
898 }
899
900
901
902
903
904
905
906
907
908
909
910 public void setAllowQuiet(boolean allow) {
911 allowQuiet = allow;
912 }
913
914
915
916
917
918
919
920 public boolean isAllowPushOptions() {
921 return allowPushOptions;
922 }
923
924
925
926
927
928
929
930
931 public void setAllowPushOptions(boolean allow) {
932 allowPushOptions = allow;
933 }
934
935
936
937
938
939
940
941
942
943
944
945
946 public boolean isQuiet() throws RequestNotYetReadException {
947 checkRequestWasRead();
948 return quiet;
949 }
950
951
952
953
954
955
956
957
958
959
960 public void setSignedPushConfig(SignedPushConfig cfg) {
961 signedPushConfig = cfg;
962 }
963
964 private PushCertificateParser getPushCertificateParser() {
965 if (pushCertificateParser == null) {
966 pushCertificateParser = new PushCertificateParser(db,
967 signedPushConfig);
968 }
969 return pushCertificateParser;
970 }
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987 public String getPeerUserAgent() {
988 return UserAgent.getAgent(enabledCapabilities, userAgent);
989 }
990
991
992
993
994
995
996 public List<ReceiveCommand> getAllCommands() {
997 return Collections.unmodifiableList(commands);
998 }
999
1000
1001
1002
1003
1004
1005
1006 public void setReceiveCommandErrorHandler(
1007 ReceiveCommandErrorHandler receiveCommandErrorHandler) {
1008 this.receiveCommandErrorHandler = receiveCommandErrorHandler;
1009 }
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036 public void sendError(String what) {
1037 if (refs == null) {
1038 if (advertiseError == null)
1039 advertiseError = new StringBuilder();
1040 advertiseError.append(what).append('\n');
1041 } else {
1042 msgOutWrapper.write(Constants.encode("error: " + what + "\n"));
1043 }
1044 }
1045
1046 private void fatalError(String msg) {
1047 if (errOut != null) {
1048 try {
1049 errOut.write(Constants.encode(msg));
1050 errOut.flush();
1051 } catch (IOException e) {
1052
1053 }
1054 } else {
1055 sendError(msg);
1056 }
1057 }
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069 public void sendMessage(String what) {
1070 msgOutWrapper.write(Constants.encode(what + "\n"));
1071 }
1072
1073
1074
1075
1076
1077
1078 public OutputStream getMessageOutputStream() {
1079 return msgOutWrapper;
1080 }
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092 public boolean hasReceivedPack() {
1093 return packSize != null;
1094 }
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106 public long getPackSize() {
1107 if (packSize != null)
1108 return packSize.longValue();
1109 throw new IllegalStateException(JGitText.get().packSizeNotSetYet);
1110 }
1111
1112
1113
1114
1115
1116
1117
1118
1119 private Set<ObjectId> getClientShallowCommits() {
1120 return clientShallowCommits;
1121 }
1122
1123
1124
1125
1126
1127
1128 private boolean hasCommands() {
1129 return !commands.isEmpty();
1130 }
1131
1132
1133
1134
1135
1136
1137 private boolean hasError() {
1138 return advertiseError != null;
1139 }
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161 protected void init(final InputStream input, final OutputStream output,
1162 final OutputStream messages) {
1163 origOut = output;
1164 rawIn = input;
1165 rawOut = output;
1166 msgOut = messages;
1167
1168 if (timeout > 0) {
1169 final Thread caller = Thread.currentThread();
1170 timer = new InterruptTimer(caller.getName() + "-Timer");
1171 timeoutIn = new TimeoutInputStream(rawIn, timer);
1172 TimeoutOutputStream o = new TimeoutOutputStream(rawOut, timer);
1173 timeoutIn.setTimeout(timeout * 1000);
1174 o.setTimeout(timeout * 1000);
1175 rawIn = timeoutIn;
1176 rawOut = o;
1177 }
1178
1179 pckIn = new PacketLineIn(rawIn);
1180 pckOut = new PacketLineOut(rawOut);
1181 pckOut.setFlushOnEnd(false);
1182
1183 enabledCapabilities = new HashSet<>();
1184 commands = new ArrayList<>();
1185 }
1186
1187
1188
1189
1190
1191
1192 private Map<String, Ref> getAdvertisedOrDefaultRefs() {
1193 if (refs == null)
1194 setAdvertisedRefs(null, null);
1195 return refs;
1196 }
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213 protected void receivePackAndCheckConnectivity() throws IOException,
1214 LargeObjectException, SubmoduleValidationException {
1215 receivePack();
1216 if (needCheckConnectivity()) {
1217 checkSubmodules();
1218 checkConnectivity();
1219 }
1220 parser = null;
1221 }
1222
1223
1224
1225
1226
1227
1228
1229 private void unlockPack() throws IOException {
1230 if (packLock != null) {
1231 packLock.unlock();
1232 packLock = null;
1233 }
1234 }
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246 public void sendAdvertisedRefs(RefAdvertiser adv)
1247 throws IOException, ServiceMayNotContinueException {
1248 if (advertiseError != null) {
1249 adv.writeOne("ERR " + advertiseError);
1250 return;
1251 }
1252
1253 try {
1254 advertiseRefsHook.advertiseRefs(this);
1255 } catch (ServiceMayNotContinueException fail) {
1256 if (fail.getMessage() != null) {
1257 adv.writeOne("ERR " + fail.getMessage());
1258 fail.setOutput();
1259 }
1260 throw fail;
1261 }
1262
1263 adv.init(db);
1264 adv.advertiseCapability(CAPABILITY_SIDE_BAND_64K);
1265 adv.advertiseCapability(CAPABILITY_DELETE_REFS);
1266 adv.advertiseCapability(CAPABILITY_REPORT_STATUS);
1267 if (allowQuiet)
1268 adv.advertiseCapability(CAPABILITY_QUIET);
1269 String nonce = getPushCertificateParser().getAdvertiseNonce();
1270 if (nonce != null) {
1271 adv.advertiseCapability(nonce);
1272 }
1273 if (db.getRefDatabase().performsAtomicTransactions())
1274 adv.advertiseCapability(CAPABILITY_ATOMIC);
1275 if (allowOfsDelta)
1276 adv.advertiseCapability(CAPABILITY_OFS_DELTA);
1277 if (allowPushOptions) {
1278 adv.advertiseCapability(CAPABILITY_PUSH_OPTIONS);
1279 }
1280 adv.advertiseCapability(OPTION_AGENT, UserAgent.get());
1281 adv.send(getAdvertisedOrDefaultRefs().values());
1282 for (ObjectId obj : advertisedHaves)
1283 adv.advertiseHave(obj);
1284 if (adv.isEmpty())
1285 adv.advertiseId(ObjectId.zeroId(), "capabilities^{}");
1286 adv.end();
1287 }
1288
1289
1290
1291
1292
1293
1294
1295
1296 @Nullable
1297 public ReceivedPackStatistics getReceivedPackStatistics() {
1298 return stats;
1299 }
1300
1301
1302
1303
1304
1305
1306 private Map<String, Ref> getAllRefs() {
1307 try {
1308 return db.getRefDatabase().getRefs().stream()
1309 .collect(Collectors.toMap(Ref::getName,
1310 Function.identity()));
1311 } catch (IOException e) {
1312 throw new UncheckedIOException(e);
1313 }
1314 }
1315
1316
1317
1318
1319
1320
1321 private void recvCommands() throws IOException {
1322 PacketLineIn pck = maxCommandBytes > 0
1323 ? new PacketLineIn(rawIn, maxCommandBytes)
1324 : pckIn;
1325 PushCertificateParser certParser = getPushCertificateParser();
1326 boolean firstPkt = true;
1327 try {
1328 for (;;) {
1329 String line;
1330 try {
1331 line = pck.readString();
1332 } catch (EOFException eof) {
1333 if (commands.isEmpty())
1334 return;
1335 throw eof;
1336 }
1337 if (PacketLineIn.isEnd(line)) {
1338 break;
1339 }
1340
1341 if (line.length() >= 48 && line.startsWith("shallow ")) {
1342 parseShallow(line.substring(8, 48));
1343 continue;
1344 }
1345
1346 if (firstPkt) {
1347 firstPkt = false;
1348 FirstCommand firstLine = FirstCommand.fromLine(line);
1349 enabledCapabilities = firstLine.getCapabilities();
1350 line = firstLine.getLine();
1351 enableCapabilities();
1352
1353 if (line.equals(GitProtocolConstants.OPTION_PUSH_CERT)) {
1354 certParser.receiveHeader(pck, !isBiDirectionalPipe());
1355 continue;
1356 }
1357 }
1358
1359 if (line.equals(PushCertificateParser.BEGIN_SIGNATURE)) {
1360 certParser.receiveSignature(pck);
1361 continue;
1362 }
1363
1364 ReceiveCommand cmd = parseCommand(line);
1365 if (cmd.getRefName().equals(Constants.HEAD)) {
1366 cmd.setResult(Result.REJECTED_CURRENT_BRANCH);
1367 } else {
1368 cmd.setRef(refs.get(cmd.getRefName()));
1369 }
1370 commands.add(cmd);
1371 if (certParser.enabled()) {
1372 certParser.addCommand(cmd);
1373 }
1374 }
1375 pushCert = certParser.build();
1376 if (hasCommands()) {
1377 readPostCommands(pck);
1378 }
1379 } catch (Throwable t) {
1380 discardCommands();
1381 throw t;
1382 }
1383 }
1384
1385 private void discardCommands() {
1386 if (sideBand) {
1387 long max = maxDiscardBytes;
1388 if (max < 0) {
1389 max = Math.max(3 * maxCommandBytes, 3L << 20);
1390 }
1391 try {
1392 new PacketLineIn(rawIn, max).discardUntilEnd();
1393 } catch (IOException e) {
1394
1395 }
1396 }
1397 }
1398
1399 private void parseShallow(String idStr) throws PackProtocolException {
1400 ObjectId id;
1401 try {
1402 id = ObjectId.fromString(idStr);
1403 } catch (InvalidObjectIdException e) {
1404 throw new PackProtocolException(e.getMessage(), e);
1405 }
1406 clientShallowCommits.add(id);
1407 }
1408
1409
1410
1411
1412
1413
1414
1415 void readPostCommands(PacketLineIn in) throws IOException {
1416 if (usePushOptions) {
1417 pushOptions = new ArrayList<>(4);
1418 for (;;) {
1419 String option = in.readString();
1420 if (PacketLineIn.isEnd(option)) {
1421 break;
1422 }
1423 pushOptions.add(option);
1424 }
1425 }
1426 }
1427
1428
1429
1430
1431 private void enableCapabilities() {
1432 reportStatus = isCapabilityEnabled(CAPABILITY_REPORT_STATUS);
1433 usePushOptions = isCapabilityEnabled(CAPABILITY_PUSH_OPTIONS);
1434 sideBand = isCapabilityEnabled(CAPABILITY_SIDE_BAND_64K);
1435 quiet = allowQuiet && isCapabilityEnabled(CAPABILITY_QUIET);
1436 if (sideBand) {
1437 OutputStream out = rawOut;
1438
1439 rawOut = new SideBandOutputStream(CH_DATA, MAX_BUF, out);
1440 msgOut = new SideBandOutputStream(CH_PROGRESS, MAX_BUF, out);
1441 errOut = new SideBandOutputStream(CH_ERROR, MAX_BUF, out);
1442
1443 pckOut = new PacketLineOut(rawOut);
1444 pckOut.setFlushOnEnd(false);
1445 }
1446 }
1447
1448
1449
1450
1451
1452
1453
1454
1455 private boolean isCapabilityEnabled(String name) {
1456 return enabledCapabilities.contains(name);
1457 }
1458
1459 private void checkRequestWasRead() {
1460 if (enabledCapabilities == null)
1461 throw new RequestNotYetReadException();
1462 }
1463
1464
1465
1466
1467
1468
1469 private boolean needPack() {
1470 for (ReceiveCommand cmd : commands) {
1471 if (cmd.getType() != ReceiveCommand.Type.DELETE)
1472 return true;
1473 }
1474 return false;
1475 }
1476
1477
1478
1479
1480
1481
1482
1483 private void receivePack() throws IOException {
1484
1485
1486
1487
1488 if (timeoutIn != null)
1489 timeoutIn.setTimeout(10 * timeout * 1000);
1490
1491 ProgressMonitor receiving = NullProgressMonitor.INSTANCE;
1492 ProgressMonitor resolving = NullProgressMonitor.INSTANCE;
1493 if (sideBand && !quiet)
1494 resolving = new SideBandProgressMonitor(msgOut);
1495
1496 try (ObjectInserter ins = db.newObjectInserter()) {
1497 String lockMsg = "jgit receive-pack";
1498 if (getRefLogIdent() != null)
1499 lockMsg += " from " + getRefLogIdent().toExternalString();
1500
1501 parser = ins.newPackParser(packInputStream());
1502 parser.setAllowThin(true);
1503 parser.setNeedNewObjectIds(checkReferencedAreReachable);
1504 parser.setNeedBaseObjectIds(checkReferencedAreReachable);
1505 parser.setCheckEofAfterPackFooter(!biDirectionalPipe
1506 && !isExpectDataAfterPackFooter());
1507 parser.setExpectDataAfterPackFooter(isExpectDataAfterPackFooter());
1508 parser.setObjectChecker(objectChecker);
1509 parser.setLockMessage(lockMsg);
1510 parser.setMaxObjectSizeLimit(maxObjectSizeLimit);
1511 packLock = parser.parse(receiving, resolving);
1512 packSize = Long.valueOf(parser.getPackSize());
1513 stats = parser.getReceivedPackStatistics();
1514 ins.flush();
1515 }
1516
1517 if (timeoutIn != null)
1518 timeoutIn.setTimeout(timeout * 1000);
1519 }
1520
1521 private InputStream packInputStream() {
1522 InputStream packIn = rawIn;
1523 if (maxPackSizeLimit >= 0) {
1524 packIn = new LimitedInputStream(packIn, maxPackSizeLimit) {
1525 @Override
1526 protected void limitExceeded() throws TooLargePackException {
1527 throw new TooLargePackException(limit);
1528 }
1529 };
1530 }
1531 return packIn;
1532 }
1533
1534 private boolean needCheckConnectivity() {
1535 return isCheckReceivedObjects()
1536 || isCheckReferencedObjectsAreReachable()
1537 || !getClientShallowCommits().isEmpty();
1538 }
1539
1540 private void checkSubmodules() throws IOException, LargeObjectException,
1541 SubmoduleValidationException {
1542 ObjectDatabase odb = db.getObjectDatabase();
1543 if (objectChecker == null) {
1544 return;
1545 }
1546 for (GitmoduleEntry entry : objectChecker.getGitsubmodules()) {
1547 AnyObjectId blobId = entry.getBlobId();
1548 ObjectLoader blob = odb.open(blobId, Constants.OBJ_BLOB);
1549
1550 SubmoduleValidator.assertValidGitModulesFile(
1551 new String(blob.getBytes(), UTF_8));
1552 }
1553 }
1554
1555 private void checkConnectivity() throws IOException {
1556 ProgressMonitor checking = NullProgressMonitor.INSTANCE;
1557 if (sideBand && !quiet) {
1558 SideBandProgressMonitor m = new SideBandProgressMonitor(msgOut);
1559 m.setDelayStart(750, TimeUnit.MILLISECONDS);
1560 checking = m;
1561 }
1562
1563 connectivityChecker.checkConnectivity(createConnectivityCheckInfo(),
1564 advertisedHaves, checking);
1565 }
1566
1567 private ConnectivityCheckInfo createConnectivityCheckInfo() {
1568 ConnectivityCheckInfo info = new ConnectivityCheckInfo();
1569 info.setCheckObjects(checkReferencedAreReachable);
1570 info.setCommands(getAllCommands());
1571 info.setRepository(db);
1572 info.setParser(parser);
1573 info.setWalk(walk);
1574 return info;
1575 }
1576
1577
1578
1579
1580 private void validateCommands() {
1581 for (ReceiveCommand cmd : commands) {
1582 final Ref ref = cmd.getRef();
1583 if (cmd.getResult() != Result.NOT_ATTEMPTED)
1584 continue;
1585
1586 if (cmd.getType() == ReceiveCommand.Type.DELETE) {
1587 if (!isAllowDeletes()) {
1588
1589 cmd.setResult(Result.REJECTED_NODELETE);
1590 continue;
1591 }
1592 if (!isAllowBranchDeletes()
1593 && ref.getName().startsWith(Constants.R_HEADS)) {
1594
1595 cmd.setResult(Result.REJECTED_NODELETE);
1596 continue;
1597 }
1598 }
1599
1600 if (cmd.getType() == ReceiveCommand.Type.CREATE) {
1601 if (!isAllowCreates()) {
1602 cmd.setResult(Result.REJECTED_NOCREATE);
1603 continue;
1604 }
1605
1606 if (ref != null && !isAllowNonFastForwards()) {
1607
1608
1609
1610 cmd.setResult(Result.REJECTED_NONFASTFORWARD);
1611 continue;
1612 }
1613
1614 if (ref != null) {
1615
1616
1617
1618 cmd.setResult(Result.REJECTED_OTHER_REASON,
1619 JGitText.get().refAlreadyExists);
1620 continue;
1621 }
1622 }
1623
1624 if (cmd.getType() == ReceiveCommand.Type.DELETE && ref != null) {
1625 ObjectId id = ref.getObjectId();
1626 if (id == null) {
1627 id = ObjectId.zeroId();
1628 }
1629 if (!ObjectId.zeroId().equals(cmd.getOldId())
1630 && !id.equals(cmd.getOldId())) {
1631
1632
1633
1634
1635 cmd.setResult(Result.REJECTED_OTHER_REASON,
1636 JGitText.get().invalidOldIdSent);
1637 continue;
1638 }
1639 }
1640
1641 if (cmd.getType() == ReceiveCommand.Type.UPDATE) {
1642 if (ref == null) {
1643
1644
1645 cmd.setResult(Result.REJECTED_OTHER_REASON,
1646 JGitText.get().noSuchRef);
1647 continue;
1648 }
1649 ObjectId id = ref.getObjectId();
1650 if (id == null) {
1651
1652 cmd.setResult(Result.REJECTED_OTHER_REASON,
1653 JGitText.get().cannotUpdateUnbornBranch);
1654 continue;
1655 }
1656
1657 if (!id.equals(cmd.getOldId())) {
1658
1659
1660
1661 cmd.setResult(Result.REJECTED_OTHER_REASON,
1662 JGitText.get().invalidOldIdSent);
1663 continue;
1664 }
1665
1666
1667
1668 RevObject oldObj, newObj;
1669 try {
1670 oldObj = walk.parseAny(cmd.getOldId());
1671 } catch (IOException e) {
1672 receiveCommandErrorHandler
1673 .handleOldIdValidationException(cmd, e);
1674 continue;
1675 }
1676
1677 try {
1678 newObj = walk.parseAny(cmd.getNewId());
1679 } catch (IOException e) {
1680 receiveCommandErrorHandler
1681 .handleNewIdValidationException(cmd, e);
1682 continue;
1683 }
1684
1685 if (oldObj instanceof RevCommit
1686 && newObj instanceof RevCommit) {
1687 try {
1688 if (walk.isMergedInto((RevCommit) oldObj,
1689 (RevCommit) newObj)) {
1690 cmd.setTypeFastForwardUpdate();
1691 } else {
1692 cmd.setType(ReceiveCommand.Type.UPDATE_NONFASTFORWARD);
1693 }
1694 } catch (IOException e) {
1695 receiveCommandErrorHandler
1696 .handleFastForwardCheckException(cmd, e);
1697 }
1698 } else {
1699 cmd.setType(ReceiveCommand.Type.UPDATE_NONFASTFORWARD);
1700 }
1701
1702 if (cmd.getType() == ReceiveCommand.Type.UPDATE_NONFASTFORWARD
1703 && !isAllowNonFastForwards()) {
1704 cmd.setResult(Result.REJECTED_NONFASTFORWARD);
1705 continue;
1706 }
1707 }
1708
1709 if (!cmd.getRefName().startsWith(Constants.R_REFS)
1710 || !Repository.isValidRefName(cmd.getRefName())) {
1711 cmd.setResult(Result.REJECTED_OTHER_REASON,
1712 JGitText.get().funnyRefname);
1713 }
1714 }
1715 }
1716
1717
1718
1719
1720
1721
1722 private boolean anyRejects() {
1723 for (ReceiveCommand cmd : commands) {
1724 if (cmd.getResult() != Result.NOT_ATTEMPTED
1725 && cmd.getResult() != Result.OK)
1726 return true;
1727 }
1728 return false;
1729 }
1730
1731
1732
1733
1734
1735 private void failPendingCommands() {
1736 ReceiveCommand.abort(commands);
1737 }
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748 protected List<ReceiveCommand> filterCommands(Result want) {
1749 return ReceiveCommand.filter(commands, want);
1750 }
1751
1752
1753
1754
1755
1756 protected void executeCommands() {
1757 List<ReceiveCommand> toApply = filterCommands(Result.NOT_ATTEMPTED);
1758 if (toApply.isEmpty())
1759 return;
1760
1761 ProgressMonitor updating = NullProgressMonitor.INSTANCE;
1762 if (sideBand) {
1763 SideBandProgressMonitor pm = new SideBandProgressMonitor(msgOut);
1764 pm.setDelayStart(250, TimeUnit.MILLISECONDS);
1765 updating = pm;
1766 }
1767
1768 BatchRefUpdate batch = db.getRefDatabase().newBatchUpdate();
1769 batch.setAllowNonFastForwards(isAllowNonFastForwards());
1770 batch.setAtomic(isAtomic());
1771 batch.setRefLogIdent(getRefLogIdent());
1772 batch.setRefLogMessage("push", true);
1773 batch.addCommand(toApply);
1774 try {
1775 batch.setPushCertificate(getPushCertificate());
1776 batch.execute(walk, updating);
1777 } catch (IOException e) {
1778 receiveCommandErrorHandler.handleBatchRefUpdateException(toApply,
1779 e);
1780 }
1781 }
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792 private void sendStatusReport(Throwable unpackError) throws IOException {
1793 Reporter out = new Reporter() {
1794 @Override
1795 void sendString(String s) throws IOException {
1796 if (reportStatus) {
1797 pckOut.writeString(s + "\n");
1798 } else if (msgOut != null) {
1799 msgOut.write(Constants.encode(s + "\n"));
1800 }
1801 }
1802 };
1803
1804 try {
1805 if (unpackError != null) {
1806 out.sendString("unpack error " + unpackError.getMessage());
1807 if (reportStatus) {
1808 for (ReceiveCommand cmd : commands) {
1809 out.sendString("ng " + cmd.getRefName()
1810 + " n/a (unpacker error)");
1811 }
1812 }
1813 return;
1814 }
1815
1816 if (reportStatus) {
1817 out.sendString("unpack ok");
1818 }
1819 for (ReceiveCommand cmd : commands) {
1820 if (cmd.getResult() == Result.OK) {
1821 if (reportStatus) {
1822 out.sendString("ok " + cmd.getRefName());
1823 }
1824 continue;
1825 }
1826
1827 final StringBuilder r = new StringBuilder();
1828 if (reportStatus) {
1829 r.append("ng ").append(cmd.getRefName()).append(" ");
1830 } else {
1831 r.append(" ! [rejected] ").append(cmd.getRefName())
1832 .append(" (");
1833 }
1834
1835 if (cmd.getResult() == Result.REJECTED_MISSING_OBJECT) {
1836 if (cmd.getMessage() == null)
1837 r.append("missing object(s)");
1838 else if (cmd.getMessage()
1839 .length() == Constants.OBJECT_ID_STRING_LENGTH) {
1840
1841
1842 r.append("object ");
1843 r.append(cmd.getMessage());
1844 r.append(" missing");
1845 } else {
1846 r.append(cmd.getMessage());
1847 }
1848 } else if (cmd.getMessage() != null) {
1849 r.append(cmd.getMessage());
1850 } else {
1851 switch (cmd.getResult()) {
1852 case NOT_ATTEMPTED:
1853 r.append("server bug; ref not processed");
1854 break;
1855
1856 case REJECTED_NOCREATE:
1857 r.append("creation prohibited");
1858 break;
1859
1860 case REJECTED_NODELETE:
1861 r.append("deletion prohibited");
1862 break;
1863
1864 case REJECTED_NONFASTFORWARD:
1865 r.append("non-fast forward");
1866 break;
1867
1868 case REJECTED_CURRENT_BRANCH:
1869 r.append("branch is currently checked out");
1870 break;
1871
1872 case REJECTED_OTHER_REASON:
1873 r.append("unspecified reason");
1874 break;
1875
1876 case LOCK_FAILURE:
1877 r.append("failed to lock");
1878 break;
1879
1880 case REJECTED_MISSING_OBJECT:
1881 case OK:
1882
1883
1884 throw new AssertionError();
1885 }
1886 }
1887
1888 if (!reportStatus) {
1889 r.append(")");
1890 }
1891 out.sendString(r.toString());
1892 }
1893 } finally {
1894 if (reportStatus) {
1895 pckOut.end();
1896 }
1897 }
1898 }
1899
1900
1901
1902
1903
1904
1905 private void close() throws IOException {
1906 if (sideBand) {
1907
1908
1909
1910
1911
1912
1913 ((SideBandOutputStream) msgOut).flushBuffer();
1914 ((SideBandOutputStream) rawOut).flushBuffer();
1915
1916 PacketLineOut plo = new PacketLineOut(origOut);
1917 plo.setFlushOnEnd(false);
1918 plo.end();
1919 }
1920
1921 if (biDirectionalPipe) {
1922
1923
1924
1925
1926 if (!sideBand && msgOut != null)
1927 msgOut.flush();
1928 rawOut.flush();
1929 }
1930 }
1931
1932
1933
1934
1935
1936
1937
1938 private void release() throws IOException {
1939 walk.close();
1940 unlockPack();
1941 timeoutIn = null;
1942 rawIn = null;
1943 rawOut = null;
1944 msgOut = null;
1945 pckIn = null;
1946 pckOut = null;
1947 refs = null;
1948
1949
1950
1951 commands = null;
1952 if (timer != null) {
1953 try {
1954 timer.terminate();
1955 } finally {
1956 timer = null;
1957 }
1958 }
1959 }
1960
1961
1962 abstract static class Reporter {
1963 abstract void sendString(String s) throws IOException;
1964 }
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975 public PushCertificate getPushCertificate() {
1976 return pushCert;
1977 }
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989 public void setPushCertificate(PushCertificate cert) {
1990 pushCert = cert;
1991 }
1992
1993
1994
1995
1996
1997
1998
1999 @Nullable
2000 public List<String> getPushOptions() {
2001 if (isAllowPushOptions() && usePushOptions) {
2002 return Collections.unmodifiableList(pushOptions);
2003 }
2004
2005
2006
2007
2008 return null;
2009 }
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024 public void setPushOptions(@Nullable List<String> options) {
2025 usePushOptions = options != null;
2026 pushOptions = options;
2027 }
2028
2029
2030
2031
2032
2033
2034 public PreReceiveHook getPreReceiveHook() {
2035 return preReceive;
2036 }
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053 public void setPreReceiveHook(PreReceiveHook h) {
2054 preReceive = h != null ? h : PreReceiveHook.NULL;
2055 }
2056
2057
2058
2059
2060
2061
2062 public PostReceiveHook getPostReceiveHook() {
2063 return postReceive;
2064 }
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077 public void setPostReceiveHook(PostReceiveHook h) {
2078 postReceive = h != null ? h : PostReceiveHook.NULL;
2079 }
2080
2081
2082
2083
2084
2085
2086
2087 public UnpackErrorHandler getUnpackErrorHandler() {
2088 return unpackErrorHandler;
2089 }
2090
2091
2092
2093
2094
2095
2096 public void setUnpackErrorHandler(UnpackErrorHandler unpackErrorHandler) {
2097 this.unpackErrorHandler = unpackErrorHandler;
2098 }
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111 @Deprecated
2112 public void setEchoCommandFailures(boolean echo) {
2113
2114 }
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134 public void receive(final InputStream input, final OutputStream output,
2135 final OutputStream messages) throws IOException {
2136 init(input, output, messages);
2137 try {
2138 service();
2139 } catch (PackProtocolException e) {
2140 fatalError(e.getMessage());
2141 throw e;
2142 } catch (InputOverLimitIOException e) {
2143 String msg = JGitText.get().tooManyCommands;
2144 fatalError(msg);
2145 throw new PackProtocolException(msg, e);
2146 } finally {
2147 try {
2148 close();
2149 } finally {
2150 release();
2151 }
2152 }
2153 }
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178 public void receiveWithExceptionPropagation(InputStream input,
2179 OutputStream output, OutputStream messages) throws IOException {
2180 init(input, output, messages);
2181 try {
2182 service();
2183 } finally {
2184 try {
2185 close();
2186 } finally {
2187 release();
2188 }
2189 }
2190 }
2191
2192 private void service() throws IOException {
2193 if (isBiDirectionalPipe()) {
2194 sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut));
2195 pckOut.flush();
2196 } else
2197 getAdvertisedOrDefaultRefs();
2198 if (hasError())
2199 return;
2200
2201 recvCommands();
2202
2203 if (hasCommands()) {
2204 try (PostReceiveExecutor e = new PostReceiveExecutor()) {
2205 if (needPack()) {
2206 try {
2207 receivePackAndCheckConnectivity();
2208 } catch (IOException | RuntimeException
2209 | SubmoduleValidationException | Error err) {
2210 unlockPack();
2211 unpackErrorHandler.handleUnpackException(err);
2212 throw new UnpackException(err);
2213 }
2214 }
2215
2216 try {
2217 setAtomic(isCapabilityEnabled(CAPABILITY_ATOMIC));
2218
2219 validateCommands();
2220 if (atomic && anyRejects()) {
2221 failPendingCommands();
2222 }
2223
2224 preReceive.onPreReceive(
2225 this, filterCommands(Result.NOT_ATTEMPTED));
2226 if (atomic && anyRejects()) {
2227 failPendingCommands();
2228 }
2229 executeCommands();
2230 } finally {
2231 unlockPack();
2232 }
2233
2234 sendStatusReport(null);
2235 }
2236 autoGc();
2237 }
2238 }
2239
2240 private void autoGc() {
2241 Repository repo = getRepository();
2242 if (!repo.getConfig().getBoolean(ConfigConstants.CONFIG_RECEIVE_SECTION,
2243 ConfigConstants.CONFIG_KEY_AUTOGC, true)) {
2244 return;
2245 }
2246 repo.autoGC(NullProgressMonitor.INSTANCE);
2247 }
2248
2249 static ReceiveCommand parseCommand(String line)
2250 throws PackProtocolException {
2251 if (line == null || line.length() < 83) {
2252 throw new PackProtocolException(
2253 JGitText.get().errorInvalidProtocolWantedOldNewRef);
2254 }
2255 String oldStr = line.substring(0, 40);
2256 String newStr = line.substring(41, 81);
2257 ObjectId oldId, newId;
2258 try {
2259 oldId = ObjectId.fromString(oldStr);
2260 newId = ObjectId.fromString(newStr);
2261 } catch (InvalidObjectIdException e) {
2262 throw new PackProtocolException(
2263 JGitText.get().errorInvalidProtocolWantedOldNewRef, e);
2264 }
2265 String name = line.substring(82);
2266 if (!Repository.isValidRefName(name)) {
2267 throw new PackProtocolException(
2268 JGitText.get().errorInvalidProtocolWantedOldNewRef);
2269 }
2270 return new ReceiveCommand(oldId, newId, name);
2271 }
2272
2273 private class PostReceiveExecutor implements AutoCloseable {
2274 @Override
2275 public void close() {
2276 postReceive.onPostReceive(ReceivePack.this,
2277 filterCommands(Result.OK));
2278 }
2279 }
2280
2281 private class DefaultUnpackErrorHandler implements UnpackErrorHandler {
2282 @Override
2283 public void handleUnpackException(Throwable t) throws IOException {
2284 sendStatusReport(t);
2285 }
2286 }
2287 }