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