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