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 package org.eclipse.jgit.api;
44
45 import static java.util.stream.Collectors.toList;
46
47 import java.io.IOException;
48 import java.net.URISyntaxException;
49 import java.text.MessageFormat;
50 import java.util.ArrayList;
51 import java.util.Arrays;
52 import java.util.List;
53
54 import org.eclipse.jgit.annotations.Nullable;
55 import org.eclipse.jgit.api.errors.GitAPIException;
56 import org.eclipse.jgit.api.errors.InvalidConfigurationException;
57 import org.eclipse.jgit.api.errors.InvalidRemoteException;
58 import org.eclipse.jgit.api.errors.JGitInternalException;
59 import org.eclipse.jgit.errors.ConfigInvalidException;
60 import org.eclipse.jgit.errors.NoRemoteRepositoryException;
61 import org.eclipse.jgit.errors.NotSupportedException;
62 import org.eclipse.jgit.errors.TransportException;
63 import org.eclipse.jgit.internal.JGitText;
64 import org.eclipse.jgit.lib.ConfigConstants;
65 import org.eclipse.jgit.lib.Constants;
66 import org.eclipse.jgit.lib.NullProgressMonitor;
67 import org.eclipse.jgit.lib.ObjectId;
68 import org.eclipse.jgit.lib.ProgressMonitor;
69 import org.eclipse.jgit.lib.Repository;
70 import org.eclipse.jgit.lib.StoredConfig;
71 import org.eclipse.jgit.lib.SubmoduleConfig.FetchRecurseSubmodulesMode;
72 import org.eclipse.jgit.revwalk.RevWalk;
73 import org.eclipse.jgit.submodule.SubmoduleWalk;
74 import org.eclipse.jgit.transport.FetchResult;
75 import org.eclipse.jgit.transport.RefSpec;
76 import org.eclipse.jgit.transport.TagOpt;
77 import org.eclipse.jgit.transport.Transport;
78
79
80
81
82
83
84
85
86
87 public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> {
88 private String remote = Constants.DEFAULT_REMOTE_NAME;
89
90 private List<RefSpec> refSpecs;
91
92 private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
93
94 private boolean checkFetchedObjects;
95
96 private Boolean removeDeletedRefs;
97
98 private boolean dryRun;
99
100 private boolean thin = Transport.DEFAULT_FETCH_THIN;
101
102 private TagOpt tagOption;
103
104 private FetchRecurseSubmodulesMode submoduleRecurseMode = null;
105
106 private Callback callback;
107
108 private boolean isForceUpdate;
109
110
111
112
113
114
115
116 public interface Callback {
117
118
119
120
121
122
123 void fetchingSubmodule(String name);
124 }
125
126
127
128
129
130
131
132 protected FetchCommand(Repository repo) {
133 super(repo);
134 refSpecs = new ArrayList<>(3);
135 }
136
137 private FetchRecurseSubmodulesMode getRecurseMode(String path) {
138
139 if (submoduleRecurseMode != null) {
140 return submoduleRecurseMode;
141 }
142
143
144 FetchRecurseSubmodulesMode mode = repo.getConfig().getEnum(
145 FetchRecurseSubmodulesMode.values(),
146 ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
147 ConfigConstants.CONFIG_KEY_FETCH_RECURSE_SUBMODULES, null);
148 if (mode != null) {
149 return mode;
150 }
151
152
153 mode = repo.getConfig().getEnum(FetchRecurseSubmodulesMode.values(),
154 ConfigConstants.CONFIG_FETCH_SECTION, null,
155 ConfigConstants.CONFIG_KEY_RECURSE_SUBMODULES, null);
156 if (mode != null) {
157 return mode;
158 }
159
160
161 return FetchRecurseSubmodulesMode.ON_DEMAND;
162 }
163
164 private void fetchSubmodules(FetchResult results)
165 throws org.eclipse.jgit.api.errors.TransportException,
166 GitAPIException, InvalidConfigurationException {
167 try (SubmoduleWalkuleWalk.html#SubmoduleWalk">SubmoduleWalk walk = new SubmoduleWalk(repo);
168 RevWalk revWalk = new RevWalk(repo)) {
169
170 ObjectId fetchHead = repo.resolve(Constants.FETCH_HEAD);
171 if (fetchHead == null) {
172 return;
173 }
174 walk.setTree(revWalk.parseTree(fetchHead));
175 while (walk.next()) {
176 try (Repository submoduleRepo = walk.getRepository()) {
177
178
179
180
181 if (submoduleRepo == null || walk.getModulesPath() == null
182 || walk.getConfigUrl() == null) {
183 continue;
184 }
185
186 FetchRecurseSubmodulesMode recurseMode = getRecurseMode(
187 walk.getPath());
188
189
190
191
192
193
194
195 if ((recurseMode == FetchRecurseSubmodulesMode.ON_DEMAND
196 && !submoduleRepo.getObjectDatabase()
197 .has(walk.getObjectId()))
198 || recurseMode == FetchRecurseSubmodulesMode.YES) {
199 FetchCommand f = new FetchCommand(submoduleRepo)
200 .setProgressMonitor(monitor)
201 .setTagOpt(tagOption)
202 .setCheckFetchedObjects(checkFetchedObjects)
203 .setRemoveDeletedRefs(isRemoveDeletedRefs())
204 .setThin(thin)
205 .setRefSpecs(applyOptions(refSpecs))
206 .setDryRun(dryRun)
207 .setRecurseSubmodules(recurseMode);
208 configure(f);
209 if (callback != null) {
210 callback.fetchingSubmodule(walk.getPath());
211 }
212 results.addSubmodule(walk.getPath(), f.call());
213 }
214 }
215 }
216 } catch (IOException e) {
217 throw new JGitInternalException(e.getMessage(), e);
218 } catch (ConfigInvalidException e) {
219 throw new InvalidConfigurationException(e.getMessage(), e);
220 }
221 }
222
223
224
225
226
227
228
229
230
231 @Override
232 public FetchResult call() throws GitAPIException, InvalidRemoteException,
233 org.eclipse.jgit.api.errors.TransportException {
234 checkCallable();
235
236 try (Transport transport = Transport.open(repo, remote)) {
237 transport.setCheckFetchedObjects(checkFetchedObjects);
238 transport.setRemoveDeletedRefs(isRemoveDeletedRefs());
239 transport.setDryRun(dryRun);
240 if (tagOption != null)
241 transport.setTagOpt(tagOption);
242 transport.setFetchThin(thin);
243 configure(transport);
244 FetchResult result = transport.fetch(monitor,
245 applyOptions(refSpecs));
246 if (!repo.isBare()) {
247 fetchSubmodules(result);
248 }
249
250 return result;
251 } catch (NoRemoteRepositoryException e) {
252 throw new InvalidRemoteException(MessageFormat.format(
253 JGitText.get().invalidRemote, remote), e);
254 } catch (TransportException e) {
255 throw new org.eclipse.jgit.api.errors.TransportException(
256 e.getMessage(), e);
257 } catch (URISyntaxException e) {
258 throw new InvalidRemoteException(MessageFormat.format(
259 JGitText.get().invalidRemote, remote));
260 } catch (NotSupportedException e) {
261 throw new JGitInternalException(
262 JGitText.get().exceptionCaughtDuringExecutionOfFetchCommand,
263 e);
264 }
265
266 }
267
268 private List<RefSpec> applyOptions(List<RefSpec> refSpecs2) {
269 if (!isForceUpdate()) {
270 return refSpecs2;
271 }
272 List<RefSpec> updated = new ArrayList<>(3);
273 for (RefSpec refSpec : refSpecs2) {
274 updated.add(refSpec.setForceUpdate(true));
275 }
276 return updated;
277 }
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294 public FetchCommand setRecurseSubmodules(
295 @Nullable FetchRecurseSubmodulesMode recurse) {
296 checkCallable();
297 submoduleRecurseMode = recurse;
298 return this;
299 }
300
301
302
303
304
305
306
307
308
309
310
311 public FetchCommand setRemote(String remote) {
312 checkCallable();
313 this.remote = remote;
314 return this;
315 }
316
317
318
319
320
321
322 public String getRemote() {
323 return remote;
324 }
325
326
327
328
329
330
331 public int getTimeout() {
332 return timeout;
333 }
334
335
336
337
338
339
340 public boolean isCheckFetchedObjects() {
341 return checkFetchedObjects;
342 }
343
344
345
346
347
348
349
350
351 public FetchCommand setCheckFetchedObjects(boolean checkFetchedObjects) {
352 checkCallable();
353 this.checkFetchedObjects = checkFetchedObjects;
354 return this;
355 }
356
357
358
359
360
361
362 public boolean isRemoveDeletedRefs() {
363 if (removeDeletedRefs != null)
364 return removeDeletedRefs.booleanValue();
365 else {
366 boolean result = false;
367 StoredConfig config = repo.getConfig();
368 result = config.getBoolean(ConfigConstants.CONFIG_FETCH_SECTION,
369 null, ConfigConstants.CONFIG_KEY_PRUNE, result);
370 result = config.getBoolean(ConfigConstants.CONFIG_REMOTE_SECTION,
371 remote, ConfigConstants.CONFIG_KEY_PRUNE, result);
372 return result;
373 }
374 }
375
376
377
378
379
380
381
382
383
384 public FetchCommand setRemoveDeletedRefs(boolean removeDeletedRefs) {
385 checkCallable();
386 this.removeDeletedRefs = Boolean.valueOf(removeDeletedRefs);
387 return this;
388 }
389
390
391
392
393
394
395 public ProgressMonitor getProgressMonitor() {
396 return monitor;
397 }
398
399
400
401
402
403
404
405
406
407
408 public FetchCommand setProgressMonitor(ProgressMonitor monitor) {
409 checkCallable();
410 if (monitor == null) {
411 monitor = NullProgressMonitor.INSTANCE;
412 }
413 this.monitor = monitor;
414 return this;
415 }
416
417
418
419
420
421
422 public List<RefSpec> getRefSpecs() {
423 return refSpecs;
424 }
425
426
427
428
429
430
431
432
433
434 public FetchCommand setRefSpecs(String... specs) {
435 return setRefSpecs(
436 Arrays.stream(specs).map(RefSpec::new).collect(toList()));
437 }
438
439
440
441
442
443
444
445
446 public FetchCommand setRefSpecs(RefSpec... specs) {
447 return setRefSpecs(Arrays.asList(specs));
448 }
449
450
451
452
453
454
455
456
457 public FetchCommand setRefSpecs(List<RefSpec> specs) {
458 checkCallable();
459 this.refSpecs.clear();
460 this.refSpecs.addAll(specs);
461 return this;
462 }
463
464
465
466
467
468
469 public boolean isDryRun() {
470 return dryRun;
471 }
472
473
474
475
476
477
478
479
480 public FetchCommand setDryRun(boolean dryRun) {
481 checkCallable();
482 this.dryRun = dryRun;
483 return this;
484 }
485
486
487
488
489
490
491 public boolean isThin() {
492 return thin;
493 }
494
495
496
497
498
499
500
501
502
503
504 public FetchCommand setThin(boolean thin) {
505 checkCallable();
506 this.thin = thin;
507 return this;
508 }
509
510
511
512
513
514
515
516
517 public FetchCommand setTagOpt(TagOpt tagOpt) {
518 checkCallable();
519 this.tagOption = tagOpt;
520 return this;
521 }
522
523
524
525
526
527
528
529
530
531 public FetchCommand setCallback(Callback callback) {
532 this.callback = callback;
533 return this;
534 }
535
536
537
538
539
540
541
542 public boolean isForceUpdate() {
543 return this.isForceUpdate;
544 }
545
546
547
548
549
550
551
552
553
554 public FetchCommand setForceUpdate(boolean force) {
555 this.isForceUpdate = force;
556 return this;
557 }
558 }