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
662
663 public String getModuleName() throws IOException, ConfigInvalidException {
664 lazyLoadModulesConfig();
665 return getModuleName(path);
666 }
667
668
669
670
671
672
673 public ObjectId getObjectId() {
674 return walk.getObjectId(0);
675 }
676
677
678
679
680
681
682
683
684
685 public String getModulesPath() throws IOException, ConfigInvalidException {
686 lazyLoadModulesConfig();
687 return modulesConfig.getString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
688 getModuleName(), ConfigConstants.CONFIG_KEY_PATH);
689 }
690
691
692
693
694
695
696
697
698
699 public String getConfigUrl() throws IOException, ConfigInvalidException {
700 return repoConfig.getString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
701 getModuleName(), ConfigConstants.CONFIG_KEY_URL);
702 }
703
704
705
706
707
708
709
710
711
712 public String getModulesUrl() throws IOException, ConfigInvalidException {
713 lazyLoadModulesConfig();
714 return modulesConfig.getString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
715 getModuleName(), ConfigConstants.CONFIG_KEY_URL);
716 }
717
718
719
720
721
722
723
724
725
726 public String getConfigUpdate() throws IOException, ConfigInvalidException {
727 return repoConfig.getString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
728 getModuleName(), ConfigConstants.CONFIG_KEY_UPDATE);
729 }
730
731
732
733
734
735
736
737
738
739 public String getModulesUpdate() throws IOException, ConfigInvalidException {
740 lazyLoadModulesConfig();
741 return modulesConfig.getString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
742 getModuleName(), ConfigConstants.CONFIG_KEY_UPDATE);
743 }
744
745
746
747
748
749
750
751
752
753
754 public IgnoreSubmoduleMode getModulesIgnore() throws IOException,
755 ConfigInvalidException {
756 IgnoreSubmoduleMode mode = repoConfig.getEnum(
757 IgnoreSubmoduleMode.values(),
758 ConfigConstants.CONFIG_SUBMODULE_SECTION, getModuleName(),
759 ConfigConstants.CONFIG_KEY_IGNORE, null);
760 if (mode != null) {
761 return mode;
762 }
763 lazyLoadModulesConfig();
764 return modulesConfig.getEnum(IgnoreSubmoduleMode.values(),
765 ConfigConstants.CONFIG_SUBMODULE_SECTION, getModuleName(),
766 ConfigConstants.CONFIG_KEY_IGNORE, IgnoreSubmoduleMode.NONE);
767 }
768
769
770
771
772
773
774
775 public Repository getRepository() throws IOException {
776 return getSubmoduleRepository(repository.getWorkTree(), path,
777 repository.getFS(), getBuilder());
778 }
779
780
781
782
783
784
785
786 public ObjectId getHead() throws IOException {
787 try (Repository subRepo = getRepository()) {
788 if (subRepo == null) {
789 return null;
790 }
791 return subRepo.resolve(Constants.HEAD);
792 }
793 }
794
795
796
797
798
799
800
801 public String getHeadRef() throws IOException {
802 try (Repository subRepo = getRepository()) {
803 if (subRepo == null) {
804 return null;
805 }
806 Ref head = subRepo.exactRef(Constants.HEAD);
807 return head != null ? head.getLeaf().getName() : null;
808 }
809 }
810
811
812
813
814
815
816
817
818
819
820
821 public String getRemoteUrl() throws IOException, ConfigInvalidException {
822 String url = getModulesUrl();
823 return url != null ? getSubmoduleRemoteUrl(repository, url) : null;
824 }
825
826
827
828
829
830
831
832
833 @Override
834 public void close() {
835 walk.close();
836 }
837 }