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