View Javadoc
1   /*
2    * Copyright (C) 2010, Google Inc. and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
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.annotations.NonNull;
32  import org.eclipse.jgit.api.errors.InvalidRefNameException;
33  import org.eclipse.jgit.errors.ConfigInvalidException;
34  import org.eclipse.jgit.errors.RepositoryNotFoundException;
35  import org.eclipse.jgit.internal.JGitText;
36  import org.eclipse.jgit.internal.storage.file.FileRepository;
37  import org.eclipse.jgit.lib.RepositoryCache.FileKey;
38  import org.eclipse.jgit.storage.file.FileBasedConfig;
39  import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
40  import org.eclipse.jgit.util.FS;
41  import org.eclipse.jgit.util.IO;
42  import org.eclipse.jgit.util.RawParseUtils;
43  import org.eclipse.jgit.util.StringUtils;
44  import org.eclipse.jgit.util.SystemReader;
45  
46  /**
47   * Base builder to customize repository construction.
48   * <p>
49   * Repository implementations may subclass this builder in order to add custom
50   * repository detection methods.
51   *
52   * @param <B>
53   *            type of the repository builder.
54   * @param <R>
55   *            type of the repository that is constructed.
56   * @see RepositoryBuilder
57   * @see FileRepositoryBuilder
58   */
59  public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Repository> {
60  	private static boolean isSymRef(byte[] ref) {
61  		if (ref.length < 9)
62  			return false;
63  		return /**/ref[0] == 'g' //
64  				&& ref[1] == 'i' //
65  				&& ref[2] == 't' //
66  				&& ref[3] == 'd' //
67  				&& ref[4] == 'i' //
68  				&& ref[5] == 'r' //
69  				&& ref[6] == ':' //
70  				&& ref[7] == ' ';
71  	}
72  
73  	private static File getSymRef(File workTree, File dotGit, FS fs)
74  			throws IOException {
75  		byte[] content = IO.readFully(dotGit);
76  		if (!isSymRef(content)) {
77  			throw new IOException(MessageFormat.format(
78  					JGitText.get().invalidGitdirRef, dotGit.getAbsolutePath()));
79  		}
80  
81  		int pathStart = 8;
82  		int lineEnd = RawParseUtils.nextLF(content, pathStart);
83  		while (content[lineEnd - 1] == '\n' ||
84  				(content[lineEnd - 1] == '\r'
85  						&& SystemReader.getInstance().isWindows())) {
86  			lineEnd--;
87  		}
88  		if (lineEnd == pathStart) {
89  			throw new IOException(MessageFormat.format(
90  					JGitText.get().invalidGitdirRef, dotGit.getAbsolutePath()));
91  		}
92  
93  		String gitdirPath = RawParseUtils.decode(content, pathStart, lineEnd);
94  		File gitdirFile = fs.resolve(workTree, gitdirPath);
95  		if (gitdirFile.isAbsolute()) {
96  			return gitdirFile;
97  		}
98  		return new File(workTree, gitdirPath).getCanonicalFile();
99  	}
100 
101 	private FS fs;
102 
103 	private File gitDir;
104 
105 	private File objectDirectory;
106 
107 	private List<File> alternateObjectDirectories;
108 
109 	private File indexFile;
110 
111 	private File workTree;
112 
113 	private String initialBranch = Constants.MASTER;
114 
115 	/** Directories limiting the search for a Git repository. */
116 	private List<File> ceilingDirectories;
117 
118 	/** True only if the caller wants to force bare behavior. */
119 	private boolean bare;
120 
121 	/** True if the caller requires the repository to exist. */
122 	private boolean mustExist;
123 
124 	/** Configuration file of target repository, lazily loaded if required. */
125 	private Config config;
126 
127 	/**
128 	 * Set the file system abstraction needed by this repository.
129 	 *
130 	 * @param fs
131 	 *            the abstraction.
132 	 * @return {@code this} (for chaining calls).
133 	 */
134 	public B setFS(FS fs) {
135 		this.fs = fs;
136 		return self();
137 	}
138 
139 	/**
140 	 * Get the file system abstraction, or null if not set.
141 	 *
142 	 * @return the file system abstraction, or null if not set.
143 	 */
144 	public FS getFS() {
145 		return fs;
146 	}
147 
148 	/**
149 	 * Set the Git directory storing the repository metadata.
150 	 * <p>
151 	 * The meta directory stores the objects, references, and meta files like
152 	 * {@code MERGE_HEAD}, or the index file. If {@code null} the path is
153 	 * assumed to be {@code workTree/.git}.
154 	 *
155 	 * @param gitDir
156 	 *            {@code GIT_DIR}, the repository meta directory.
157 	 * @return {@code this} (for chaining calls).
158 	 */
159 	public B setGitDir(File gitDir) {
160 		this.gitDir = gitDir;
161 		this.config = null;
162 		return self();
163 	}
164 
165 	/**
166 	 * Get the meta data directory; null if not set.
167 	 *
168 	 * @return the meta data directory; null if not set.
169 	 */
170 	public File getGitDir() {
171 		return gitDir;
172 	}
173 
174 	/**
175 	 * Set the directory storing the repository's objects.
176 	 *
177 	 * @param objectDirectory
178 	 *            {@code GIT_OBJECT_DIRECTORY}, the directory where the
179 	 *            repository's object files are stored.
180 	 * @return {@code this} (for chaining calls).
181 	 */
182 	public B setObjectDirectory(File objectDirectory) {
183 		this.objectDirectory = objectDirectory;
184 		return self();
185 	}
186 
187 	/**
188 	 * Get the object directory; null if not set.
189 	 *
190 	 * @return the object directory; null if not set.
191 	 */
192 	public File getObjectDirectory() {
193 		return objectDirectory;
194 	}
195 
196 	/**
197 	 * Add an alternate object directory to the search list.
198 	 * <p>
199 	 * This setting handles one alternate directory at a time, and is provided
200 	 * to support {@code GIT_ALTERNATE_OBJECT_DIRECTORIES}.
201 	 *
202 	 * @param other
203 	 *            another objects directory to search after the standard one.
204 	 * @return {@code this} (for chaining calls).
205 	 */
206 	public B addAlternateObjectDirectory(File other) {
207 		if (other != null) {
208 			if (alternateObjectDirectories == null)
209 				alternateObjectDirectories = new LinkedList<>();
210 			alternateObjectDirectories.add(other);
211 		}
212 		return self();
213 	}
214 
215 	/**
216 	 * Add alternate object directories to the search list.
217 	 * <p>
218 	 * This setting handles several alternate directories at once, and is
219 	 * provided to support {@code GIT_ALTERNATE_OBJECT_DIRECTORIES}.
220 	 *
221 	 * @param inList
222 	 *            other object directories to search after the standard one. The
223 	 *            collection's contents is copied to an internal list.
224 	 * @return {@code this} (for chaining calls).
225 	 */
226 	public B addAlternateObjectDirectories(Collection<File> inList) {
227 		if (inList != null) {
228 			for (File path : inList)
229 				addAlternateObjectDirectory(path);
230 		}
231 		return self();
232 	}
233 
234 	/**
235 	 * Add alternate object directories to the search list.
236 	 * <p>
237 	 * This setting handles several alternate directories at once, and is
238 	 * provided to support {@code GIT_ALTERNATE_OBJECT_DIRECTORIES}.
239 	 *
240 	 * @param inList
241 	 *            other object directories to search after the standard one. The
242 	 *            array's contents is copied to an internal list.
243 	 * @return {@code this} (for chaining calls).
244 	 */
245 	public B addAlternateObjectDirectories(File[] inList) {
246 		if (inList != null) {
247 			for (File path : inList)
248 				addAlternateObjectDirectory(path);
249 		}
250 		return self();
251 	}
252 
253 	/**
254 	 * Get ordered array of alternate directories; null if non were set.
255 	 *
256 	 * @return ordered array of alternate directories; null if non were set.
257 	 */
258 	public File[] getAlternateObjectDirectories() {
259 		final List<File> alts = alternateObjectDirectories;
260 		if (alts == null)
261 			return null;
262 		return alts.toArray(new File[0]);
263 	}
264 
265 	/**
266 	 * Force the repository to be treated as bare (have no working directory).
267 	 * <p>
268 	 * If bare the working directory aspects of the repository won't be
269 	 * configured, and will not be accessible.
270 	 *
271 	 * @return {@code this} (for chaining calls).
272 	 */
273 	public B setBare() {
274 		setIndexFile(null);
275 		setWorkTree(null);
276 		bare = true;
277 		return self();
278 	}
279 
280 	/**
281 	 * Whether this repository was forced bare by {@link #setBare()}.
282 	 *
283 	 * @return true if this repository was forced bare by {@link #setBare()}.
284 	 */
285 	public boolean isBare() {
286 		return bare;
287 	}
288 
289 	/**
290 	 * Require the repository to exist before it can be opened.
291 	 *
292 	 * @param mustExist
293 	 *            true if it must exist; false if it can be missing and created
294 	 *            after being built.
295 	 * @return {@code this} (for chaining calls).
296 	 */
297 	public B setMustExist(boolean mustExist) {
298 		this.mustExist = mustExist;
299 		return self();
300 	}
301 
302 	/**
303 	 * Whether the repository must exist before being opened.
304 	 *
305 	 * @return true if the repository must exist before being opened.
306 	 */
307 	public boolean isMustExist() {
308 		return mustExist;
309 	}
310 
311 	/**
312 	 * Set the top level directory of the working files.
313 	 *
314 	 * @param workTree
315 	 *            {@code GIT_WORK_TREE}, the working directory of the checkout.
316 	 * @return {@code this} (for chaining calls).
317 	 */
318 	public B setWorkTree(File workTree) {
319 		this.workTree = workTree;
320 		return self();
321 	}
322 
323 	/**
324 	 * Get the work tree directory, or null if not set.
325 	 *
326 	 * @return the work tree directory, or null if not set.
327 	 */
328 	public File getWorkTree() {
329 		return workTree;
330 	}
331 
332 	/**
333 	 * Set the local index file that is caching checked out file status.
334 	 * <p>
335 	 * The location of the index file tracking the status information for each
336 	 * checked out file in {@code workTree}. This may be null to assume the
337 	 * default {@code gitDiir/index}.
338 	 *
339 	 * @param indexFile
340 	 *            {@code GIT_INDEX_FILE}, the index file location.
341 	 * @return {@code this} (for chaining calls).
342 	 */
343 	public B setIndexFile(File indexFile) {
344 		this.indexFile = indexFile;
345 		return self();
346 	}
347 
348 	/**
349 	 * Get the index file location, or null if not set.
350 	 *
351 	 * @return the index file location, or null if not set.
352 	 */
353 	public File getIndexFile() {
354 		return indexFile;
355 	}
356 
357 	/**
358 	 * Set the initial branch of the new repository. If not specified
359 	 * ({@code null} or empty), fall back to the default name (currently
360 	 * master).
361 	 *
362 	 * @param branch
363 	 *            initial branch name of the new repository. If {@code null} or
364 	 *            empty the configured default branch will be used.
365 	 * @return {@code this}
366 	 * @throws InvalidRefNameException
367 	 *             if the branch name is not valid
368 	 *
369 	 * @since 5.11
370 	 */
371 	public B setInitialBranch(String branch) throws InvalidRefNameException {
372 		if (StringUtils.isEmptyOrNull(branch)) {
373 			this.initialBranch = Constants.MASTER;
374 		} else {
375 			if (!Repository.isValidRefName(Constants.R_HEADS + branch)) {
376 				throw new InvalidRefNameException(MessageFormat
377 						.format(JGitText.get().branchNameInvalid, branch));
378 			}
379 			this.initialBranch = branch;
380 		}
381 		return self();
382 	}
383 
384 	/**
385 	 * Get the initial branch of the new repository.
386 	 *
387 	 * @return the initial branch of the new repository.
388 	 * @since 5.11
389 	 */
390 	public @NonNull String getInitialBranch() {
391 		return initialBranch;
392 	}
393 
394 	/**
395 	 * Read standard Git environment variables and configure from those.
396 	 * <p>
397 	 * This method tries to read the standard Git environment variables, such as
398 	 * {@code GIT_DIR} and {@code GIT_WORK_TREE} to configure this builder
399 	 * instance. If an environment variable is set, it overrides the value
400 	 * already set in this builder.
401 	 *
402 	 * @return {@code this} (for chaining calls).
403 	 */
404 	public B readEnvironment() {
405 		return readEnvironment(SystemReader.getInstance());
406 	}
407 
408 	/**
409 	 * Read standard Git environment variables and configure from those.
410 	 * <p>
411 	 * This method tries to read the standard Git environment variables, such as
412 	 * {@code GIT_DIR} and {@code GIT_WORK_TREE} to configure this builder
413 	 * instance. If a property is already set in the builder, the environment
414 	 * variable is not used.
415 	 *
416 	 * @param sr
417 	 *            the SystemReader abstraction to access the environment.
418 	 * @return {@code this} (for chaining calls).
419 	 */
420 	public B readEnvironment(SystemReader sr) {
421 		if (getGitDir() == null) {
422 			String val = sr.getenv(GIT_DIR_KEY);
423 			if (val != null)
424 				setGitDir(new File(val));
425 		}
426 
427 		if (getObjectDirectory() == null) {
428 			String val = sr.getenv(GIT_OBJECT_DIRECTORY_KEY);
429 			if (val != null)
430 				setObjectDirectory(new File(val));
431 		}
432 
433 		if (getAlternateObjectDirectories() == null) {
434 			String val = sr.getenv(GIT_ALTERNATE_OBJECT_DIRECTORIES_KEY);
435 			if (val != null) {
436 				for (String path : val.split(File.pathSeparator))
437 					addAlternateObjectDirectory(new File(path));
438 			}
439 		}
440 
441 		if (getWorkTree() == null) {
442 			String val = sr.getenv(GIT_WORK_TREE_KEY);
443 			if (val != null)
444 				setWorkTree(new File(val));
445 		}
446 
447 		if (getIndexFile() == null) {
448 			String val = sr.getenv(GIT_INDEX_FILE_KEY);
449 			if (val != null)
450 				setIndexFile(new File(val));
451 		}
452 
453 		if (ceilingDirectories == null) {
454 			String val = sr.getenv(GIT_CEILING_DIRECTORIES_KEY);
455 			if (val != null) {
456 				for (String path : val.split(File.pathSeparator))
457 					addCeilingDirectory(new File(path));
458 			}
459 		}
460 
461 		return self();
462 	}
463 
464 	/**
465 	 * Add a ceiling directory to the search limit list.
466 	 * <p>
467 	 * This setting handles one ceiling directory at a time, and is provided to
468 	 * support {@code GIT_CEILING_DIRECTORIES}.
469 	 *
470 	 * @param root
471 	 *            a path to stop searching at; its parent will not be searched.
472 	 * @return {@code this} (for chaining calls).
473 	 */
474 	public B addCeilingDirectory(File root) {
475 		if (root != null) {
476 			if (ceilingDirectories == null)
477 				ceilingDirectories = new LinkedList<>();
478 			ceilingDirectories.add(root);
479 		}
480 		return self();
481 	}
482 
483 	/**
484 	 * Add ceiling directories to the search list.
485 	 * <p>
486 	 * This setting handles several ceiling directories at once, and is provided
487 	 * to support {@code GIT_CEILING_DIRECTORIES}.
488 	 *
489 	 * @param inList
490 	 *            directory paths to stop searching at. The collection's
491 	 *            contents is copied to an internal list.
492 	 * @return {@code this} (for chaining calls).
493 	 */
494 	public B addCeilingDirectories(Collection<File> inList) {
495 		if (inList != null) {
496 			for (File path : inList)
497 				addCeilingDirectory(path);
498 		}
499 		return self();
500 	}
501 
502 	/**
503 	 * Add ceiling directories to the search list.
504 	 * <p>
505 	 * This setting handles several ceiling directories at once, and is provided
506 	 * to support {@code GIT_CEILING_DIRECTORIES}.
507 	 *
508 	 * @param inList
509 	 *            directory paths to stop searching at. The array's contents is
510 	 *            copied to an internal list.
511 	 * @return {@code this} (for chaining calls).
512 	 */
513 	public B addCeilingDirectories(File[] inList) {
514 		if (inList != null) {
515 			for (File path : inList)
516 				addCeilingDirectory(path);
517 		}
518 		return self();
519 	}
520 
521 	/**
522 	 * Configure {@code GIT_DIR} by searching up the file system.
523 	 * <p>
524 	 * Starts from the current working directory of the JVM and scans up through
525 	 * the directory tree until a Git repository is found. Success can be
526 	 * determined by checking for {@code getGitDir() != null}.
527 	 * <p>
528 	 * The search can be limited to specific spaces of the local filesystem by
529 	 * {@link #addCeilingDirectory(File)}, or inheriting the list through a
530 	 * prior call to {@link #readEnvironment()}.
531 	 *
532 	 * @return {@code this} (for chaining calls).
533 	 */
534 	public B findGitDir() {
535 		if (getGitDir() == null)
536 			findGitDir(new File("").getAbsoluteFile()); //$NON-NLS-1$
537 		return self();
538 	}
539 
540 	/**
541 	 * Configure {@code GIT_DIR} by searching up the file system.
542 	 * <p>
543 	 * Starts from the supplied directory path and scans up through the parent
544 	 * directory tree until a Git repository is found. Success can be determined
545 	 * by checking for {@code getGitDir() != null}.
546 	 * <p>
547 	 * The search can be limited to specific spaces of the local filesystem by
548 	 * {@link #addCeilingDirectory(File)}, or inheriting the list through a
549 	 * prior call to {@link #readEnvironment()}.
550 	 *
551 	 * @param current
552 	 *            directory to begin searching in.
553 	 * @return {@code this} (for chaining calls).
554 	 */
555 	public B findGitDir(File current) {
556 		if (getGitDir() == null) {
557 			FS tryFS = safeFS();
558 			while (current != null) {
559 				File dir = new File(current, DOT_GIT);
560 				if (FileKey.isGitRepository(dir, tryFS)) {
561 					setGitDir(dir);
562 					break;
563 				} else if (dir.isFile()) {
564 					try {
565 						setGitDir(getSymRef(current, dir, tryFS));
566 						break;
567 					} catch (IOException ignored) {
568 						// Continue searching if gitdir ref isn't found
569 					}
570 				} else if (FileKey.isGitRepository(current, tryFS)) {
571 					setGitDir(current);
572 					break;
573 				}
574 
575 				current = current.getParentFile();
576 				if (current != null && ceilingDirectories != null
577 						&& ceilingDirectories.contains(current))
578 					break;
579 			}
580 		}
581 		return self();
582 	}
583 
584 	/**
585 	 * Guess and populate all parameters not already defined.
586 	 * <p>
587 	 * If an option was not set, the setup method will try to default the option
588 	 * based on other options. If insufficient information is available, an
589 	 * exception is thrown to the caller.
590 	 *
591 	 * @return {@code this}
592 	 * @throws java.lang.IllegalArgumentException
593 	 *             insufficient parameters were set, or some parameters are
594 	 *             incompatible with one another.
595 	 * @throws java.io.IOException
596 	 *             the repository could not be accessed to configure the rest of
597 	 *             the builder's parameters.
598 	 */
599 	public B setup() throws IllegalArgumentException, IOException {
600 		requireGitDirOrWorkTree();
601 		setupGitDir();
602 		setupWorkTree();
603 		setupInternals();
604 		return self();
605 	}
606 
607 	/**
608 	 * Create a repository matching the configuration in this builder.
609 	 * <p>
610 	 * If an option was not set, the build method will try to default the option
611 	 * based on other options. If insufficient information is available, an
612 	 * exception is thrown to the caller.
613 	 *
614 	 * @return a repository matching this configuration. The caller is
615 	 *         responsible to close the repository instance when it is no longer
616 	 *         needed.
617 	 * @throws java.lang.IllegalArgumentException
618 	 *             insufficient parameters were set.
619 	 * @throws java.io.IOException
620 	 *             the repository could not be accessed to configure the rest of
621 	 *             the builder's parameters.
622 	 */
623 	@SuppressWarnings({ "unchecked", "resource" })
624 	public R build() throws IOException {
625 		R repo = (R) new FileRepository(setup());
626 		if (isMustExist() && !repo.getObjectDatabase().exists())
627 			throw new RepositoryNotFoundException(getGitDir());
628 		return repo;
629 	}
630 
631 	/**
632 	 * Require either {@code gitDir} or {@code workTree} to be set.
633 	 */
634 	protected void requireGitDirOrWorkTree() {
635 		if (getGitDir() == null && getWorkTree() == null)
636 			throw new IllegalArgumentException(
637 					JGitText.get().eitherGitDirOrWorkTreeRequired);
638 	}
639 
640 	/**
641 	 * Perform standard gitDir initialization.
642 	 *
643 	 * @throws java.io.IOException
644 	 *             the repository could not be accessed
645 	 */
646 	protected void setupGitDir() throws IOException {
647 		// No gitDir? Try to assume its under the workTree or a ref to another
648 		// location
649 		if (getGitDir() == null && getWorkTree() != null) {
650 			File dotGit = new File(getWorkTree(), DOT_GIT);
651 			if (!dotGit.isFile())
652 				setGitDir(dotGit);
653 			else
654 				setGitDir(getSymRef(getWorkTree(), dotGit, safeFS()));
655 		}
656 	}
657 
658 	/**
659 	 * Perform standard work-tree initialization.
660 	 * <p>
661 	 * This is a method typically invoked inside of {@link #setup()}, near the
662 	 * end after the repository has been identified and its configuration is
663 	 * available for inspection.
664 	 *
665 	 * @throws java.io.IOException
666 	 *             the repository configuration could not be read.
667 	 */
668 	protected void setupWorkTree() throws IOException {
669 		if (getFS() == null)
670 			setFS(FS.DETECTED);
671 
672 		// If we aren't bare, we should have a work tree.
673 		//
674 		if (!isBare() && getWorkTree() == null)
675 			setWorkTree(guessWorkTreeOrFail());
676 
677 		if (!isBare()) {
678 			// If after guessing we're still not bare, we must have
679 			// a metadata directory to hold the repository. Assume
680 			// its at the work tree.
681 			//
682 			if (getGitDir() == null)
683 				setGitDir(getWorkTree().getParentFile());
684 			if (getIndexFile() == null)
685 				setIndexFile(new File(getGitDir(), "index")); //$NON-NLS-1$
686 		}
687 	}
688 
689 	/**
690 	 * Configure the internal implementation details of the repository.
691 	 *
692 	 * @throws java.io.IOException
693 	 *             the repository could not be accessed
694 	 */
695 	protected void setupInternals() throws IOException {
696 		if (getObjectDirectory() == null && getGitDir() != null)
697 			setObjectDirectory(safeFS().resolve(getGitDir(), Constants.OBJECTS));
698 	}
699 
700 	/**
701 	 * Get the cached repository configuration, loading if not yet available.
702 	 *
703 	 * @return the configuration of the repository.
704 	 * @throws java.io.IOException
705 	 *             the configuration is not available, or is badly formed.
706 	 */
707 	protected Config getConfig() throws IOException {
708 		if (config == null)
709 			config = loadConfig();
710 		return config;
711 	}
712 
713 	/**
714 	 * Parse and load the repository specific configuration.
715 	 * <p>
716 	 * The default implementation reads {@code gitDir/config}, or returns an
717 	 * empty configuration if gitDir was not set.
718 	 *
719 	 * @return the repository's configuration.
720 	 * @throws java.io.IOException
721 	 *             the configuration is not available.
722 	 */
723 	protected Config loadConfig() throws IOException {
724 		if (getGitDir() != null) {
725 			// We only want the repository's configuration file, and not
726 			// the user file, as these parameters must be unique to this
727 			// repository and not inherited from other files.
728 			//
729 			File path = safeFS().resolve(getGitDir(), Constants.CONFIG);
730 			FileBasedConfig cfg = new FileBasedConfig(path, safeFS());
731 			try {
732 				cfg.load();
733 			} catch (ConfigInvalidException err) {
734 				throw new IllegalArgumentException(MessageFormat.format(
735 						JGitText.get().repositoryConfigFileInvalid, path
736 								.getAbsolutePath(), err.getMessage()));
737 			}
738 			return cfg;
739 		}
740 		return new Config();
741 	}
742 
743 	private File guessWorkTreeOrFail() throws IOException {
744 		final Config cfg = getConfig();
745 
746 		// If set, core.worktree wins.
747 		//
748 		String path = cfg.getString(CONFIG_CORE_SECTION, null,
749 				CONFIG_KEY_WORKTREE);
750 		if (path != null)
751 			return safeFS().resolve(getGitDir(), path).getCanonicalFile();
752 
753 		// If core.bare is set, honor its value. Assume workTree is
754 		// the parent directory of the repository.
755 		//
756 		if (cfg.getString(CONFIG_CORE_SECTION, null, CONFIG_KEY_BARE) != null) {
757 			if (cfg.getBoolean(CONFIG_CORE_SECTION, CONFIG_KEY_BARE, true)) {
758 				setBare();
759 				return null;
760 			}
761 			return getGitDir().getParentFile();
762 		}
763 
764 		if (getGitDir().getName().equals(DOT_GIT)) {
765 			// No value for the "bare" flag, but gitDir is named ".git",
766 			// use the parent of the directory
767 			//
768 			return getGitDir().getParentFile();
769 		}
770 
771 		// We have to assume we are bare.
772 		//
773 		setBare();
774 		return null;
775 	}
776 
777 	/**
778 	 * Get the configured FS, or {@link FS#DETECTED}.
779 	 *
780 	 * @return the configured FS, or {@link FS#DETECTED}.
781 	 */
782 	protected FS safeFS() {
783 		return getFS() != null ? getFS() : FS.DETECTED;
784 	}
785 
786 	/**
787 	 * Get this object
788 	 *
789 	 * @return {@code this}
790 	 */
791 	@SuppressWarnings("unchecked")
792 	protected final B self() {
793 		return (B) this;
794 	}
795 }