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