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