1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.lib;
12
13 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_CORE_SECTION;
14 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_BARE;
15 import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_WORKTREE;
16 import static org.eclipse.jgit.lib.Constants.DOT_GIT;
17 import static org.eclipse.jgit.lib.Constants.GIT_ALTERNATE_OBJECT_DIRECTORIES_KEY;
18 import static org.eclipse.jgit.lib.Constants.GIT_CEILING_DIRECTORIES_KEY;
19 import static org.eclipse.jgit.lib.Constants.GIT_DIR_KEY;
20 import static org.eclipse.jgit.lib.Constants.GIT_INDEX_FILE_KEY;
21 import static org.eclipse.jgit.lib.Constants.GIT_OBJECT_DIRECTORY_KEY;
22 import static org.eclipse.jgit.lib.Constants.GIT_WORK_TREE_KEY;
23
24 import java.io.File;
25 import java.io.IOException;
26 import java.text.MessageFormat;
27 import java.util.Collection;
28 import java.util.LinkedList;
29 import java.util.List;
30
31 import org.eclipse.jgit.errors.ConfigInvalidException;
32 import org.eclipse.jgit.errors.RepositoryNotFoundException;
33 import org.eclipse.jgit.internal.JGitText;
34 import org.eclipse.jgit.internal.storage.file.FileRepository;
35 import org.eclipse.jgit.lib.RepositoryCache.FileKey;
36 import org.eclipse.jgit.storage.file.FileBasedConfig;
37 import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
38 import org.eclipse.jgit.util.FS;
39 import org.eclipse.jgit.util.IO;
40 import org.eclipse.jgit.util.RawParseUtils;
41 import org.eclipse.jgit.util.SystemReader;
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Repository> {
57 private static boolean isSymRef(byte[] ref) {
58 if (ref.length < 9)
59 return false;
60 return ref[0] == 'g'
61 && ref[1] == 'i'
62 && ref[2] == 't'
63 && ref[3] == 'd'
64 && ref[4] == 'i'
65 && ref[5] == 'r'
66 && ref[6] == ':'
67 && ref[7] == ' ';
68 }
69
70 private static File getSymRef(File workTree, File dotGit, FS fs)
71 throws IOException {
72 byte[] content = IO.readFully(dotGit);
73 if (!isSymRef(content)) {
74 throw new IOException(MessageFormat.format(
75 JGitText.get().invalidGitdirRef, dotGit.getAbsolutePath()));
76 }
77
78 int pathStart = 8;
79 int lineEnd = RawParseUtils.nextLF(content, pathStart);
80 while (content[lineEnd - 1] == '\n' ||
81 (content[lineEnd - 1] == '\r'
82 && SystemReader.getInstance().isWindows())) {
83 lineEnd--;
84 }
85 if (lineEnd == pathStart) {
86 throw new IOException(MessageFormat.format(
87 JGitText.get().invalidGitdirRef, dotGit.getAbsolutePath()));
88 }
89
90 String gitdirPath = RawParseUtils.decode(content, pathStart, lineEnd);
91 File gitdirFile = fs.resolve(workTree, gitdirPath);
92 if (gitdirFile.isAbsolute()) {
93 return gitdirFile;
94 }
95 return new File(workTree, gitdirPath).getCanonicalFile();
96 }
97
98 private FS fs;
99
100 private File gitDir;
101
102 private File objectDirectory;
103
104 private List<File> alternateObjectDirectories;
105
106 private File indexFile;
107
108 private File workTree;
109
110
111 private List<File> ceilingDirectories;
112
113
114 private boolean bare;
115
116
117 private boolean mustExist;
118
119
120 private Config config;
121
122
123
124
125
126
127
128
129 public B setFS(FS fs) {
130 this.fs = fs;
131 return self();
132 }
133
134
135
136
137
138
139 public FS getFS() {
140 return fs;
141 }
142
143
144
145
146
147
148
149
150
151
152
153
154 public B setGitDir(File gitDir) {
155 this.gitDir = gitDir;
156 this.config = null;
157 return self();
158 }
159
160
161
162
163
164
165 public File getGitDir() {
166 return gitDir;
167 }
168
169
170
171
172
173
174
175
176
177 public B setObjectDirectory(File objectDirectory) {
178 this.objectDirectory = objectDirectory;
179 return self();
180 }
181
182
183
184
185
186
187 public File getObjectDirectory() {
188 return objectDirectory;
189 }
190
191
192
193
194
195
196
197
198
199
200
201 public B addAlternateObjectDirectory(File other) {
202 if (other != null) {
203 if (alternateObjectDirectories == null)
204 alternateObjectDirectories = new LinkedList<>();
205 alternateObjectDirectories.add(other);
206 }
207 return self();
208 }
209
210
211
212
213
214
215
216
217
218
219
220
221 public B addAlternateObjectDirectories(Collection<File> inList) {
222 if (inList != null) {
223 for (File path : inList)
224 addAlternateObjectDirectory(path);
225 }
226 return self();
227 }
228
229
230
231
232
233
234
235
236
237
238
239
240 public B addAlternateObjectDirectories(File[] inList) {
241 if (inList != null) {
242 for (File path : inList)
243 addAlternateObjectDirectory(path);
244 }
245 return self();
246 }
247
248
249
250
251
252
253 public File[] getAlternateObjectDirectories() {
254 final List<File> alts = alternateObjectDirectories;
255 if (alts == null)
256 return null;
257 return alts.toArray(new File[0]);
258 }
259
260
261
262
263
264
265
266
267
268 public B setBare() {
269 setIndexFile(null);
270 setWorkTree(null);
271 bare = true;
272 return self();
273 }
274
275
276
277
278
279
280 public boolean isBare() {
281 return bare;
282 }
283
284
285
286
287
288
289
290
291
292 public B setMustExist(boolean mustExist) {
293 this.mustExist = mustExist;
294 return self();
295 }
296
297
298
299
300
301
302 public boolean isMustExist() {
303 return mustExist;
304 }
305
306
307
308
309
310
311
312
313 public B setWorkTree(File workTree) {
314 this.workTree = workTree;
315 return self();
316 }
317
318
319
320
321
322
323 public File getWorkTree() {
324 return workTree;
325 }
326
327
328
329
330
331
332
333
334
335
336
337
338 public B setIndexFile(File indexFile) {
339 this.indexFile = indexFile;
340 return self();
341 }
342
343
344
345
346
347
348 public File getIndexFile() {
349 return indexFile;
350 }
351
352
353
354
355
356
357
358
359
360
361
362 public B readEnvironment() {
363 return readEnvironment(SystemReader.getInstance());
364 }
365
366
367
368
369
370
371
372
373
374
375
376
377
378 public B readEnvironment(SystemReader sr) {
379 if (getGitDir() == null) {
380 String val = sr.getenv(GIT_DIR_KEY);
381 if (val != null)
382 setGitDir(new File(val));
383 }
384
385 if (getObjectDirectory() == null) {
386 String val = sr.getenv(GIT_OBJECT_DIRECTORY_KEY);
387 if (val != null)
388 setObjectDirectory(new File(val));
389 }
390
391 if (getAlternateObjectDirectories() == null) {
392 String val = sr.getenv(GIT_ALTERNATE_OBJECT_DIRECTORIES_KEY);
393 if (val != null) {
394 for (String path : val.split(File.pathSeparator))
395 addAlternateObjectDirectory(new File(path));
396 }
397 }
398
399 if (getWorkTree() == null) {
400 String val = sr.getenv(GIT_WORK_TREE_KEY);
401 if (val != null)
402 setWorkTree(new File(val));
403 }
404
405 if (getIndexFile() == null) {
406 String val = sr.getenv(GIT_INDEX_FILE_KEY);
407 if (val != null)
408 setIndexFile(new File(val));
409 }
410
411 if (ceilingDirectories == null) {
412 String val = sr.getenv(GIT_CEILING_DIRECTORIES_KEY);
413 if (val != null) {
414 for (String path : val.split(File.pathSeparator))
415 addCeilingDirectory(new File(path));
416 }
417 }
418
419 return self();
420 }
421
422
423
424
425
426
427
428
429
430
431
432 public B addCeilingDirectory(File root) {
433 if (root != null) {
434 if (ceilingDirectories == null)
435 ceilingDirectories = new LinkedList<>();
436 ceilingDirectories.add(root);
437 }
438 return self();
439 }
440
441
442
443
444
445
446
447
448
449
450
451
452 public B addCeilingDirectories(Collection<File> inList) {
453 if (inList != null) {
454 for (File path : inList)
455 addCeilingDirectory(path);
456 }
457 return self();
458 }
459
460
461
462
463
464
465
466
467
468
469
470
471 public B addCeilingDirectories(File[] inList) {
472 if (inList != null) {
473 for (File path : inList)
474 addCeilingDirectory(path);
475 }
476 return self();
477 }
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492 public B findGitDir() {
493 if (getGitDir() == null)
494 findGitDir(new File("").getAbsoluteFile());
495 return self();
496 }
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513 public B findGitDir(File current) {
514 if (getGitDir() == null) {
515 FS tryFS = safeFS();
516 while (current != null) {
517 File dir = new File(current, DOT_GIT);
518 if (FileKey.isGitRepository(dir, tryFS)) {
519 setGitDir(dir);
520 break;
521 } else if (dir.isFile()) {
522 try {
523 setGitDir(getSymRef(current, dir, tryFS));
524 break;
525 } catch (IOException ignored) {
526
527 }
528 } else if (FileKey.isGitRepository(current, tryFS)) {
529 setGitDir(current);
530 break;
531 }
532
533 current = current.getParentFile();
534 if (current != null && ceilingDirectories != null
535 && ceilingDirectories.contains(current))
536 break;
537 }
538 }
539 return self();
540 }
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557 public B setup() throws IllegalArgumentException, IOException {
558 requireGitDirOrWorkTree();
559 setupGitDir();
560 setupWorkTree();
561 setupInternals();
562 return self();
563 }
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581 @SuppressWarnings({ "unchecked", "resource" })
582 public R build() throws IOException {
583 R repo = (R) new FileRepository(setup());
584 if (isMustExist() && !repo.getObjectDatabase().exists())
585 throw new RepositoryNotFoundException(getGitDir());
586 return repo;
587 }
588
589
590
591
592 protected void requireGitDirOrWorkTree() {
593 if (getGitDir() == null && getWorkTree() == null)
594 throw new IllegalArgumentException(
595 JGitText.get().eitherGitDirOrWorkTreeRequired);
596 }
597
598
599
600
601
602
603
604 protected void setupGitDir() throws IOException {
605
606
607 if (getGitDir() == null && getWorkTree() != null) {
608 File dotGit = new File(getWorkTree(), DOT_GIT);
609 if (!dotGit.isFile())
610 setGitDir(dotGit);
611 else
612 setGitDir(getSymRef(getWorkTree(), dotGit, safeFS()));
613 }
614 }
615
616
617
618
619
620
621
622
623
624
625
626 protected void setupWorkTree() throws IOException {
627 if (getFS() == null)
628 setFS(FS.DETECTED);
629
630
631
632 if (!isBare() && getWorkTree() == null)
633 setWorkTree(guessWorkTreeOrFail());
634
635 if (!isBare()) {
636
637
638
639
640 if (getGitDir() == null)
641 setGitDir(getWorkTree().getParentFile());
642 if (getIndexFile() == null)
643 setIndexFile(new File(getGitDir(), "index"));
644 }
645 }
646
647
648
649
650
651
652
653 protected void setupInternals() throws IOException {
654 if (getObjectDirectory() == null && getGitDir() != null)
655 setObjectDirectory(safeFS().resolve(getGitDir(), Constants.OBJECTS));
656 }
657
658
659
660
661
662
663
664
665 protected Config getConfig() throws IOException {
666 if (config == null)
667 config = loadConfig();
668 return config;
669 }
670
671
672
673
674
675
676
677
678
679
680
681 protected Config loadConfig() throws IOException {
682 if (getGitDir() != null) {
683
684
685
686
687 File path = safeFS().resolve(getGitDir(), Constants.CONFIG);
688 FileBasedConfig cfg = new FileBasedConfig(path, safeFS());
689 try {
690 cfg.load();
691 } catch (ConfigInvalidException err) {
692 throw new IllegalArgumentException(MessageFormat.format(
693 JGitText.get().repositoryConfigFileInvalid, path
694 .getAbsolutePath(), err.getMessage()));
695 }
696 return cfg;
697 }
698 return new Config();
699 }
700
701 private File guessWorkTreeOrFail() throws IOException {
702 final Config cfg = getConfig();
703
704
705
706 String path = cfg.getString(CONFIG_CORE_SECTION, null,
707 CONFIG_KEY_WORKTREE);
708 if (path != null)
709 return safeFS().resolve(getGitDir(), path).getCanonicalFile();
710
711
712
713
714 if (cfg.getString(CONFIG_CORE_SECTION, null, CONFIG_KEY_BARE) != null) {
715 if (cfg.getBoolean(CONFIG_CORE_SECTION, CONFIG_KEY_BARE, true)) {
716 setBare();
717 return null;
718 }
719 return getGitDir().getParentFile();
720 }
721
722 if (getGitDir().getName().equals(DOT_GIT)) {
723
724
725
726 return getGitDir().getParentFile();
727 }
728
729
730
731 setBare();
732 return null;
733 }
734
735
736
737
738
739
740 protected FS safeFS() {
741 return getFS() != null ? getFS() : FS.DETECTED;
742 }
743
744
745
746
747
748
749 @SuppressWarnings("unchecked")
750 protected final B self() {
751 return (B) this;
752 }
753 }