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