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.submodule;
44
45 import java.io.File;
46 import java.io.IOException;
47 import java.text.MessageFormat;
48
49 import org.eclipse.jgit.dircache.DirCache;
50 import org.eclipse.jgit.dircache.DirCacheIterator;
51 import org.eclipse.jgit.errors.ConfigInvalidException;
52 import org.eclipse.jgit.errors.CorruptObjectException;
53 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
54 import org.eclipse.jgit.errors.MissingObjectException;
55 import org.eclipse.jgit.errors.RepositoryNotFoundException;
56 import org.eclipse.jgit.internal.JGitText;
57 import org.eclipse.jgit.lib.AnyObjectId;
58 import org.eclipse.jgit.lib.BlobBasedConfig;
59 import org.eclipse.jgit.lib.Config;
60 import org.eclipse.jgit.lib.ConfigConstants;
61 import org.eclipse.jgit.lib.Constants;
62 import org.eclipse.jgit.lib.FileMode;
63 import org.eclipse.jgit.lib.ObjectId;
64 import org.eclipse.jgit.lib.Ref;
65 import org.eclipse.jgit.lib.Repository;
66 import org.eclipse.jgit.lib.RepositoryBuilder;
67 import org.eclipse.jgit.lib.StoredConfig;
68 import org.eclipse.jgit.storage.file.FileBasedConfig;
69 import org.eclipse.jgit.treewalk.AbstractTreeIterator;
70 import org.eclipse.jgit.treewalk.CanonicalTreeParser;
71 import org.eclipse.jgit.treewalk.TreeWalk;
72 import org.eclipse.jgit.treewalk.filter.PathFilter;
73 import org.eclipse.jgit.treewalk.filter.TreeFilter;
74 import org.eclipse.jgit.util.FS;
75
76
77
78
79 public class SubmoduleWalk implements AutoCloseable {
80
81
82
83
84
85
86 public enum IgnoreSubmoduleMode {
87
88
89
90 ALL,
91
92
93
94
95 DIRTY,
96
97
98
99
100 UNTRACKED,
101
102
103
104
105 NONE;
106 }
107
108
109
110
111
112
113
114
115
116
117
118 public static SubmoduleWalk forIndex(Repository repository)
119 throws IOException {
120 SubmoduleWalk generator = new SubmoduleWalk(repository);
121 try {
122 DirCache index = repository.readDirCache();
123 generator.setTree(new DirCacheIterator(index));
124 } catch (IOException e) {
125 generator.close();
126 throw e;
127 }
128 return generator;
129 }
130
131
132
133
134
135
136
137
138
139
140
141
142
143 public static SubmoduleWalk forPath(Repository repository,
144 AnyObjectId treeId, String path) throws IOException {
145 SubmoduleWalk generator = new SubmoduleWalk(repository);
146 try {
147 generator.setTree(treeId);
148 PathFilter filter = PathFilter.create(path);
149 generator.setFilter(filter);
150 generator.setRootTree(treeId);
151 while (generator.next())
152 if (filter.isDone(generator.walk))
153 return generator;
154 } catch (IOException e) {
155 generator.close();
156 throw e;
157 }
158 generator.close();
159 return null;
160 }
161
162
163
164
165
166
167
168
169
170
171
172
173
174 public static SubmoduleWalk forPath(Repository repository,
175 AbstractTreeIterator iterator, String path) throws IOException {
176 SubmoduleWalk generator = new SubmoduleWalk(repository);
177 try {
178 generator.setTree(iterator);
179 PathFilter filter = PathFilter.create(path);
180 generator.setFilter(filter);
181 generator.setRootTree(iterator);
182 while (generator.next())
183 if (filter.isDone(generator.walk))
184 return generator;
185 } catch (IOException e) {
186 generator.close();
187 throw e;
188 }
189 generator.close();
190 return null;
191 }
192
193
194
195
196
197
198
199
200 public static File getSubmoduleDirectory(final Repository parent,
201 final String path) {
202 return new File(parent.getWorkTree(), path);
203 }
204
205
206
207
208
209
210
211
212
213 public static Repository getSubmoduleRepository(final Repository parent,
214 final String path) throws IOException {
215 return getSubmoduleRepository(parent.getWorkTree(), path);
216 }
217
218
219
220
221
222
223
224
225
226 public static Repository getSubmoduleRepository(final File parent,
227 final String path) throws IOException {
228 File subWorkTree = new File(parent, path);
229 if (!subWorkTree.isDirectory())
230 return null;
231 File workTree = new File(parent, path);
232 try {
233 return new RepositoryBuilder()
234 .setMustExist(true)
235 .setFS(FS.DETECTED)
236 .setWorkTree(workTree)
237 .build();
238 } catch (RepositoryNotFoundException e) {
239 return null;
240 }
241 }
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260 public static String getSubmoduleRemoteUrl(final Repository parent,
261 final String url) throws IOException {
262 if (!url.startsWith("./") && !url.startsWith("../"))
263 return url;
264
265 String remoteName = null;
266
267 Ref ref = parent.exactRef(Constants.HEAD);
268 if (ref != null) {
269 if (ref.isSymbolic())
270 ref = ref.getLeaf();
271 remoteName = parent.getConfig().getString(
272 ConfigConstants.CONFIG_BRANCH_SECTION,
273 Repository.shortenRefName(ref.getName()),
274 ConfigConstants.CONFIG_KEY_REMOTE);
275 }
276
277
278 if (remoteName == null)
279 remoteName = Constants.DEFAULT_REMOTE_NAME;
280
281 String remoteUrl = parent.getConfig().getString(
282 ConfigConstants.CONFIG_REMOTE_SECTION, remoteName,
283 ConfigConstants.CONFIG_KEY_URL);
284
285
286 if (remoteUrl == null) {
287 remoteUrl = parent.getWorkTree().getAbsolutePath();
288
289 if ('\\' == File.separatorChar)
290 remoteUrl = remoteUrl.replace('\\', '/');
291 }
292
293
294 if (remoteUrl.charAt(remoteUrl.length() - 1) == '/')
295 remoteUrl = remoteUrl.substring(0, remoteUrl.length() - 1);
296
297 char separator = '/';
298 String submoduleUrl = url;
299 while (submoduleUrl.length() > 0) {
300 if (submoduleUrl.startsWith("./"))
301 submoduleUrl = submoduleUrl.substring(2);
302 else if (submoduleUrl.startsWith("../")) {
303 int lastSeparator = remoteUrl.lastIndexOf('/');
304 if (lastSeparator < 1) {
305 lastSeparator = remoteUrl.lastIndexOf(':');
306 separator = ':';
307 }
308 if (lastSeparator < 1)
309 throw new IOException(MessageFormat.format(
310 JGitText.get().submoduleParentRemoteUrlInvalid,
311 remoteUrl));
312 remoteUrl = remoteUrl.substring(0, lastSeparator);
313 submoduleUrl = submoduleUrl.substring(3);
314 } else
315 break;
316 }
317 return remoteUrl + separator + submoduleUrl;
318 }
319
320 private final Repository repository;
321
322 private final TreeWalk walk;
323
324 private StoredConfig repoConfig;
325
326 private AbstractTreeIterator rootTree;
327
328 private Config modulesConfig;
329
330 private String path;
331
332
333
334
335
336
337
338 public SubmoduleWalk(final Repository repository) throws IOException {
339 this.repository = repository;
340 repoConfig = repository.getConfig();
341 walk = new TreeWalk(repository);
342 walk.setRecursive(true);
343 }
344
345
346
347
348
349
350
351
352
353
354
355 public SubmoduleWalk setModulesConfig(final Config config) {
356 modulesConfig = config;
357 return this;
358 }
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373 public SubmoduleWalk setRootTree(final AbstractTreeIterator tree) {
374 rootTree = tree;
375 modulesConfig = null;
376 return this;
377 }
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393 public SubmoduleWalk setRootTree(final AnyObjectId id) throws IOException {
394 final CanonicalTreeParser p = new CanonicalTreeParser();
395 p.reset(walk.getObjectReader(), id);
396 rootTree = p;
397 modulesConfig = null;
398 return this;
399 }
400
401
402
403
404
405
406
407
408
409
410
411
412
413 public SubmoduleWalk loadModulesConfig() throws IOException, ConfigInvalidException {
414 if (rootTree == null) {
415 File modulesFile = new File(repository.getWorkTree(),
416 Constants.DOT_GIT_MODULES);
417 FileBasedConfig config = new FileBasedConfig(modulesFile,
418 repository.getFS());
419 config.load();
420 modulesConfig = config;
421 } else {
422 try (TreeWalk configWalk = new TreeWalk(repository)) {
423 configWalk.addTree(rootTree);
424
425
426
427 int idx;
428 for (idx = 0; !rootTree.first(); idx++) {
429 rootTree.back(1);
430 }
431
432 try {
433 configWalk.setRecursive(false);
434 PathFilter filter = PathFilter.create(Constants.DOT_GIT_MODULES);
435 configWalk.setFilter(filter);
436 while (configWalk.next()) {
437 if (filter.isDone(configWalk)) {
438 modulesConfig = new BlobBasedConfig(null, repository,
439 configWalk.getObjectId(0));
440 return this;
441 }
442 }
443 modulesConfig = new Config();
444 } finally {
445 if (idx > 0)
446 rootTree.next(idx);
447 }
448 }
449 }
450 return this;
451 }
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466 public static boolean containsGitModulesFile(Repository repository)
467 throws IOException {
468 if (repository.isBare()) {
469 return false;
470 }
471 File modulesFile = new File(repository.getWorkTree(),
472 Constants.DOT_GIT_MODULES);
473 return (modulesFile.exists());
474 }
475
476 private void lazyLoadModulesConfig() throws IOException, ConfigInvalidException {
477 if (modulesConfig == null)
478 loadModulesConfig();
479 }
480
481
482
483
484
485
486
487 public SubmoduleWalk setFilter(TreeFilter filter) {
488 walk.setFilter(filter);
489 return this;
490 }
491
492
493
494
495
496
497
498
499 public SubmoduleWalk setTree(final AbstractTreeIterator iterator)
500 throws CorruptObjectException {
501 walk.addTree(iterator);
502 return this;
503 }
504
505
506
507
508
509
510
511
512
513
514 public SubmoduleWalk setTree(final AnyObjectId treeId) throws IOException {
515 walk.addTree(treeId);
516 return this;
517 }
518
519
520
521
522
523
524 public SubmoduleWalk reset() {
525 repoConfig = repository.getConfig();
526 modulesConfig = null;
527 walk.reset();
528 return this;
529 }
530
531
532
533
534
535
536 public File getDirectory() {
537 return getSubmoduleDirectory(repository, path);
538 }
539
540
541
542
543
544
545
546
547
548
549 public boolean next() throws IOException {
550 while (walk.next()) {
551 if (FileMode.GITLINK != walk.getFileMode(0))
552 continue;
553 path = walk.getPathString();
554 return true;
555 }
556 path = null;
557 return false;
558 }
559
560
561
562
563
564
565 public String getPath() {
566 return path;
567 }
568
569
570
571
572
573
574 public ObjectId getObjectId() {
575 return walk.getObjectId(0);
576 }
577
578
579
580
581
582
583
584
585
586 public String getModulesPath() throws IOException, ConfigInvalidException {
587 lazyLoadModulesConfig();
588 return modulesConfig.getString(
589 ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
590 ConfigConstants.CONFIG_KEY_PATH);
591 }
592
593
594
595
596
597
598
599
600
601 public String getConfigUrl() throws IOException, ConfigInvalidException {
602 return repoConfig.getString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
603 path, ConfigConstants.CONFIG_KEY_URL);
604 }
605
606
607
608
609
610
611
612
613
614 public String getModulesUrl() throws IOException, ConfigInvalidException {
615 lazyLoadModulesConfig();
616 return modulesConfig.getString(
617 ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
618 ConfigConstants.CONFIG_KEY_URL);
619 }
620
621
622
623
624
625
626
627
628
629 public String getConfigUpdate() throws IOException, ConfigInvalidException {
630 return repoConfig.getString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
631 path, ConfigConstants.CONFIG_KEY_UPDATE);
632 }
633
634
635
636
637
638
639
640
641
642 public String getModulesUpdate() throws IOException, ConfigInvalidException {
643 lazyLoadModulesConfig();
644 return modulesConfig.getString(
645 ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
646 ConfigConstants.CONFIG_KEY_UPDATE);
647 }
648
649
650
651
652
653
654
655
656
657
658 public IgnoreSubmoduleMode getModulesIgnore() throws IOException,
659 ConfigInvalidException {
660 lazyLoadModulesConfig();
661 String name = modulesConfig.getString(
662 ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
663 ConfigConstants.CONFIG_KEY_IGNORE);
664 if (name == null)
665 return null;
666 return IgnoreSubmoduleMode.valueOf(name.trim().toUpperCase());
667 }
668
669
670
671
672
673
674
675 public Repository getRepository() throws IOException {
676 return getSubmoduleRepository(repository, path);
677 }
678
679
680
681
682
683
684
685 public ObjectId getHead() throws IOException {
686 Repository subRepo = getRepository();
687 if (subRepo == null)
688 return null;
689 try {
690 return subRepo.resolve(Constants.HEAD);
691 } finally {
692 subRepo.close();
693 }
694 }
695
696
697
698
699
700
701
702 public String getHeadRef() throws IOException {
703 Repository subRepo = getRepository();
704 if (subRepo == null)
705 return null;
706 try {
707 Ref head = subRepo.exactRef(Constants.HEAD);
708 return head != null ? head.getLeaf().getName() : null;
709 } finally {
710 subRepo.close();
711 }
712 }
713
714
715
716
717
718
719
720
721
722
723
724 public String getRemoteUrl() throws IOException, ConfigInvalidException {
725 String url = getModulesUrl();
726 return url != null ? getSubmoduleRemoteUrl(repository, url) : null;
727 }
728
729
730
731
732
733
734 @Override
735 public void close() {
736 walk.close();
737 }
738 }