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