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