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