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 (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.hasObject(walk.getObjectId()))
197 || recurseMode == FetchRecurseSubmodulesMode.YES) {
198 FetchCommand f = new FetchCommand(submoduleRepo)
199 .setProgressMonitor(monitor)
200 .setTagOpt(tagOption)
201 .setCheckFetchedObjects(checkFetchedObjects)
202 .setRemoveDeletedRefs(isRemoveDeletedRefs())
203 .setThin(thin)
204 .setRefSpecs(applyOptions(refSpecs))
205 .setDryRun(dryRun)
206 .setRecurseSubmodules(recurseMode);
207 configure(f);
208 if (callback != null) {
209 callback.fetchingSubmodule(walk.getPath());
210 }
211 results.addSubmodule(walk.getPath(), f.call());
212 }
213 }
214 }
215 } catch (IOException e) {
216 throw new JGitInternalException(e.getMessage(), e);
217 } catch (ConfigInvalidException e) {
218 throw new InvalidConfigurationException(e.getMessage(), e);
219 }
220 }
221
222
223
224
225
226
227
228
229
230 @Override
231 public FetchResult call() throws GitAPIException, InvalidRemoteException,
232 org.eclipse.jgit.api.errors.TransportException {
233 checkCallable();
234
235 try (Transport transport = Transport.open(repo, remote)) {
236 transport.setCheckFetchedObjects(checkFetchedObjects);
237 transport.setRemoveDeletedRefs(isRemoveDeletedRefs());
238 transport.setDryRun(dryRun);
239 if (tagOption != null)
240 transport.setTagOpt(tagOption);
241 transport.setFetchThin(thin);
242 configure(transport);
243 FetchResult result = transport.fetch(monitor,
244 applyOptions(refSpecs));
245 if (!repo.isBare()) {
246 fetchSubmodules(result);
247 }
248
249 return result;
250 } catch (NoRemoteRepositoryException e) {
251 throw new InvalidRemoteException(MessageFormat.format(
252 JGitText.get().invalidRemote, remote), e);
253 } catch (TransportException e) {
254 throw new org.eclipse.jgit.api.errors.TransportException(
255 e.getMessage(), e);
256 } catch (URISyntaxException e) {
257 throw new InvalidRemoteException(MessageFormat.format(
258 JGitText.get().invalidRemote, remote));
259 } catch (NotSupportedException e) {
260 throw new JGitInternalException(
261 JGitText.get().exceptionCaughtDuringExecutionOfFetchCommand,
262 e);
263 }
264
265 }
266
267 private List<RefSpec> applyOptions(List<RefSpec> refSpecs2) {
268 if (!isForceUpdate()) {
269 return refSpecs2;
270 }
271 List<RefSpec> updated = new ArrayList<>(3);
272 for (RefSpec refSpec : refSpecs2) {
273 updated.add(refSpec.setForceUpdate(true));
274 }
275 return updated;
276 }
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293 public FetchCommand setRecurseSubmodules(
294 @Nullable FetchRecurseSubmodulesMode recurse) {
295 checkCallable();
296 submoduleRecurseMode = recurse;
297 return this;
298 }
299
300
301
302
303
304
305
306
307
308
309
310 public FetchCommand setRemote(String remote) {
311 checkCallable();
312 this.remote = remote;
313 return this;
314 }
315
316
317
318
319
320
321 public String getRemote() {
322 return remote;
323 }
324
325
326
327
328
329
330 public int getTimeout() {
331 return timeout;
332 }
333
334
335
336
337
338
339 public boolean isCheckFetchedObjects() {
340 return checkFetchedObjects;
341 }
342
343
344
345
346
347
348
349
350 public FetchCommand setCheckFetchedObjects(boolean checkFetchedObjects) {
351 checkCallable();
352 this.checkFetchedObjects = checkFetchedObjects;
353 return this;
354 }
355
356
357
358
359
360
361 public boolean isRemoveDeletedRefs() {
362 if (removeDeletedRefs != null)
363 return removeDeletedRefs.booleanValue();
364 else {
365 boolean result = false;
366 StoredConfig config = repo.getConfig();
367 result = config.getBoolean(ConfigConstants.CONFIG_FETCH_SECTION,
368 null, ConfigConstants.CONFIG_KEY_PRUNE, result);
369 result = config.getBoolean(ConfigConstants.CONFIG_REMOTE_SECTION,
370 remote, ConfigConstants.CONFIG_KEY_PRUNE, result);
371 return result;
372 }
373 }
374
375
376
377
378
379
380
381
382
383 public FetchCommand setRemoveDeletedRefs(boolean removeDeletedRefs) {
384 checkCallable();
385 this.removeDeletedRefs = Boolean.valueOf(removeDeletedRefs);
386 return this;
387 }
388
389
390
391
392
393
394 public ProgressMonitor getProgressMonitor() {
395 return monitor;
396 }
397
398
399
400
401
402
403
404
405
406
407 public FetchCommand setProgressMonitor(ProgressMonitor monitor) {
408 checkCallable();
409 if (monitor == null) {
410 monitor = NullProgressMonitor.INSTANCE;
411 }
412 this.monitor = monitor;
413 return this;
414 }
415
416
417
418
419
420
421 public List<RefSpec> getRefSpecs() {
422 return refSpecs;
423 }
424
425
426
427
428
429
430
431
432
433 public FetchCommand setRefSpecs(String... specs) {
434 return setRefSpecs(
435 Arrays.stream(specs).map(RefSpec::new).collect(toList()));
436 }
437
438
439
440
441
442
443
444
445 public FetchCommand setRefSpecs(RefSpec... specs) {
446 return setRefSpecs(Arrays.asList(specs));
447 }
448
449
450
451
452
453
454
455
456 public FetchCommand setRefSpecs(List<RefSpec> specs) {
457 checkCallable();
458 this.refSpecs.clear();
459 this.refSpecs.addAll(specs);
460 return this;
461 }
462
463
464
465
466
467
468 public boolean isDryRun() {
469 return dryRun;
470 }
471
472
473
474
475
476
477
478
479 public FetchCommand setDryRun(boolean dryRun) {
480 checkCallable();
481 this.dryRun = dryRun;
482 return this;
483 }
484
485
486
487
488
489
490 public boolean isThin() {
491 return thin;
492 }
493
494
495
496
497
498
499
500
501
502
503 public FetchCommand setThin(boolean thin) {
504 checkCallable();
505 this.thin = thin;
506 return this;
507 }
508
509
510
511
512
513
514
515
516 public FetchCommand setTagOpt(TagOpt tagOpt) {
517 checkCallable();
518 this.tagOption = tagOpt;
519 return this;
520 }
521
522
523
524
525
526
527
528
529
530 public FetchCommand setCallback(Callback callback) {
531 this.callback = callback;
532 return this;
533 }
534
535
536
537
538
539
540
541 public boolean isForceUpdate() {
542 return this.isForceUpdate;
543 }
544
545
546
547
548
549
550
551
552
553 public FetchCommand setForceUpdate(boolean force) {
554 this.isForceUpdate = force;
555 return this;
556 }
557 }