1
2
3
4
5
6
7
8
9
10 package org.eclipse.jgit.api;
11
12 import java.io.IOException;
13 import java.io.OutputStream;
14 import java.io.PrintStream;
15 import java.net.URISyntaxException;
16 import java.text.MessageFormat;
17 import java.util.ArrayList;
18 import java.util.Arrays;
19 import java.util.Collection;
20 import java.util.Collections;
21 import java.util.HashMap;
22 import java.util.List;
23 import java.util.Map;
24
25 import org.eclipse.jgit.api.errors.DetachedHeadException;
26 import org.eclipse.jgit.api.errors.GitAPIException;
27 import org.eclipse.jgit.api.errors.InvalidRefNameException;
28 import org.eclipse.jgit.api.errors.InvalidRemoteException;
29 import org.eclipse.jgit.api.errors.JGitInternalException;
30 import org.eclipse.jgit.errors.NotSupportedException;
31 import org.eclipse.jgit.errors.TooLargeObjectInPackException;
32 import org.eclipse.jgit.errors.TooLargePackException;
33 import org.eclipse.jgit.errors.TransportException;
34 import org.eclipse.jgit.internal.JGitText;
35 import org.eclipse.jgit.lib.BranchConfig;
36 import org.eclipse.jgit.lib.Config;
37 import org.eclipse.jgit.lib.ConfigConstants;
38 import org.eclipse.jgit.lib.Constants;
39 import org.eclipse.jgit.lib.NullProgressMonitor;
40 import org.eclipse.jgit.lib.ProgressMonitor;
41 import org.eclipse.jgit.lib.Ref;
42 import org.eclipse.jgit.lib.Repository;
43 import org.eclipse.jgit.transport.PushConfig;
44 import org.eclipse.jgit.transport.PushConfig.PushDefault;
45 import org.eclipse.jgit.transport.PushResult;
46 import org.eclipse.jgit.transport.RefLeaseSpec;
47 import org.eclipse.jgit.transport.RefSpec;
48 import org.eclipse.jgit.transport.RemoteConfig;
49 import org.eclipse.jgit.transport.RemoteRefUpdate;
50 import org.eclipse.jgit.transport.Transport;
51
52
53
54
55
56
57
58
59
60 public class PushCommand extends
61 TransportCommand<PushCommand, Iterable<PushResult>> {
62
63 private String remote;
64
65 private final List<RefSpec> refSpecs;
66
67 private final Map<String, RefLeaseSpec> refLeaseSpecs;
68
69 private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
70
71 private String receivePack = RemoteConfig.DEFAULT_RECEIVE_PACK;
72
73 private boolean dryRun;
74 private boolean atomic;
75 private boolean force;
76 private boolean thin = Transport.DEFAULT_PUSH_THIN;
77 private boolean useBitmaps = Transport.DEFAULT_PUSH_USE_BITMAPS;
78
79 private PrintStream hookOutRedirect;
80
81 private PrintStream hookErrRedirect;
82
83 private OutputStream out;
84
85 private List<String> pushOptions;
86
87
88
89 private PushDefault pushDefault = PushDefault.CURRENT;
90
91
92
93
94
95
96
97
98
99 protected PushCommand(Repository repo) {
100 super(repo);
101 refSpecs = new ArrayList<>(3);
102 refLeaseSpecs = new HashMap<>();
103 }
104
105
106
107
108
109
110
111
112
113 @Override
114 public Iterable<PushResult> call() throws GitAPIException,
115 InvalidRemoteException,
116 org.eclipse.jgit.api.errors.TransportException {
117 checkCallable();
118 setCallable(false);
119
120 ArrayList<PushResult> pushResults = new ArrayList<>(3);
121
122 try {
123 Config config = repo.getConfig();
124 remote = determineRemote(config, remote);
125 if (refSpecs.isEmpty()) {
126 RemoteConfig rc = new RemoteConfig(config,
127 getRemote());
128 refSpecs.addAll(rc.getPushRefSpecs());
129 if (refSpecs.isEmpty()) {
130 determineDefaultRefSpecs(config);
131 }
132 }
133
134 if (force) {
135 for (int i = 0; i < refSpecs.size(); i++)
136 refSpecs.set(i, refSpecs.get(i).setForceUpdate(true));
137 }
138
139 List<Transport> transports = Transport.openAll(repo, remote,
140 Transport.Operation.PUSH);
141 for (@SuppressWarnings("resource")
142 final Transport transport : transports) {
143 transport.setPushThin(thin);
144 transport.setPushAtomic(atomic);
145 if (receivePack != null)
146 transport.setOptionReceivePack(receivePack);
147 transport.setDryRun(dryRun);
148 transport.setPushOptions(pushOptions);
149 transport.setPushUseBitmaps(useBitmaps);
150 transport.setHookOutputStream(hookOutRedirect);
151 transport.setHookErrorStream(hookErrRedirect);
152 configure(transport);
153
154 final Collection<RemoteRefUpdate> toPush = transport
155 .findRemoteRefUpdatesFor(refSpecs, refLeaseSpecs);
156
157 try {
158 PushResult result = transport.push(monitor, toPush, out);
159 pushResults.add(result);
160
161 } catch (TooLargePackException e) {
162 throw new org.eclipse.jgit.api.errors.TooLargePackException(
163 e.getMessage(), e);
164 } catch (TooLargeObjectInPackException e) {
165 throw new org.eclipse.jgit.api.errors.TooLargeObjectInPackException(
166 e.getMessage(), e);
167 } catch (TransportException e) {
168 throw new org.eclipse.jgit.api.errors.TransportException(
169 e.getMessage(), e);
170 } finally {
171 transport.close();
172 }
173 }
174
175 } catch (URISyntaxException e) {
176 throw new InvalidRemoteException(
177 MessageFormat.format(JGitText.get().invalidRemote, remote),
178 e);
179 } catch (TransportException e) {
180 throw new org.eclipse.jgit.api.errors.TransportException(
181 e.getMessage(), e);
182 } catch (NotSupportedException e) {
183 throw new JGitInternalException(
184 JGitText.get().exceptionCaughtDuringExecutionOfPushCommand,
185 e);
186 } catch (IOException e) {
187 throw new JGitInternalException(
188 JGitText.get().exceptionCaughtDuringExecutionOfPushCommand,
189 e);
190 }
191
192 return pushResults;
193 }
194
195 private String determineRemote(Config config, String remoteName)
196 throws IOException {
197 if (remoteName != null) {
198 return remoteName;
199 }
200 Ref head = repo.exactRef(Constants.HEAD);
201 String effectiveRemote = null;
202 BranchConfig branchCfg = null;
203 if (head != null && head.isSymbolic()) {
204 String currentBranch = head.getLeaf().getName();
205 branchCfg = new BranchConfig(config,
206 Repository.shortenRefName(currentBranch));
207 effectiveRemote = branchCfg.getPushRemote();
208 }
209 if (effectiveRemote == null) {
210 effectiveRemote = config.getString(
211 ConfigConstants.CONFIG_REMOTE_SECTION, null,
212 ConfigConstants.CONFIG_KEY_PUSH_DEFAULT);
213 if (effectiveRemote == null && branchCfg != null) {
214 effectiveRemote = branchCfg.getRemote();
215 }
216 }
217 if (effectiveRemote == null) {
218 effectiveRemote = Constants.DEFAULT_REMOTE_NAME;
219 }
220 return effectiveRemote;
221 }
222
223 private String getCurrentBranch()
224 throws IOException, DetachedHeadException {
225 Ref head = repo.exactRef(Constants.HEAD);
226 if (head != null && head.isSymbolic()) {
227 return head.getLeaf().getName();
228 }
229 throw new DetachedHeadException();
230 }
231
232 private void determineDefaultRefSpecs(Config config)
233 throws IOException, GitAPIException {
234 if (pushDefault == null) {
235 pushDefault = config.get(PushConfig::new).getPushDefault();
236 }
237 switch (pushDefault) {
238 case CURRENT:
239 refSpecs.add(new RefSpec(getCurrentBranch()));
240 break;
241 case MATCHING:
242 refSpecs.add(new RefSpec(":"));
243 break;
244 case NOTHING:
245 throw new InvalidRefNameException(
246 JGitText.get().pushDefaultNothing);
247 case SIMPLE:
248 case UPSTREAM:
249 String currentBranch = getCurrentBranch();
250 BranchConfig branchCfg = new BranchConfig(config,
251 Repository.shortenRefName(currentBranch));
252 String fetchRemote = branchCfg.getRemote();
253 if (fetchRemote == null) {
254 fetchRemote = Constants.DEFAULT_REMOTE_NAME;
255 }
256 boolean isTriangular = !fetchRemote.equals(remote);
257 if (isTriangular) {
258 if (PushDefault.UPSTREAM.equals(pushDefault)) {
259 throw new InvalidRefNameException(MessageFormat.format(
260 JGitText.get().pushDefaultTriangularUpstream,
261 remote, fetchRemote));
262 }
263
264
265
266
267 refSpecs.add(new RefSpec(currentBranch));
268 } else {
269 String trackedBranch = branchCfg.getMerge();
270 if (branchCfg.isRemoteLocal() || trackedBranch == null
271 || !trackedBranch.startsWith(Constants.R_HEADS)) {
272 throw new InvalidRefNameException(MessageFormat.format(
273 JGitText.get().pushDefaultNoUpstream,
274 currentBranch));
275 }
276 if (PushDefault.SIMPLE.equals(pushDefault)
277 && !trackedBranch.equals(currentBranch)) {
278 throw new InvalidRefNameException(MessageFormat.format(
279 JGitText.get().pushDefaultSimple, currentBranch,
280 trackedBranch));
281 }
282 refSpecs.add(new RefSpec(currentBranch + ':' + trackedBranch));
283 }
284 break;
285 default:
286 throw new InvalidRefNameException(MessageFormat
287 .format(JGitText.get().pushDefaultUnknown, pushDefault));
288 }
289 }
290
291
292
293
294
295
296
297
298
299
300
301 public PushCommand setRemote(String remote) {
302 checkCallable();
303 this.remote = remote;
304 return this;
305 }
306
307
308
309
310
311
312 public String getRemote() {
313 return remote;
314 }
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330 public PushCommand setHookOutputStream(PrintStream redirect) {
331 checkCallable();
332 hookOutRedirect = redirect;
333 return this;
334 }
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350 public PushCommand setHookErrorStream(PrintStream redirect) {
351 checkCallable();
352 hookErrRedirect = redirect;
353 return this;
354 }
355
356
357
358
359
360
361
362
363
364
365
366
367 public PushCommand setReceivePack(String receivePack) {
368 checkCallable();
369 this.receivePack = receivePack;
370 return this;
371 }
372
373
374
375
376
377
378 public String getReceivePack() {
379 return receivePack;
380 }
381
382
383
384
385
386
387 public int getTimeout() {
388 return timeout;
389 }
390
391
392
393
394
395
396 public ProgressMonitor getProgressMonitor() {
397 return monitor;
398 }
399
400
401
402
403
404
405
406
407
408
409 public PushCommand setProgressMonitor(ProgressMonitor monitor) {
410 checkCallable();
411 if (monitor == null) {
412 monitor = NullProgressMonitor.INSTANCE;
413 }
414 this.monitor = monitor;
415 return this;
416 }
417
418
419
420
421
422
423
424 public List<RefLeaseSpec> getRefLeaseSpecs() {
425 return new ArrayList<>(refLeaseSpecs.values());
426 }
427
428
429
430
431
432
433
434
435
436
437 public PushCommand setRefLeaseSpecs(RefLeaseSpec... specs) {
438 return setRefLeaseSpecs(Arrays.asList(specs));
439 }
440
441
442
443
444
445
446
447
448
449
450 public PushCommand setRefLeaseSpecs(List<RefLeaseSpec> specs) {
451 checkCallable();
452 this.refLeaseSpecs.clear();
453 for (RefLeaseSpec spec : specs) {
454 refLeaseSpecs.put(spec.getRef(), spec);
455 }
456 return this;
457 }
458
459
460
461
462
463
464 public List<RefSpec> getRefSpecs() {
465 return refSpecs;
466 }
467
468
469
470
471
472
473
474 public PushCommand setRefSpecs(RefSpec... specs) {
475 checkCallable();
476 this.refSpecs.clear();
477 Collections.addAll(refSpecs, specs);
478 return this;
479 }
480
481
482
483
484
485
486
487
488 public PushCommand setRefSpecs(List<RefSpec> specs) {
489 checkCallable();
490 this.refSpecs.clear();
491 this.refSpecs.addAll(specs);
492 return this;
493 }
494
495
496
497
498
499
500
501 public PushDefault getPushDefault() {
502 return pushDefault;
503 }
504
505
506
507
508
509
510
511
512
513
514
515
516
517 public PushCommand setPushDefault(PushDefault pushDefault) {
518 checkCallable();
519 this.pushDefault = pushDefault;
520 return this;
521 }
522
523
524
525
526
527
528 public PushCommand setPushAll() {
529 refSpecs.add(Transport.REFSPEC_PUSH_ALL);
530 return this;
531 }
532
533
534
535
536
537
538 public PushCommand setPushTags() {
539 refSpecs.add(Transport.REFSPEC_TAGS);
540 return this;
541 }
542
543
544
545
546
547
548
549
550 public PushCommand add(Ref ref) {
551 refSpecs.add(new RefSpec(ref.getLeaf().getName()));
552 return this;
553 }
554
555
556
557
558
559
560
561
562
563
564 public PushCommand add(String nameOrSpec) {
565 if (0 <= nameOrSpec.indexOf(':')) {
566 refSpecs.add(new RefSpec(nameOrSpec));
567 } else {
568 Ref src;
569 try {
570 src = repo.findRef(nameOrSpec);
571 } catch (IOException e) {
572 throw new JGitInternalException(
573 JGitText.get().exceptionCaughtDuringExecutionOfPushCommand,
574 e);
575 }
576 if (src != null)
577 add(src);
578 }
579 return this;
580 }
581
582
583
584
585
586
587 public boolean isDryRun() {
588 return dryRun;
589 }
590
591
592
593
594
595
596
597 public PushCommand setDryRun(boolean dryRun) {
598 checkCallable();
599 this.dryRun = dryRun;
600 return this;
601 }
602
603
604
605
606
607
608 public boolean isThin() {
609 return thin;
610 }
611
612
613
614
615
616
617
618
619
620
621 public PushCommand setThin(boolean thin) {
622 checkCallable();
623 this.thin = thin;
624 return this;
625 }
626
627
628
629
630
631
632
633 public boolean isUseBitmaps() {
634 return useBitmaps;
635 }
636
637
638
639
640
641
642
643
644
645
646
647 public PushCommand setUseBitmaps(boolean useBitmaps) {
648 checkCallable();
649 this.useBitmaps = useBitmaps;
650 return this;
651 }
652
653
654
655
656
657
658
659
660 public boolean isAtomic() {
661 return atomic;
662 }
663
664
665
666
667
668
669
670
671
672
673
674 public PushCommand setAtomic(boolean atomic) {
675 checkCallable();
676 this.atomic = atomic;
677 return this;
678 }
679
680
681
682
683
684
685 public boolean isForce() {
686 return force;
687 }
688
689
690
691
692
693
694
695
696 public PushCommand setForce(boolean force) {
697 checkCallable();
698 this.force = force;
699 return this;
700 }
701
702
703
704
705
706
707
708
709
710 public PushCommand setOutputStream(OutputStream out) {
711 this.out = out;
712 return this;
713 }
714
715
716
717
718
719
720
721 public List<String> getPushOptions() {
722 return pushOptions;
723 }
724
725
726
727
728
729
730
731
732
733 public PushCommand setPushOptions(List<String> pushOptions) {
734 this.pushOptions = pushOptions;
735 return this;
736 }
737 }