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