Repository.java

  1. /*
  2.  * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
  3.  * Copyright (C) 2008-2010, Google Inc.
  4.  * Copyright (C) 2006-2010, Robin Rosenberg <robin.rosenberg@dewire.com>
  5.  * Copyright (C) 2006-2012, Shawn O. Pearce <spearce@spearce.org>
  6.  * Copyright (C) 2012, Daniel Megert <daniel_megert@ch.ibm.com>
  7.  * Copyright (C) 2017, Wim Jongman <wim.jongman@remainsoftware.com>
  8.  * and other copyright owners as documented in the project's IP log.
  9.  *
  10.  * This program and the accompanying materials are made available
  11.  * under the terms of the Eclipse Distribution License v1.0 which
  12.  * accompanies this distribution, is reproduced below, and is
  13.  * available at http://www.eclipse.org/org/documents/edl-v10.php
  14.  *
  15.  * All rights reserved.
  16.  *
  17.  * Redistribution and use in source and binary forms, with or
  18.  * without modification, are permitted provided that the following
  19.  * conditions are met:
  20.  *
  21.  * - Redistributions of source code must retain the above copyright
  22.  *   notice, this list of conditions and the following disclaimer.
  23.  *
  24.  * - Redistributions in binary form must reproduce the above
  25.  *   copyright notice, this list of conditions and the following
  26.  *   disclaimer in the documentation and/or other materials provided
  27.  *   with the distribution.
  28.  *
  29.  * - Neither the name of the Eclipse Foundation, Inc. nor the
  30.  *   names of its contributors may be used to endorse or promote
  31.  *   products derived from this software without specific prior
  32.  *   written permission.
  33.  *
  34.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  35.  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  36.  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  38.  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  39.  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  41.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  42.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  43.  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  44.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  45.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  46.  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  47.  */

  48. package org.eclipse.jgit.lib;

  49. import static org.eclipse.jgit.lib.Constants.LOCK_SUFFIX;
  50. import static java.nio.charset.StandardCharsets.UTF_8;

  51. import java.io.BufferedOutputStream;
  52. import java.io.File;
  53. import java.io.FileNotFoundException;
  54. import java.io.FileOutputStream;
  55. import java.io.IOException;
  56. import java.io.OutputStream;
  57. import java.io.UncheckedIOException;
  58. import java.net.URISyntaxException;
  59. import java.text.MessageFormat;
  60. import java.util.Collection;
  61. import java.util.Collections;
  62. import java.util.HashMap;
  63. import java.util.HashSet;
  64. import java.util.LinkedList;
  65. import java.util.List;
  66. import java.util.Map;
  67. import java.util.Set;
  68. import java.util.concurrent.atomic.AtomicInteger;
  69. import java.util.concurrent.atomic.AtomicLong;
  70. import java.util.regex.Pattern;

  71. import org.eclipse.jgit.annotations.NonNull;
  72. import org.eclipse.jgit.annotations.Nullable;
  73. import org.eclipse.jgit.attributes.AttributesNodeProvider;
  74. import org.eclipse.jgit.dircache.DirCache;
  75. import org.eclipse.jgit.errors.AmbiguousObjectException;
  76. import org.eclipse.jgit.errors.CorruptObjectException;
  77. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  78. import org.eclipse.jgit.errors.MissingObjectException;
  79. import org.eclipse.jgit.errors.NoWorkTreeException;
  80. import org.eclipse.jgit.errors.RevisionSyntaxException;
  81. import org.eclipse.jgit.events.IndexChangedEvent;
  82. import org.eclipse.jgit.events.IndexChangedListener;
  83. import org.eclipse.jgit.events.ListenerList;
  84. import org.eclipse.jgit.events.RepositoryEvent;
  85. import org.eclipse.jgit.internal.JGitText;
  86. import org.eclipse.jgit.revwalk.RevBlob;
  87. import org.eclipse.jgit.revwalk.RevCommit;
  88. import org.eclipse.jgit.revwalk.RevObject;
  89. import org.eclipse.jgit.revwalk.RevTree;
  90. import org.eclipse.jgit.revwalk.RevWalk;
  91. import org.eclipse.jgit.transport.RefSpec;
  92. import org.eclipse.jgit.transport.RemoteConfig;
  93. import org.eclipse.jgit.treewalk.TreeWalk;
  94. import org.eclipse.jgit.util.FS;
  95. import org.eclipse.jgit.util.FileUtils;
  96. import org.eclipse.jgit.util.IO;
  97. import org.eclipse.jgit.util.RawParseUtils;
  98. import org.eclipse.jgit.util.SystemReader;
  99. import org.slf4j.Logger;
  100. import org.slf4j.LoggerFactory;

  101. /**
  102.  * Represents a Git repository.
  103.  * <p>
  104.  * A repository holds all objects and refs used for managing source code (could
  105.  * be any type of file, but source code is what SCM's are typically used for).
  106.  * <p>
  107.  * The thread-safety of a {@link org.eclipse.jgit.lib.Repository} very much
  108.  * depends on the concrete implementation. Applications working with a generic
  109.  * {@code Repository} type must not assume the instance is thread-safe.
  110.  * <ul>
  111.  * <li>{@code FileRepository} is thread-safe.
  112.  * <li>{@code DfsRepository} thread-safety is determined by its subclass.
  113.  * </ul>
  114.  */
  115. public abstract class Repository implements AutoCloseable {
  116.     private static final Logger LOG = LoggerFactory.getLogger(Repository.class);
  117.     private static final ListenerList globalListeners = new ListenerList();

  118.     /**
  119.      * Branch names containing slashes should not have a name component that is
  120.      * one of the reserved device names on Windows.
  121.      *
  122.      * @see #normalizeBranchName(String)
  123.      */
  124.     private static final Pattern FORBIDDEN_BRANCH_NAME_COMPONENTS = Pattern
  125.             .compile(
  126.                     "(^|/)(aux|com[1-9]|con|lpt[1-9]|nul|prn)(\\.[^/]*)?", //$NON-NLS-1$
  127.                     Pattern.CASE_INSENSITIVE);

  128.     /**
  129.      * Get the global listener list observing all events in this JVM.
  130.      *
  131.      * @return the global listener list observing all events in this JVM.
  132.      */
  133.     public static ListenerList getGlobalListenerList() {
  134.         return globalListeners;
  135.     }

  136.     /** Use counter */
  137.     final AtomicInteger useCnt = new AtomicInteger(1);

  138.     final AtomicLong closedAt = new AtomicLong();

  139.     /** Metadata directory holding the repository's critical files. */
  140.     private final File gitDir;

  141.     /** File abstraction used to resolve paths. */
  142.     private final FS fs;

  143.     private final ListenerList myListeners = new ListenerList();

  144.     /** If not bare, the top level directory of the working files. */
  145.     private final File workTree;

  146.     /** If not bare, the index file caching the working file states. */
  147.     private final File indexFile;

  148.     /**
  149.      * Initialize a new repository instance.
  150.      *
  151.      * @param options
  152.      *            options to configure the repository.
  153.      */
  154.     protected Repository(BaseRepositoryBuilder options) {
  155.         gitDir = options.getGitDir();
  156.         fs = options.getFS();
  157.         workTree = options.getWorkTree();
  158.         indexFile = options.getIndexFile();
  159.     }

  160.     /**
  161.      * Get listeners observing only events on this repository.
  162.      *
  163.      * @return listeners observing only events on this repository.
  164.      */
  165.     @NonNull
  166.     public ListenerList getListenerList() {
  167.         return myListeners;
  168.     }

  169.     /**
  170.      * Fire an event to all registered listeners.
  171.      * <p>
  172.      * The source repository of the event is automatically set to this
  173.      * repository, before the event is delivered to any listeners.
  174.      *
  175.      * @param event
  176.      *            the event to deliver.
  177.      */
  178.     public void fireEvent(RepositoryEvent<?> event) {
  179.         event.setRepository(this);
  180.         myListeners.dispatch(event);
  181.         globalListeners.dispatch(event);
  182.     }

  183.     /**
  184.      * Create a new Git repository.
  185.      * <p>
  186.      * Repository with working tree is created using this method. This method is
  187.      * the same as {@code create(false)}.
  188.      *
  189.      * @throws java.io.IOException
  190.      * @see #create(boolean)
  191.      */
  192.     public void create() throws IOException {
  193.         create(false);
  194.     }

  195.     /**
  196.      * Create a new Git repository initializing the necessary files and
  197.      * directories.
  198.      *
  199.      * @param bare
  200.      *            if true, a bare repository (a repository without a working
  201.      *            directory) is created.
  202.      * @throws java.io.IOException
  203.      *             in case of IO problem
  204.      */
  205.     public abstract void create(boolean bare) throws IOException;

  206.     /**
  207.      * Get local metadata directory
  208.      *
  209.      * @return local metadata directory; {@code null} if repository isn't local.
  210.      */
  211.     /*
  212.      * TODO This method should be annotated as Nullable, because in some
  213.      * specific configurations metadata is not located in the local file system
  214.      * (for example in memory databases). In "usual" repositories this
  215.      * annotation would only cause compiler errors at places where the actual
  216.      * directory can never be null.
  217.      */
  218.     public File getDirectory() {
  219.         return gitDir;
  220.     }

  221.     /**
  222.      * Get repository identifier.
  223.      *
  224.      * @return repository identifier. The returned identifier has to be unique
  225.      *         within a given Git server.
  226.      * @since 5.4
  227.      */
  228.     public abstract String getIdentifier();

  229.     /**
  230.      * Get the object database which stores this repository's data.
  231.      *
  232.      * @return the object database which stores this repository's data.
  233.      */
  234.     @NonNull
  235.     public abstract ObjectDatabase getObjectDatabase();

  236.     /**
  237.      * Create a new inserter to create objects in {@link #getObjectDatabase()}.
  238.      *
  239.      * @return a new inserter to create objects in {@link #getObjectDatabase()}.
  240.      */
  241.     @NonNull
  242.     public ObjectInserter newObjectInserter() {
  243.         return getObjectDatabase().newInserter();
  244.     }

  245.     /**
  246.      * Create a new reader to read objects from {@link #getObjectDatabase()}.
  247.      *
  248.      * @return a new reader to read objects from {@link #getObjectDatabase()}.
  249.      */
  250.     @NonNull
  251.     public ObjectReader newObjectReader() {
  252.         return getObjectDatabase().newReader();
  253.     }

  254.     /**
  255.      * Get the reference database which stores the reference namespace.
  256.      *
  257.      * @return the reference database which stores the reference namespace.
  258.      */
  259.     @NonNull
  260.     public abstract RefDatabase getRefDatabase();

  261.     /**
  262.      * Get the configuration of this repository.
  263.      *
  264.      * @return the configuration of this repository.
  265.      */
  266.     @NonNull
  267.     public abstract StoredConfig getConfig();

  268.     /**
  269.      * Create a new {@link org.eclipse.jgit.attributes.AttributesNodeProvider}.
  270.      *
  271.      * @return a new {@link org.eclipse.jgit.attributes.AttributesNodeProvider}.
  272.      *         This {@link org.eclipse.jgit.attributes.AttributesNodeProvider}
  273.      *         is lazy loaded only once. It means that it will not be updated
  274.      *         after loading. Prefer creating new instance for each use.
  275.      * @since 4.2
  276.      */
  277.     @NonNull
  278.     public abstract AttributesNodeProvider createAttributesNodeProvider();

  279.     /**
  280.      * Get the used file system abstraction.
  281.      *
  282.      * @return the used file system abstraction, or {@code null} if
  283.      *         repository isn't local.
  284.      */
  285.     /*
  286.      * TODO This method should be annotated as Nullable, because in some
  287.      * specific configurations metadata is not located in the local file system
  288.      * (for example in memory databases). In "usual" repositories this
  289.      * annotation would only cause compiler errors at places where the actual
  290.      * directory can never be null.
  291.      */
  292.     public FS getFS() {
  293.         return fs;
  294.     }

  295.     /**
  296.      * Whether the specified object is stored in this repo or any of the known
  297.      * shared repositories.
  298.      *
  299.      * @param objectId
  300.      *            a {@link org.eclipse.jgit.lib.AnyObjectId} object.
  301.      * @return true if the specified object is stored in this repo or any of the
  302.      *         known shared repositories.
  303.      * @deprecated use {@code getObjectDatabase().has(objectId)}
  304.      */
  305.     @Deprecated
  306.     public boolean hasObject(AnyObjectId objectId) {
  307.         try {
  308.             return getObjectDatabase().has(objectId);
  309.         } catch (IOException e) {
  310.             throw new UncheckedIOException(e);
  311.         }
  312.     }

  313.     /**
  314.      * Open an object from this repository.
  315.      * <p>
  316.      * This is a one-shot call interface which may be faster than allocating a
  317.      * {@link #newObjectReader()} to perform the lookup.
  318.      *
  319.      * @param objectId
  320.      *            identity of the object to open.
  321.      * @return a {@link org.eclipse.jgit.lib.ObjectLoader} for accessing the
  322.      *         object.
  323.      * @throws org.eclipse.jgit.errors.MissingObjectException
  324.      *             the object does not exist.
  325.      * @throws java.io.IOException
  326.      *             the object store cannot be accessed.
  327.      */
  328.     @NonNull
  329.     public ObjectLoader open(AnyObjectId objectId)
  330.             throws MissingObjectException, IOException {
  331.         return getObjectDatabase().open(objectId);
  332.     }

  333.     /**
  334.      * Open an object from this repository.
  335.      * <p>
  336.      * This is a one-shot call interface which may be faster than allocating a
  337.      * {@link #newObjectReader()} to perform the lookup.
  338.      *
  339.      * @param objectId
  340.      *            identity of the object to open.
  341.      * @param typeHint
  342.      *            hint about the type of object being requested, e.g.
  343.      *            {@link org.eclipse.jgit.lib.Constants#OBJ_BLOB};
  344.      *            {@link org.eclipse.jgit.lib.ObjectReader#OBJ_ANY} if the
  345.      *            object type is not known, or does not matter to the caller.
  346.      * @return a {@link org.eclipse.jgit.lib.ObjectLoader} for accessing the
  347.      *         object.
  348.      * @throws org.eclipse.jgit.errors.MissingObjectException
  349.      *             the object does not exist.
  350.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  351.      *             typeHint was not OBJ_ANY, and the object's actual type does
  352.      *             not match typeHint.
  353.      * @throws java.io.IOException
  354.      *             the object store cannot be accessed.
  355.      */
  356.     @NonNull
  357.     public ObjectLoader open(AnyObjectId objectId, int typeHint)
  358.             throws MissingObjectException, IncorrectObjectTypeException,
  359.             IOException {
  360.         return getObjectDatabase().open(objectId, typeHint);
  361.     }

  362.     /**
  363.      * Create a command to update, create or delete a ref in this repository.
  364.      *
  365.      * @param ref
  366.      *            name of the ref the caller wants to modify.
  367.      * @return an update command. The caller must finish populating this command
  368.      *         and then invoke one of the update methods to actually make a
  369.      *         change.
  370.      * @throws java.io.IOException
  371.      *             a symbolic ref was passed in and could not be resolved back
  372.      *             to the base ref, as the symbolic ref could not be read.
  373.      */
  374.     @NonNull
  375.     public RefUpdate updateRef(String ref) throws IOException {
  376.         return updateRef(ref, false);
  377.     }

  378.     /**
  379.      * Create a command to update, create or delete a ref in this repository.
  380.      *
  381.      * @param ref
  382.      *            name of the ref the caller wants to modify.
  383.      * @param detach
  384.      *            true to create a detached head
  385.      * @return an update command. The caller must finish populating this command
  386.      *         and then invoke one of the update methods to actually make a
  387.      *         change.
  388.      * @throws java.io.IOException
  389.      *             a symbolic ref was passed in and could not be resolved back
  390.      *             to the base ref, as the symbolic ref could not be read.
  391.      */
  392.     @NonNull
  393.     public RefUpdate updateRef(String ref, boolean detach) throws IOException {
  394.         return getRefDatabase().newUpdate(ref, detach);
  395.     }

  396.     /**
  397.      * Create a command to rename a ref in this repository
  398.      *
  399.      * @param fromRef
  400.      *            name of ref to rename from
  401.      * @param toRef
  402.      *            name of ref to rename to
  403.      * @return an update command that knows how to rename a branch to another.
  404.      * @throws java.io.IOException
  405.      *             the rename could not be performed.
  406.      */
  407.     @NonNull
  408.     public RefRename renameRef(String fromRef, String toRef) throws IOException {
  409.         return getRefDatabase().newRename(fromRef, toRef);
  410.     }

  411.     /**
  412.      * Parse a git revision string and return an object id.
  413.      *
  414.      * Combinations of these operators are supported:
  415.      * <ul>
  416.      * <li><b>HEAD</b>, <b>MERGE_HEAD</b>, <b>FETCH_HEAD</b></li>
  417.      * <li><b>SHA-1</b>: a complete or abbreviated SHA-1</li>
  418.      * <li><b>refs/...</b>: a complete reference name</li>
  419.      * <li><b>short-name</b>: a short reference name under {@code refs/heads},
  420.      * {@code refs/tags}, or {@code refs/remotes} namespace</li>
  421.      * <li><b>tag-NN-gABBREV</b>: output from describe, parsed by treating
  422.      * {@code ABBREV} as an abbreviated SHA-1.</li>
  423.      * <li><i>id</i><b>^</b>: first parent of commit <i>id</i>, this is the same
  424.      * as {@code id^1}</li>
  425.      * <li><i>id</i><b>^0</b>: ensure <i>id</i> is a commit</li>
  426.      * <li><i>id</i><b>^n</b>: n-th parent of commit <i>id</i></li>
  427.      * <li><i>id</i><b>~n</b>: n-th historical ancestor of <i>id</i>, by first
  428.      * parent. {@code id~3} is equivalent to {@code id^1^1^1} or {@code id^^^}.</li>
  429.      * <li><i>id</i><b>:path</b>: Lookup path under tree named by <i>id</i></li>
  430.      * <li><i>id</i><b>^{commit}</b>: ensure <i>id</i> is a commit</li>
  431.      * <li><i>id</i><b>^{tree}</b>: ensure <i>id</i> is a tree</li>
  432.      * <li><i>id</i><b>^{tag}</b>: ensure <i>id</i> is a tag</li>
  433.      * <li><i>id</i><b>^{blob}</b>: ensure <i>id</i> is a blob</li>
  434.      * </ul>
  435.      *
  436.      * <p>
  437.      * The following operators are specified by Git conventions, but are not
  438.      * supported by this method:
  439.      * <ul>
  440.      * <li><b>ref@{n}</b>: n-th version of ref as given by its reflog</li>
  441.      * <li><b>ref@{time}</b>: value of ref at the designated time</li>
  442.      * </ul>
  443.      *
  444.      * @param revstr
  445.      *            A git object references expression
  446.      * @return an ObjectId or {@code null} if revstr can't be resolved to any
  447.      *         ObjectId
  448.      * @throws org.eclipse.jgit.errors.AmbiguousObjectException
  449.      *             {@code revstr} contains an abbreviated ObjectId and this
  450.      *             repository contains more than one object which match to the
  451.      *             input abbreviation.
  452.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  453.      *             the id parsed does not meet the type required to finish
  454.      *             applying the operators in the expression.
  455.      * @throws org.eclipse.jgit.errors.RevisionSyntaxException
  456.      *             the expression is not supported by this implementation, or
  457.      *             does not meet the standard syntax.
  458.      * @throws java.io.IOException
  459.      *             on serious errors
  460.      */
  461.     @Nullable
  462.     public ObjectId resolve(String revstr)
  463.             throws AmbiguousObjectException, IncorrectObjectTypeException,
  464.             RevisionSyntaxException, IOException {
  465.         try (RevWalk rw = new RevWalk(this)) {
  466.             rw.setRetainBody(false);
  467.             Object resolved = resolve(rw, revstr);
  468.             if (resolved instanceof String) {
  469.                 final Ref ref = findRef((String) resolved);
  470.                 return ref != null ? ref.getLeaf().getObjectId() : null;
  471.             } else {
  472.                 return (ObjectId) resolved;
  473.             }
  474.         }
  475.     }

  476.     /**
  477.      * Simplify an expression, but unlike {@link #resolve(String)} it will not
  478.      * resolve a branch passed or resulting from the expression, such as @{-}.
  479.      * Thus this method can be used to process an expression to a method that
  480.      * expects a branch or revision id.
  481.      *
  482.      * @param revstr a {@link java.lang.String} object.
  483.      * @return object id or ref name from resolved expression or {@code null} if
  484.      *         given expression cannot be resolved
  485.      * @throws org.eclipse.jgit.errors.AmbiguousObjectException
  486.      * @throws java.io.IOException
  487.      */
  488.     @Nullable
  489.     public String simplify(String revstr)
  490.             throws AmbiguousObjectException, IOException {
  491.         try (RevWalk rw = new RevWalk(this)) {
  492.             rw.setRetainBody(true);
  493.             Object resolved = resolve(rw, revstr);
  494.             if (resolved != null)
  495.                 if (resolved instanceof String)
  496.                     return (String) resolved;
  497.                 else
  498.                     return ((AnyObjectId) resolved).getName();
  499.             return null;
  500.         }
  501.     }

  502.     @Nullable
  503.     private Object resolve(RevWalk rw, String revstr)
  504.             throws IOException {
  505.         char[] revChars = revstr.toCharArray();
  506.         RevObject rev = null;
  507.         String name = null;
  508.         int done = 0;
  509.         for (int i = 0; i < revChars.length; ++i) {
  510.             switch (revChars[i]) {
  511.             case '^':
  512.                 if (rev == null) {
  513.                     if (name == null)
  514.                         if (done == 0)
  515.                             name = new String(revChars, done, i);
  516.                         else {
  517.                             done = i + 1;
  518.                             break;
  519.                         }
  520.                     rev = parseSimple(rw, name);
  521.                     name = null;
  522.                     if (rev == null)
  523.                         return null;
  524.                 }
  525.                 if (i + 1 < revChars.length) {
  526.                     switch (revChars[i + 1]) {
  527.                     case '0':
  528.                     case '1':
  529.                     case '2':
  530.                     case '3':
  531.                     case '4':
  532.                     case '5':
  533.                     case '6':
  534.                     case '7':
  535.                     case '8':
  536.                     case '9':
  537.                         int j;
  538.                         rev = rw.parseCommit(rev);
  539.                         for (j = i + 1; j < revChars.length; ++j) {
  540.                             if (!Character.isDigit(revChars[j]))
  541.                                 break;
  542.                         }
  543.                         String parentnum = new String(revChars, i + 1, j - i
  544.                                 - 1);
  545.                         int pnum;
  546.                         try {
  547.                             pnum = Integer.parseInt(parentnum);
  548.                         } catch (NumberFormatException e) {
  549.                             throw new RevisionSyntaxException(
  550.                                     JGitText.get().invalidCommitParentNumber,
  551.                                     revstr);
  552.                         }
  553.                         if (pnum != 0) {
  554.                             RevCommit commit = (RevCommit) rev;
  555.                             if (pnum > commit.getParentCount())
  556.                                 rev = null;
  557.                             else
  558.                                 rev = commit.getParent(pnum - 1);
  559.                         }
  560.                         i = j - 1;
  561.                         done = j;
  562.                         break;
  563.                     case '{':
  564.                         int k;
  565.                         String item = null;
  566.                         for (k = i + 2; k < revChars.length; ++k) {
  567.                             if (revChars[k] == '}') {
  568.                                 item = new String(revChars, i + 2, k - i - 2);
  569.                                 break;
  570.                             }
  571.                         }
  572.                         i = k;
  573.                         if (item != null)
  574.                             if (item.equals("tree")) { //$NON-NLS-1$
  575.                                 rev = rw.parseTree(rev);
  576.                             } else if (item.equals("commit")) { //$NON-NLS-1$
  577.                                 rev = rw.parseCommit(rev);
  578.                             } else if (item.equals("blob")) { //$NON-NLS-1$
  579.                                 rev = rw.peel(rev);
  580.                                 if (!(rev instanceof RevBlob))
  581.                                     throw new IncorrectObjectTypeException(rev,
  582.                                             Constants.TYPE_BLOB);
  583.                             } else if (item.isEmpty()) {
  584.                                 rev = rw.peel(rev);
  585.                             } else
  586.                                 throw new RevisionSyntaxException(revstr);
  587.                         else
  588.                             throw new RevisionSyntaxException(revstr);
  589.                         done = k;
  590.                         break;
  591.                     default:
  592.                         rev = rw.peel(rev);
  593.                         if (rev instanceof RevCommit) {
  594.                             RevCommit commit = ((RevCommit) rev);
  595.                             if (commit.getParentCount() == 0)
  596.                                 rev = null;
  597.                             else
  598.                                 rev = commit.getParent(0);
  599.                         } else
  600.                             throw new IncorrectObjectTypeException(rev,
  601.                                     Constants.TYPE_COMMIT);
  602.                     }
  603.                 } else {
  604.                     rev = rw.peel(rev);
  605.                     if (rev instanceof RevCommit) {
  606.                         RevCommit commit = ((RevCommit) rev);
  607.                         if (commit.getParentCount() == 0)
  608.                             rev = null;
  609.                         else
  610.                             rev = commit.getParent(0);
  611.                     } else
  612.                         throw new IncorrectObjectTypeException(rev,
  613.                                 Constants.TYPE_COMMIT);
  614.                 }
  615.                 done = i + 1;
  616.                 break;
  617.             case '~':
  618.                 if (rev == null) {
  619.                     if (name == null)
  620.                         if (done == 0)
  621.                             name = new String(revChars, done, i);
  622.                         else {
  623.                             done = i + 1;
  624.                             break;
  625.                         }
  626.                     rev = parseSimple(rw, name);
  627.                     name = null;
  628.                     if (rev == null)
  629.                         return null;
  630.                 }
  631.                 rev = rw.peel(rev);
  632.                 if (!(rev instanceof RevCommit))
  633.                     throw new IncorrectObjectTypeException(rev,
  634.                             Constants.TYPE_COMMIT);
  635.                 int l;
  636.                 for (l = i + 1; l < revChars.length; ++l) {
  637.                     if (!Character.isDigit(revChars[l]))
  638.                         break;
  639.                 }
  640.                 int dist;
  641.                 if (l - i > 1) {
  642.                     String distnum = new String(revChars, i + 1, l - i - 1);
  643.                     try {
  644.                         dist = Integer.parseInt(distnum);
  645.                     } catch (NumberFormatException e) {
  646.                         throw new RevisionSyntaxException(
  647.                                 JGitText.get().invalidAncestryLength, revstr);
  648.                     }
  649.                 } else
  650.                     dist = 1;
  651.                 while (dist > 0) {
  652.                     RevCommit commit = (RevCommit) rev;
  653.                     if (commit.getParentCount() == 0) {
  654.                         rev = null;
  655.                         break;
  656.                     }
  657.                     commit = commit.getParent(0);
  658.                     rw.parseHeaders(commit);
  659.                     rev = commit;
  660.                     --dist;
  661.                 }
  662.                 i = l - 1;
  663.                 done = l;
  664.                 break;
  665.             case '@':
  666.                 if (rev != null)
  667.                     throw new RevisionSyntaxException(revstr);
  668.                 if (i + 1 == revChars.length)
  669.                     continue;
  670.                 if (i + 1 < revChars.length && revChars[i + 1] != '{')
  671.                     continue;
  672.                 int m;
  673.                 String time = null;
  674.                 for (m = i + 2; m < revChars.length; ++m) {
  675.                     if (revChars[m] == '}') {
  676.                         time = new String(revChars, i + 2, m - i - 2);
  677.                         break;
  678.                     }
  679.                 }
  680.                 if (time != null) {
  681.                     if (time.equals("upstream")) { //$NON-NLS-1$
  682.                         if (name == null)
  683.                             name = new String(revChars, done, i);
  684.                         if (name.isEmpty())
  685.                             // Currently checked out branch, HEAD if
  686.                             // detached
  687.                             name = Constants.HEAD;
  688.                         if (!Repository.isValidRefName("x/" + name)) //$NON-NLS-1$
  689.                             throw new RevisionSyntaxException(MessageFormat
  690.                                     .format(JGitText.get().invalidRefName,
  691.                                             name),
  692.                                     revstr);
  693.                         Ref ref = findRef(name);
  694.                         name = null;
  695.                         if (ref == null)
  696.                             return null;
  697.                         if (ref.isSymbolic())
  698.                             ref = ref.getLeaf();
  699.                         name = ref.getName();

  700.                         RemoteConfig remoteConfig;
  701.                         try {
  702.                             remoteConfig = new RemoteConfig(getConfig(),
  703.                                     "origin"); //$NON-NLS-1$
  704.                         } catch (URISyntaxException e) {
  705.                             throw new RevisionSyntaxException(revstr);
  706.                         }
  707.                         String remoteBranchName = getConfig()
  708.                                 .getString(
  709.                                         ConfigConstants.CONFIG_BRANCH_SECTION,
  710.                                 Repository.shortenRefName(ref.getName()),
  711.                                         ConfigConstants.CONFIG_KEY_MERGE);
  712.                         List<RefSpec> fetchRefSpecs = remoteConfig
  713.                                 .getFetchRefSpecs();
  714.                         for (RefSpec refSpec : fetchRefSpecs) {
  715.                             if (refSpec.matchSource(remoteBranchName)) {
  716.                                 RefSpec expandFromSource = refSpec
  717.                                         .expandFromSource(remoteBranchName);
  718.                                 name = expandFromSource.getDestination();
  719.                                 break;
  720.                             }
  721.                         }
  722.                         if (name == null)
  723.                             throw new RevisionSyntaxException(revstr);
  724.                     } else if (time.matches("^-\\d+$")) { //$NON-NLS-1$
  725.                         if (name != null)
  726.                             throw new RevisionSyntaxException(revstr);
  727.                         else {
  728.                             String previousCheckout = resolveReflogCheckout(-Integer
  729.                                     .parseInt(time));
  730.                             if (ObjectId.isId(previousCheckout))
  731.                                 rev = parseSimple(rw, previousCheckout);
  732.                             else
  733.                                 name = previousCheckout;
  734.                         }
  735.                     } else {
  736.                         if (name == null)
  737.                             name = new String(revChars, done, i);
  738.                         if (name.isEmpty())
  739.                             name = Constants.HEAD;
  740.                         if (!Repository.isValidRefName("x/" + name)) //$NON-NLS-1$
  741.                             throw new RevisionSyntaxException(MessageFormat
  742.                                     .format(JGitText.get().invalidRefName,
  743.                                             name),
  744.                                     revstr);
  745.                         Ref ref = findRef(name);
  746.                         name = null;
  747.                         if (ref == null)
  748.                             return null;
  749.                         // @{n} means current branch, not HEAD@{1} unless
  750.                         // detached
  751.                         if (ref.isSymbolic())
  752.                             ref = ref.getLeaf();
  753.                         rev = resolveReflog(rw, ref, time);
  754.                     }
  755.                     i = m;
  756.                 } else
  757.                     throw new RevisionSyntaxException(revstr);
  758.                 break;
  759.             case ':': {
  760.                 RevTree tree;
  761.                 if (rev == null) {
  762.                     if (name == null)
  763.                         name = new String(revChars, done, i);
  764.                     if (name.isEmpty())
  765.                         name = Constants.HEAD;
  766.                     rev = parseSimple(rw, name);
  767.                     name = null;
  768.                 }
  769.                 if (rev == null)
  770.                     return null;
  771.                 tree = rw.parseTree(rev);
  772.                 if (i == revChars.length - 1)
  773.                     return tree.copy();

  774.                 TreeWalk tw = TreeWalk.forPath(rw.getObjectReader(),
  775.                         new String(revChars, i + 1, revChars.length - i - 1),
  776.                         tree);
  777.                 return tw != null ? tw.getObjectId(0) : null;
  778.             }
  779.             default:
  780.                 if (rev != null)
  781.                     throw new RevisionSyntaxException(revstr);
  782.             }
  783.         }
  784.         if (rev != null)
  785.             return rev.copy();
  786.         if (name != null)
  787.             return name;
  788.         if (done == revstr.length())
  789.             return null;
  790.         name = revstr.substring(done);
  791.         if (!Repository.isValidRefName("x/" + name)) //$NON-NLS-1$
  792.             throw new RevisionSyntaxException(
  793.                     MessageFormat.format(JGitText.get().invalidRefName, name),
  794.                     revstr);
  795.         if (findRef(name) != null)
  796.             return name;
  797.         return resolveSimple(name);
  798.     }

  799.     private static boolean isHex(char c) {
  800.         return ('0' <= c && c <= '9') //
  801.                 || ('a' <= c && c <= 'f') //
  802.                 || ('A' <= c && c <= 'F');
  803.     }

  804.     private static boolean isAllHex(String str, int ptr) {
  805.         while (ptr < str.length()) {
  806.             if (!isHex(str.charAt(ptr++)))
  807.                 return false;
  808.         }
  809.         return true;
  810.     }

  811.     @Nullable
  812.     private RevObject parseSimple(RevWalk rw, String revstr) throws IOException {
  813.         ObjectId id = resolveSimple(revstr);
  814.         return id != null ? rw.parseAny(id) : null;
  815.     }

  816.     @Nullable
  817.     private ObjectId resolveSimple(String revstr) throws IOException {
  818.         if (ObjectId.isId(revstr))
  819.             return ObjectId.fromString(revstr);

  820.         if (Repository.isValidRefName("x/" + revstr)) { //$NON-NLS-1$
  821.             Ref r = getRefDatabase().findRef(revstr);
  822.             if (r != null)
  823.                 return r.getObjectId();
  824.         }

  825.         if (AbbreviatedObjectId.isId(revstr))
  826.             return resolveAbbreviation(revstr);

  827.         int dashg = revstr.indexOf("-g"); //$NON-NLS-1$
  828.         if ((dashg + 5) < revstr.length() && 0 <= dashg
  829.                 && isHex(revstr.charAt(dashg + 2))
  830.                 && isHex(revstr.charAt(dashg + 3))
  831.                 && isAllHex(revstr, dashg + 4)) {
  832.             // Possibly output from git describe?
  833.             String s = revstr.substring(dashg + 2);
  834.             if (AbbreviatedObjectId.isId(s))
  835.                 return resolveAbbreviation(s);
  836.         }

  837.         return null;
  838.     }

  839.     @Nullable
  840.     private String resolveReflogCheckout(int checkoutNo)
  841.             throws IOException {
  842.         ReflogReader reader = getReflogReader(Constants.HEAD);
  843.         if (reader == null) {
  844.             return null;
  845.         }
  846.         List<ReflogEntry> reflogEntries = reader.getReverseEntries();
  847.         for (ReflogEntry entry : reflogEntries) {
  848.             CheckoutEntry checkout = entry.parseCheckout();
  849.             if (checkout != null)
  850.                 if (checkoutNo-- == 1)
  851.                     return checkout.getFromBranch();
  852.         }
  853.         return null;
  854.     }

  855.     private RevCommit resolveReflog(RevWalk rw, Ref ref, String time)
  856.             throws IOException {
  857.         int number;
  858.         try {
  859.             number = Integer.parseInt(time);
  860.         } catch (NumberFormatException nfe) {
  861.             throw new RevisionSyntaxException(MessageFormat.format(
  862.                     JGitText.get().invalidReflogRevision, time));
  863.         }
  864.         assert number >= 0;
  865.         ReflogReader reader = getReflogReader(ref.getName());
  866.         if (reader == null) {
  867.             throw new RevisionSyntaxException(
  868.                     MessageFormat.format(JGitText.get().reflogEntryNotFound,
  869.                             Integer.valueOf(number), ref.getName()));
  870.         }
  871.         ReflogEntry entry = reader.getReverseEntry(number);
  872.         if (entry == null)
  873.             throw new RevisionSyntaxException(MessageFormat.format(
  874.                     JGitText.get().reflogEntryNotFound,
  875.                     Integer.valueOf(number), ref.getName()));

  876.         return rw.parseCommit(entry.getNewId());
  877.     }

  878.     @Nullable
  879.     private ObjectId resolveAbbreviation(String revstr) throws IOException,
  880.             AmbiguousObjectException {
  881.         AbbreviatedObjectId id = AbbreviatedObjectId.fromString(revstr);
  882.         try (ObjectReader reader = newObjectReader()) {
  883.             Collection<ObjectId> matches = reader.resolve(id);
  884.             if (matches.isEmpty())
  885.                 return null;
  886.             else if (matches.size() == 1)
  887.                 return matches.iterator().next();
  888.             else
  889.                 throw new AmbiguousObjectException(id, matches);
  890.         }
  891.     }

  892.     /**
  893.      * Increment the use counter by one, requiring a matched {@link #close()}.
  894.      */
  895.     public void incrementOpen() {
  896.         useCnt.incrementAndGet();
  897.     }

  898.     /**
  899.      * {@inheritDoc}
  900.      * <p>
  901.      * Decrement the use count, and maybe close resources.
  902.      */
  903.     @Override
  904.     public void close() {
  905.         int newCount = useCnt.decrementAndGet();
  906.         if (newCount == 0) {
  907.             if (RepositoryCache.isCached(this)) {
  908.                 closedAt.set(System.currentTimeMillis());
  909.             } else {
  910.                 doClose();
  911.             }
  912.         } else if (newCount == -1) {
  913.             // should not happen, only log when useCnt became negative to
  914.             // minimize number of log entries
  915.             String message = MessageFormat.format(JGitText.get().corruptUseCnt,
  916.                     toString());
  917.             if (LOG.isDebugEnabled()) {
  918.                 LOG.debug(message, new IllegalStateException());
  919.             } else {
  920.                 LOG.warn(message);
  921.             }
  922.             if (RepositoryCache.isCached(this)) {
  923.                 closedAt.set(System.currentTimeMillis());
  924.             }
  925.         }
  926.     }

  927.     /**
  928.      * Invoked when the use count drops to zero during {@link #close()}.
  929.      * <p>
  930.      * The default implementation closes the object and ref databases.
  931.      */
  932.     protected void doClose() {
  933.         getObjectDatabase().close();
  934.         getRefDatabase().close();
  935.     }

  936.     /** {@inheritDoc} */
  937.     @Override
  938.     @NonNull
  939.     public String toString() {
  940.         String desc;
  941.         File directory = getDirectory();
  942.         if (directory != null)
  943.             desc = directory.getPath();
  944.         else
  945.             desc = getClass().getSimpleName() + "-" //$NON-NLS-1$
  946.                     + System.identityHashCode(this);
  947.         return "Repository[" + desc + "]"; //$NON-NLS-1$ //$NON-NLS-2$
  948.     }

  949.     /**
  950.      * Get the name of the reference that {@code HEAD} points to.
  951.      * <p>
  952.      * This is essentially the same as doing:
  953.      *
  954.      * <pre>
  955.      * return exactRef(Constants.HEAD).getTarget().getName()
  956.      * </pre>
  957.      *
  958.      * Except when HEAD is detached, in which case this method returns the
  959.      * current ObjectId in hexadecimal string format.
  960.      *
  961.      * @return name of current branch (for example {@code refs/heads/master}),
  962.      *         an ObjectId in hex format if the current branch is detached, or
  963.      *         {@code null} if the repository is corrupt and has no HEAD
  964.      *         reference.
  965.      * @throws java.io.IOException
  966.      */
  967.     @Nullable
  968.     public String getFullBranch() throws IOException {
  969.         Ref head = exactRef(Constants.HEAD);
  970.         if (head == null) {
  971.             return null;
  972.         }
  973.         if (head.isSymbolic()) {
  974.             return head.getTarget().getName();
  975.         }
  976.         ObjectId objectId = head.getObjectId();
  977.         if (objectId != null) {
  978.             return objectId.name();
  979.         }
  980.         return null;
  981.     }

  982.     /**
  983.      * Get the short name of the current branch that {@code HEAD} points to.
  984.      * <p>
  985.      * This is essentially the same as {@link #getFullBranch()}, except the
  986.      * leading prefix {@code refs/heads/} is removed from the reference before
  987.      * it is returned to the caller.
  988.      *
  989.      * @return name of current branch (for example {@code master}), an ObjectId
  990.      *         in hex format if the current branch is detached, or {@code null}
  991.      *         if the repository is corrupt and has no HEAD reference.
  992.      * @throws java.io.IOException
  993.      */
  994.     @Nullable
  995.     public String getBranch() throws IOException {
  996.         String name = getFullBranch();
  997.         if (name != null)
  998.             return shortenRefName(name);
  999.         return null;
  1000.     }

  1001.     /**
  1002.      * Objects known to exist but not expressed by {@link #getAllRefs()}.
  1003.      * <p>
  1004.      * When a repository borrows objects from another repository, it can
  1005.      * advertise that it safely has that other repository's references, without
  1006.      * exposing any other details about the other repository.  This may help
  1007.      * a client trying to push changes avoid pushing more than it needs to.
  1008.      *
  1009.      * @return unmodifiable collection of other known objects.
  1010.      */
  1011.     @NonNull
  1012.     public Set<ObjectId> getAdditionalHaves() {
  1013.         return Collections.emptySet();
  1014.     }

  1015.     /**
  1016.      * Get a ref by name.
  1017.      *
  1018.      * @param name
  1019.      *            the name of the ref to lookup. Must not be a short-hand
  1020.      *            form; e.g., "master" is not automatically expanded to
  1021.      *            "refs/heads/master".
  1022.      * @return the Ref with the given name, or {@code null} if it does not exist
  1023.      * @throws java.io.IOException
  1024.      * @since 4.2
  1025.      */
  1026.     @Nullable
  1027.     public final Ref exactRef(String name) throws IOException {
  1028.         return getRefDatabase().exactRef(name);
  1029.     }

  1030.     /**
  1031.      * Search for a ref by (possibly abbreviated) name.
  1032.      *
  1033.      * @param name
  1034.      *            the name of the ref to lookup. May be a short-hand form, e.g.
  1035.      *            "master" which is automatically expanded to
  1036.      *            "refs/heads/master" if "refs/heads/master" already exists.
  1037.      * @return the Ref with the given name, or {@code null} if it does not exist
  1038.      * @throws java.io.IOException
  1039.      * @since 4.2
  1040.      */
  1041.     @Nullable
  1042.     public final Ref findRef(String name) throws IOException {
  1043.         return getRefDatabase().findRef(name);
  1044.     }

  1045.     /**
  1046.      * Get mutable map of all known refs, including symrefs like HEAD that may
  1047.      * not point to any object yet.
  1048.      *
  1049.      * @return mutable map of all known refs (heads, tags, remotes).
  1050.      * @deprecated use {@code getRefDatabase().getRefs()} instead.
  1051.      */
  1052.     @Deprecated
  1053.     @NonNull
  1054.     public Map<String, Ref> getAllRefs() {
  1055.         try {
  1056.             return getRefDatabase().getRefs(RefDatabase.ALL);
  1057.         } catch (IOException e) {
  1058.             throw new UncheckedIOException(e);
  1059.         }
  1060.     }

  1061.     /**
  1062.      * Get mutable map of all tags
  1063.      *
  1064.      * @return mutable map of all tags; key is short tag name ("v1.0") and value
  1065.      *         of the entry contains the ref with the full tag name
  1066.      *         ("refs/tags/v1.0").
  1067.      * @deprecated use {@code getRefDatabase().getRefsByPrefix(R_TAGS)} instead
  1068.      */
  1069.     @Deprecated
  1070.     @NonNull
  1071.     public Map<String, Ref> getTags() {
  1072.         try {
  1073.             return getRefDatabase().getRefs(Constants.R_TAGS);
  1074.         } catch (IOException e) {
  1075.             throw new UncheckedIOException(e);
  1076.         }
  1077.     }

  1078.     /**
  1079.      * Peel a possibly unpeeled reference to an annotated tag.
  1080.      * <p>
  1081.      * If the ref cannot be peeled (as it does not refer to an annotated tag)
  1082.      * the peeled id stays null, but {@link org.eclipse.jgit.lib.Ref#isPeeled()}
  1083.      * will be true.
  1084.      *
  1085.      * @param ref
  1086.      *            The ref to peel
  1087.      * @return <code>ref</code> if <code>ref.isPeeled()</code> is true; else a
  1088.      *         new Ref object representing the same data as Ref, but isPeeled()
  1089.      *         will be true and getPeeledObjectId will contain the peeled object
  1090.      *         (or null).
  1091.      * @deprecated use {@code getRefDatabase().peel(ref)} instead.
  1092.      */
  1093.     @Deprecated
  1094.     @NonNull
  1095.     public Ref peel(Ref ref) {
  1096.         try {
  1097.             return getRefDatabase().peel(ref);
  1098.         } catch (IOException e) {
  1099.             // Historical accident; if the reference cannot be peeled due
  1100.             // to some sort of repository access problem we claim that the
  1101.             // same as if the reference was not an annotated tag.
  1102.             return ref;
  1103.         }
  1104.     }

  1105.     /**
  1106.      * Get a map with all objects referenced by a peeled ref.
  1107.      *
  1108.      * @return a map with all objects referenced by a peeled ref.
  1109.      */
  1110.     @NonNull
  1111.     public Map<AnyObjectId, Set<Ref>> getAllRefsByPeeledObjectId() {
  1112.         Map<String, Ref> allRefs = getAllRefs();
  1113.         Map<AnyObjectId, Set<Ref>> ret = new HashMap<>(allRefs.size());
  1114.         for (Ref ref : allRefs.values()) {
  1115.             ref = peel(ref);
  1116.             AnyObjectId target = ref.getPeeledObjectId();
  1117.             if (target == null)
  1118.                 target = ref.getObjectId();
  1119.             // We assume most Sets here are singletons
  1120.             Set<Ref> oset = ret.put(target, Collections.singleton(ref));
  1121.             if (oset != null) {
  1122.                 // that was not the case (rare)
  1123.                 if (oset.size() == 1) {
  1124.                     // Was a read-only singleton, we must copy to a new Set
  1125.                     oset = new HashSet<>(oset);
  1126.                 }
  1127.                 ret.put(target, oset);
  1128.                 oset.add(ref);
  1129.             }
  1130.         }
  1131.         return ret;
  1132.     }

  1133.     /**
  1134.      * Get the index file location or {@code null} if repository isn't local.
  1135.      *
  1136.      * @return the index file location or {@code null} if repository isn't
  1137.      *         local.
  1138.      * @throws org.eclipse.jgit.errors.NoWorkTreeException
  1139.      *             if this is bare, which implies it has no working directory.
  1140.      *             See {@link #isBare()}.
  1141.      */
  1142.     @NonNull
  1143.     public File getIndexFile() throws NoWorkTreeException {
  1144.         if (isBare())
  1145.             throw new NoWorkTreeException();
  1146.         return indexFile;
  1147.     }

  1148.     /**
  1149.      * Locate a reference to a commit and immediately parse its content.
  1150.      * <p>
  1151.      * This method only returns successfully if the commit object exists,
  1152.      * is verified to be a commit, and was parsed without error.
  1153.      *
  1154.      * @param id
  1155.      *            name of the commit object.
  1156.      * @return reference to the commit object. Never null.
  1157.      * @throws org.eclipse.jgit.errors.MissingObjectException
  1158.      *             the supplied commit does not exist.
  1159.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  1160.      *             the supplied id is not a commit or an annotated tag.
  1161.      * @throws java.io.IOException
  1162.      *             a pack file or loose object could not be read.
  1163.      * @since 4.8
  1164.      */
  1165.     public RevCommit parseCommit(AnyObjectId id) throws IncorrectObjectTypeException,
  1166.             IOException, MissingObjectException {
  1167.         if (id instanceof RevCommit && ((RevCommit) id).getRawBuffer() != null) {
  1168.             return (RevCommit) id;
  1169.         }
  1170.         try (RevWalk walk = new RevWalk(this)) {
  1171.             return walk.parseCommit(id);
  1172.         }
  1173.     }

  1174.     /**
  1175.      * Create a new in-core index representation and read an index from disk.
  1176.      * <p>
  1177.      * The new index will be read before it is returned to the caller. Read
  1178.      * failures are reported as exceptions and therefore prevent the method from
  1179.      * returning a partially populated index.
  1180.      *
  1181.      * @return a cache representing the contents of the specified index file (if
  1182.      *         it exists) or an empty cache if the file does not exist.
  1183.      * @throws org.eclipse.jgit.errors.NoWorkTreeException
  1184.      *             if this is bare, which implies it has no working directory.
  1185.      *             See {@link #isBare()}.
  1186.      * @throws java.io.IOException
  1187.      *             the index file is present but could not be read.
  1188.      * @throws org.eclipse.jgit.errors.CorruptObjectException
  1189.      *             the index file is using a format or extension that this
  1190.      *             library does not support.
  1191.      */
  1192.     @NonNull
  1193.     public DirCache readDirCache() throws NoWorkTreeException,
  1194.             CorruptObjectException, IOException {
  1195.         return DirCache.read(this);
  1196.     }

  1197.     /**
  1198.      * Create a new in-core index representation, lock it, and read from disk.
  1199.      * <p>
  1200.      * The new index will be locked and then read before it is returned to the
  1201.      * caller. Read failures are reported as exceptions and therefore prevent
  1202.      * the method from returning a partially populated index.
  1203.      *
  1204.      * @return a cache representing the contents of the specified index file (if
  1205.      *         it exists) or an empty cache if the file does not exist.
  1206.      * @throws org.eclipse.jgit.errors.NoWorkTreeException
  1207.      *             if this is bare, which implies it has no working directory.
  1208.      *             See {@link #isBare()}.
  1209.      * @throws java.io.IOException
  1210.      *             the index file is present but could not be read, or the lock
  1211.      *             could not be obtained.
  1212.      * @throws org.eclipse.jgit.errors.CorruptObjectException
  1213.      *             the index file is using a format or extension that this
  1214.      *             library does not support.
  1215.      */
  1216.     @NonNull
  1217.     public DirCache lockDirCache() throws NoWorkTreeException,
  1218.             CorruptObjectException, IOException {
  1219.         // we want DirCache to inform us so that we can inform registered
  1220.         // listeners about index changes
  1221.         IndexChangedListener l = (IndexChangedEvent event) -> {
  1222.             notifyIndexChanged(true);
  1223.         };
  1224.         return DirCache.lock(this, l);
  1225.     }

  1226.     /**
  1227.      * Get the repository state
  1228.      *
  1229.      * @return the repository state
  1230.      */
  1231.     @NonNull
  1232.     public RepositoryState getRepositoryState() {
  1233.         if (isBare() || getDirectory() == null)
  1234.             return RepositoryState.BARE;

  1235.         // Pre Git-1.6 logic
  1236.         if (new File(getWorkTree(), ".dotest").exists()) //$NON-NLS-1$
  1237.             return RepositoryState.REBASING;
  1238.         if (new File(getDirectory(), ".dotest-merge").exists()) //$NON-NLS-1$
  1239.             return RepositoryState.REBASING_INTERACTIVE;

  1240.         // From 1.6 onwards
  1241.         if (new File(getDirectory(),"rebase-apply/rebasing").exists()) //$NON-NLS-1$
  1242.             return RepositoryState.REBASING_REBASING;
  1243.         if (new File(getDirectory(),"rebase-apply/applying").exists()) //$NON-NLS-1$
  1244.             return RepositoryState.APPLY;
  1245.         if (new File(getDirectory(),"rebase-apply").exists()) //$NON-NLS-1$
  1246.             return RepositoryState.REBASING;

  1247.         if (new File(getDirectory(),"rebase-merge/interactive").exists()) //$NON-NLS-1$
  1248.             return RepositoryState.REBASING_INTERACTIVE;
  1249.         if (new File(getDirectory(),"rebase-merge").exists()) //$NON-NLS-1$
  1250.             return RepositoryState.REBASING_MERGE;

  1251.         // Both versions
  1252.         if (new File(getDirectory(), Constants.MERGE_HEAD).exists()) {
  1253.             // we are merging - now check whether we have unmerged paths
  1254.             try {
  1255.                 if (!readDirCache().hasUnmergedPaths()) {
  1256.                     // no unmerged paths -> return the MERGING_RESOLVED state
  1257.                     return RepositoryState.MERGING_RESOLVED;
  1258.                 }
  1259.             } catch (IOException e) {
  1260.                 throw new UncheckedIOException(e);
  1261.             }
  1262.             return RepositoryState.MERGING;
  1263.         }

  1264.         if (new File(getDirectory(), "BISECT_LOG").exists()) //$NON-NLS-1$
  1265.             return RepositoryState.BISECTING;

  1266.         if (new File(getDirectory(), Constants.CHERRY_PICK_HEAD).exists()) {
  1267.             try {
  1268.                 if (!readDirCache().hasUnmergedPaths()) {
  1269.                     // no unmerged paths
  1270.                     return RepositoryState.CHERRY_PICKING_RESOLVED;
  1271.                 }
  1272.             } catch (IOException e) {
  1273.                 throw new UncheckedIOException(e);
  1274.             }

  1275.             return RepositoryState.CHERRY_PICKING;
  1276.         }

  1277.         if (new File(getDirectory(), Constants.REVERT_HEAD).exists()) {
  1278.             try {
  1279.                 if (!readDirCache().hasUnmergedPaths()) {
  1280.                     // no unmerged paths
  1281.                     return RepositoryState.REVERTING_RESOLVED;
  1282.                 }
  1283.             } catch (IOException e) {
  1284.                 throw new UncheckedIOException(e);
  1285.             }

  1286.             return RepositoryState.REVERTING;
  1287.         }

  1288.         return RepositoryState.SAFE;
  1289.     }

  1290.     /**
  1291.      * Check validity of a ref name. It must not contain character that has
  1292.      * a special meaning in a Git object reference expression. Some other
  1293.      * dangerous characters are also excluded.
  1294.      *
  1295.      * For portability reasons '\' is excluded
  1296.      *
  1297.      * @param refName a {@link java.lang.String} object.
  1298.      * @return true if refName is a valid ref name
  1299.      */
  1300.     public static boolean isValidRefName(String refName) {
  1301.         final int len = refName.length();
  1302.         if (len == 0) {
  1303.             return false;
  1304.         }
  1305.         if (refName.endsWith(LOCK_SUFFIX)) {
  1306.             return false;
  1307.         }

  1308.         // Refs may be stored as loose files so invalid paths
  1309.         // on the local system must also be invalid refs.
  1310.         try {
  1311.             SystemReader.getInstance().checkPath(refName);
  1312.         } catch (CorruptObjectException e) {
  1313.             return false;
  1314.         }

  1315.         int components = 1;
  1316.         char p = '\0';
  1317.         for (int i = 0; i < len; i++) {
  1318.             final char c = refName.charAt(i);
  1319.             if (c <= ' ')
  1320.                 return false;
  1321.             switch (c) {
  1322.             case '.':
  1323.                 switch (p) {
  1324.                 case '\0': case '/': case '.':
  1325.                     return false;
  1326.                 }
  1327.                 if (i == len -1)
  1328.                     return false;
  1329.                 break;
  1330.             case '/':
  1331.                 if (i == 0 || i == len - 1)
  1332.                     return false;
  1333.                 if (p == '/')
  1334.                     return false;
  1335.                 components++;
  1336.                 break;
  1337.             case '{':
  1338.                 if (p == '@')
  1339.                     return false;
  1340.                 break;
  1341.             case '~': case '^': case ':':
  1342.             case '?': case '[': case '*':
  1343.             case '\\':
  1344.             case '\u007F':
  1345.                 return false;
  1346.             }
  1347.             p = c;
  1348.         }
  1349.         return components > 1;
  1350.     }

  1351.     /**
  1352.      * Normalizes the passed branch name into a possible valid branch name. The
  1353.      * validity of the returned name should be checked by a subsequent call to
  1354.      * {@link #isValidRefName(String)}.
  1355.      * <p>
  1356.      * Future implementations of this method could be more restrictive or more
  1357.      * lenient about the validity of specific characters in the returned name.
  1358.      * <p>
  1359.      * The current implementation returns the trimmed input string if this is
  1360.      * already a valid branch name. Otherwise it returns a trimmed string with
  1361.      * special characters not allowed by {@link #isValidRefName(String)}
  1362.      * replaced by hyphens ('-') and blanks replaced by underscores ('_').
  1363.      * Leading and trailing slashes, dots, hyphens, and underscores are removed.
  1364.      *
  1365.      * @param name
  1366.      *            to normalize
  1367.      * @return The normalized name or an empty String if it is {@code null} or
  1368.      *         empty.
  1369.      * @since 4.7
  1370.      * @see #isValidRefName(String)
  1371.      */
  1372.     public static String normalizeBranchName(String name) {
  1373.         if (name == null || name.isEmpty()) {
  1374.             return ""; //$NON-NLS-1$
  1375.         }
  1376.         String result = name.trim();
  1377.         String fullName = result.startsWith(Constants.R_HEADS) ? result
  1378.                 : Constants.R_HEADS + result;
  1379.         if (isValidRefName(fullName)) {
  1380.             return result;
  1381.         }

  1382.         // All Unicode blanks to underscore
  1383.         result = result.replaceAll("(?:\\h|\\v)+", "_"); //$NON-NLS-1$ //$NON-NLS-2$
  1384.         StringBuilder b = new StringBuilder();
  1385.         char p = '/';
  1386.         for (int i = 0, len = result.length(); i < len; i++) {
  1387.             char c = result.charAt(i);
  1388.             if (c < ' ' || c == 127) {
  1389.                 continue;
  1390.             }
  1391.             // Substitute a dash for problematic characters
  1392.             switch (c) {
  1393.             case '\\':
  1394.             case '^':
  1395.             case '~':
  1396.             case ':':
  1397.             case '?':
  1398.             case '*':
  1399.             case '[':
  1400.             case '@':
  1401.             case '<':
  1402.             case '>':
  1403.             case '|':
  1404.             case '"':
  1405.                 c = '-';
  1406.                 break;
  1407.             default:
  1408.                 break;
  1409.             }
  1410.             // Collapse multiple slashes, dashes, dots, underscores, and omit
  1411.             // dashes, dots, and underscores following a slash.
  1412.             switch (c) {
  1413.             case '/':
  1414.                 if (p == '/') {
  1415.                     continue;
  1416.                 }
  1417.                 p = '/';
  1418.                 break;
  1419.             case '.':
  1420.             case '_':
  1421.             case '-':
  1422.                 if (p == '/' || p == '-') {
  1423.                     continue;
  1424.                 }
  1425.                 p = '-';
  1426.                 break;
  1427.             default:
  1428.                 p = c;
  1429.                 break;
  1430.             }
  1431.             b.append(c);
  1432.         }
  1433.         // Strip trailing special characters, and avoid the .lock extension
  1434.         result = b.toString().replaceFirst("[/_.-]+$", "") //$NON-NLS-1$ //$NON-NLS-2$
  1435.                 .replaceAll("\\.lock($|/)", "_lock$1"); //$NON-NLS-1$ //$NON-NLS-2$
  1436.         return FORBIDDEN_BRANCH_NAME_COMPONENTS.matcher(result)
  1437.                 .replaceAll("$1+$2$3"); //$NON-NLS-1$
  1438.     }

  1439.     /**
  1440.      * Strip work dir and return normalized repository path.
  1441.      *
  1442.      * @param workDir
  1443.      *            Work dir
  1444.      * @param file
  1445.      *            File whose path shall be stripped of its workdir
  1446.      * @return normalized repository relative path or the empty string if the
  1447.      *         file is not relative to the work directory.
  1448.      */
  1449.     @NonNull
  1450.     public static String stripWorkDir(File workDir, File file) {
  1451.         final String filePath = file.getPath();
  1452.         final String workDirPath = workDir.getPath();

  1453.         if (filePath.length() <= workDirPath.length() ||
  1454.             filePath.charAt(workDirPath.length()) != File.separatorChar ||
  1455.             !filePath.startsWith(workDirPath)) {
  1456.             File absWd = workDir.isAbsolute() ? workDir : workDir.getAbsoluteFile();
  1457.             File absFile = file.isAbsolute() ? file : file.getAbsoluteFile();
  1458.             if (absWd == workDir && absFile == file)
  1459.                 return ""; //$NON-NLS-1$
  1460.             return stripWorkDir(absWd, absFile);
  1461.         }

  1462.         String relName = filePath.substring(workDirPath.length() + 1);
  1463.         if (File.separatorChar != '/')
  1464.             relName = relName.replace(File.separatorChar, '/');
  1465.         return relName;
  1466.     }

  1467.     /**
  1468.      * Whether this repository is bare
  1469.      *
  1470.      * @return true if this is bare, which implies it has no working directory.
  1471.      */
  1472.     public boolean isBare() {
  1473.         return workTree == null;
  1474.     }

  1475.     /**
  1476.      * Get the root directory of the working tree, where files are checked out
  1477.      * for viewing and editing.
  1478.      *
  1479.      * @return the root directory of the working tree, where files are checked
  1480.      *         out for viewing and editing.
  1481.      * @throws org.eclipse.jgit.errors.NoWorkTreeException
  1482.      *             if this is bare, which implies it has no working directory.
  1483.      *             See {@link #isBare()}.
  1484.      */
  1485.     @NonNull
  1486.     public File getWorkTree() throws NoWorkTreeException {
  1487.         if (isBare())
  1488.             throw new NoWorkTreeException();
  1489.         return workTree;
  1490.     }

  1491.     /**
  1492.      * Force a scan for changed refs. Fires an IndexChangedEvent(false) if
  1493.      * changes are detected.
  1494.      *
  1495.      * @throws java.io.IOException
  1496.      */
  1497.     public abstract void scanForRepoChanges() throws IOException;

  1498.     /**
  1499.      * Notify that the index changed by firing an IndexChangedEvent.
  1500.      *
  1501.      * @param internal
  1502.      *                     {@code true} if the index was changed by the same
  1503.      *                     JGit process
  1504.      * @since 5.0
  1505.      */
  1506.     public abstract void notifyIndexChanged(boolean internal);

  1507.     /**
  1508.      * Get a shortened more user friendly ref name
  1509.      *
  1510.      * @param refName
  1511.      *            a {@link java.lang.String} object.
  1512.      * @return a more user friendly ref name
  1513.      */
  1514.     @NonNull
  1515.     public static String shortenRefName(String refName) {
  1516.         if (refName.startsWith(Constants.R_HEADS))
  1517.             return refName.substring(Constants.R_HEADS.length());
  1518.         if (refName.startsWith(Constants.R_TAGS))
  1519.             return refName.substring(Constants.R_TAGS.length());
  1520.         if (refName.startsWith(Constants.R_REMOTES))
  1521.             return refName.substring(Constants.R_REMOTES.length());
  1522.         return refName;
  1523.     }

  1524.     /**
  1525.      * Get a shortened more user friendly remote tracking branch name
  1526.      *
  1527.      * @param refName
  1528.      *            a {@link java.lang.String} object.
  1529.      * @return the remote branch name part of <code>refName</code>, i.e. without
  1530.      *         the <code>refs/remotes/&lt;remote&gt;</code> prefix, if
  1531.      *         <code>refName</code> represents a remote tracking branch;
  1532.      *         otherwise {@code null}.
  1533.      * @since 3.4
  1534.      */
  1535.     @Nullable
  1536.     public String shortenRemoteBranchName(String refName) {
  1537.         for (String remote : getRemoteNames()) {
  1538.             String remotePrefix = Constants.R_REMOTES + remote + "/"; //$NON-NLS-1$
  1539.             if (refName.startsWith(remotePrefix))
  1540.                 return refName.substring(remotePrefix.length());
  1541.         }
  1542.         return null;
  1543.     }

  1544.     /**
  1545.      * Get remote name
  1546.      *
  1547.      * @param refName
  1548.      *            a {@link java.lang.String} object.
  1549.      * @return the remote name part of <code>refName</code>, i.e. without the
  1550.      *         <code>refs/remotes/&lt;remote&gt;</code> prefix, if
  1551.      *         <code>refName</code> represents a remote tracking branch;
  1552.      *         otherwise {@code null}.
  1553.      * @since 3.4
  1554.      */
  1555.     @Nullable
  1556.     public String getRemoteName(String refName) {
  1557.         for (String remote : getRemoteNames()) {
  1558.             String remotePrefix = Constants.R_REMOTES + remote + "/"; //$NON-NLS-1$
  1559.             if (refName.startsWith(remotePrefix))
  1560.                 return remote;
  1561.         }
  1562.         return null;
  1563.     }

  1564.     /**
  1565.      * Read the {@code GIT_DIR/description} file for gitweb.
  1566.      *
  1567.      * @return description text; null if no description has been configured.
  1568.      * @throws java.io.IOException
  1569.      *             description cannot be accessed.
  1570.      * @since 4.6
  1571.      */
  1572.     @Nullable
  1573.     public String getGitwebDescription() throws IOException {
  1574.         return null;
  1575.     }

  1576.     /**
  1577.      * Set the {@code GIT_DIR/description} file for gitweb.
  1578.      *
  1579.      * @param description
  1580.      *            new description; null to clear the description.
  1581.      * @throws java.io.IOException
  1582.      *             description cannot be persisted.
  1583.      * @since 4.6
  1584.      */
  1585.     public void setGitwebDescription(@Nullable String description)
  1586.             throws IOException {
  1587.         throw new IOException(JGitText.get().unsupportedRepositoryDescription);
  1588.     }

  1589.     /**
  1590.      * Get the reflog reader
  1591.      *
  1592.      * @param refName
  1593.      *            a {@link java.lang.String} object.
  1594.      * @return a {@link org.eclipse.jgit.lib.ReflogReader} for the supplied
  1595.      *         refname, or {@code null} if the named ref does not exist.
  1596.      * @throws java.io.IOException
  1597.      *             the ref could not be accessed.
  1598.      * @since 3.0
  1599.      */
  1600.     @Nullable
  1601.     public abstract ReflogReader getReflogReader(String refName)
  1602.             throws IOException;

  1603.     /**
  1604.      * Return the information stored in the file $GIT_DIR/MERGE_MSG. In this
  1605.      * file operations triggering a merge will store a template for the commit
  1606.      * message of the merge commit.
  1607.      *
  1608.      * @return a String containing the content of the MERGE_MSG file or
  1609.      *         {@code null} if this file doesn't exist
  1610.      * @throws java.io.IOException
  1611.      * @throws org.eclipse.jgit.errors.NoWorkTreeException
  1612.      *             if this is bare, which implies it has no working directory.
  1613.      *             See {@link #isBare()}.
  1614.      */
  1615.     @Nullable
  1616.     public String readMergeCommitMsg() throws IOException, NoWorkTreeException {
  1617.         return readCommitMsgFile(Constants.MERGE_MSG);
  1618.     }

  1619.     /**
  1620.      * Write new content to the file $GIT_DIR/MERGE_MSG. In this file operations
  1621.      * triggering a merge will store a template for the commit message of the
  1622.      * merge commit. If <code>null</code> is specified as message the file will
  1623.      * be deleted.
  1624.      *
  1625.      * @param msg
  1626.      *            the message which should be written or <code>null</code> to
  1627.      *            delete the file
  1628.      * @throws java.io.IOException
  1629.      */
  1630.     public void writeMergeCommitMsg(String msg) throws IOException {
  1631.         File mergeMsgFile = new File(gitDir, Constants.MERGE_MSG);
  1632.         writeCommitMsg(mergeMsgFile, msg);
  1633.     }

  1634.     /**
  1635.      * Return the information stored in the file $GIT_DIR/COMMIT_EDITMSG. In
  1636.      * this file hooks triggered by an operation may read or modify the current
  1637.      * commit message.
  1638.      *
  1639.      * @return a String containing the content of the COMMIT_EDITMSG file or
  1640.      *         {@code null} if this file doesn't exist
  1641.      * @throws java.io.IOException
  1642.      * @throws org.eclipse.jgit.errors.NoWorkTreeException
  1643.      *             if this is bare, which implies it has no working directory.
  1644.      *             See {@link #isBare()}.
  1645.      * @since 4.0
  1646.      */
  1647.     @Nullable
  1648.     public String readCommitEditMsg() throws IOException, NoWorkTreeException {
  1649.         return readCommitMsgFile(Constants.COMMIT_EDITMSG);
  1650.     }

  1651.     /**
  1652.      * Write new content to the file $GIT_DIR/COMMIT_EDITMSG. In this file hooks
  1653.      * triggered by an operation may read or modify the current commit message.
  1654.      * If {@code null} is specified as message the file will be deleted.
  1655.      *
  1656.      * @param msg
  1657.      *            the message which should be written or {@code null} to delete
  1658.      *            the file
  1659.      * @throws java.io.IOException
  1660.      * @since 4.0
  1661.      */
  1662.     public void writeCommitEditMsg(String msg) throws IOException {
  1663.         File commiEditMsgFile = new File(gitDir, Constants.COMMIT_EDITMSG);
  1664.         writeCommitMsg(commiEditMsgFile, msg);
  1665.     }

  1666.     /**
  1667.      * Return the information stored in the file $GIT_DIR/MERGE_HEAD. In this
  1668.      * file operations triggering a merge will store the IDs of all heads which
  1669.      * should be merged together with HEAD.
  1670.      *
  1671.      * @return a list of commits which IDs are listed in the MERGE_HEAD file or
  1672.      *         {@code null} if this file doesn't exist. Also if the file exists
  1673.      *         but is empty {@code null} will be returned
  1674.      * @throws java.io.IOException
  1675.      * @throws org.eclipse.jgit.errors.NoWorkTreeException
  1676.      *             if this is bare, which implies it has no working directory.
  1677.      *             See {@link #isBare()}.
  1678.      */
  1679.     @Nullable
  1680.     public List<ObjectId> readMergeHeads() throws IOException, NoWorkTreeException {
  1681.         if (isBare() || getDirectory() == null)
  1682.             throw new NoWorkTreeException();

  1683.         byte[] raw = readGitDirectoryFile(Constants.MERGE_HEAD);
  1684.         if (raw == null)
  1685.             return null;

  1686.         LinkedList<ObjectId> heads = new LinkedList<>();
  1687.         for (int p = 0; p < raw.length;) {
  1688.             heads.add(ObjectId.fromString(raw, p));
  1689.             p = RawParseUtils
  1690.                     .nextLF(raw, p + Constants.OBJECT_ID_STRING_LENGTH);
  1691.         }
  1692.         return heads;
  1693.     }

  1694.     /**
  1695.      * Write new merge-heads into $GIT_DIR/MERGE_HEAD. In this file operations
  1696.      * triggering a merge will store the IDs of all heads which should be merged
  1697.      * together with HEAD. If <code>null</code> is specified as list of commits
  1698.      * the file will be deleted
  1699.      *
  1700.      * @param heads
  1701.      *            a list of commits which IDs should be written to
  1702.      *            $GIT_DIR/MERGE_HEAD or <code>null</code> to delete the file
  1703.      * @throws java.io.IOException
  1704.      */
  1705.     public void writeMergeHeads(List<? extends ObjectId> heads) throws IOException {
  1706.         writeHeadsFile(heads, Constants.MERGE_HEAD);
  1707.     }

  1708.     /**
  1709.      * Return the information stored in the file $GIT_DIR/CHERRY_PICK_HEAD.
  1710.      *
  1711.      * @return object id from CHERRY_PICK_HEAD file or {@code null} if this file
  1712.      *         doesn't exist. Also if the file exists but is empty {@code null}
  1713.      *         will be returned
  1714.      * @throws java.io.IOException
  1715.      * @throws org.eclipse.jgit.errors.NoWorkTreeException
  1716.      *             if this is bare, which implies it has no working directory.
  1717.      *             See {@link #isBare()}.
  1718.      */
  1719.     @Nullable
  1720.     public ObjectId readCherryPickHead() throws IOException,
  1721.             NoWorkTreeException {
  1722.         if (isBare() || getDirectory() == null)
  1723.             throw new NoWorkTreeException();

  1724.         byte[] raw = readGitDirectoryFile(Constants.CHERRY_PICK_HEAD);
  1725.         if (raw == null)
  1726.             return null;

  1727.         return ObjectId.fromString(raw, 0);
  1728.     }

  1729.     /**
  1730.      * Return the information stored in the file $GIT_DIR/REVERT_HEAD.
  1731.      *
  1732.      * @return object id from REVERT_HEAD file or {@code null} if this file
  1733.      *         doesn't exist. Also if the file exists but is empty {@code null}
  1734.      *         will be returned
  1735.      * @throws java.io.IOException
  1736.      * @throws org.eclipse.jgit.errors.NoWorkTreeException
  1737.      *             if this is bare, which implies it has no working directory.
  1738.      *             See {@link #isBare()}.
  1739.      */
  1740.     @Nullable
  1741.     public ObjectId readRevertHead() throws IOException, NoWorkTreeException {
  1742.         if (isBare() || getDirectory() == null)
  1743.             throw new NoWorkTreeException();

  1744.         byte[] raw = readGitDirectoryFile(Constants.REVERT_HEAD);
  1745.         if (raw == null)
  1746.             return null;
  1747.         return ObjectId.fromString(raw, 0);
  1748.     }

  1749.     /**
  1750.      * Write cherry pick commit into $GIT_DIR/CHERRY_PICK_HEAD. This is used in
  1751.      * case of conflicts to store the cherry which was tried to be picked.
  1752.      *
  1753.      * @param head
  1754.      *            an object id of the cherry commit or <code>null</code> to
  1755.      *            delete the file
  1756.      * @throws java.io.IOException
  1757.      */
  1758.     public void writeCherryPickHead(ObjectId head) throws IOException {
  1759.         List<ObjectId> heads = (head != null) ? Collections.singletonList(head)
  1760.                 : null;
  1761.         writeHeadsFile(heads, Constants.CHERRY_PICK_HEAD);
  1762.     }

  1763.     /**
  1764.      * Write revert commit into $GIT_DIR/REVERT_HEAD. This is used in case of
  1765.      * conflicts to store the revert which was tried to be picked.
  1766.      *
  1767.      * @param head
  1768.      *            an object id of the revert commit or <code>null</code> to
  1769.      *            delete the file
  1770.      * @throws java.io.IOException
  1771.      */
  1772.     public void writeRevertHead(ObjectId head) throws IOException {
  1773.         List<ObjectId> heads = (head != null) ? Collections.singletonList(head)
  1774.                 : null;
  1775.         writeHeadsFile(heads, Constants.REVERT_HEAD);
  1776.     }

  1777.     /**
  1778.      * Write original HEAD commit into $GIT_DIR/ORIG_HEAD.
  1779.      *
  1780.      * @param head
  1781.      *            an object id of the original HEAD commit or <code>null</code>
  1782.      *            to delete the file
  1783.      * @throws java.io.IOException
  1784.      */
  1785.     public void writeOrigHead(ObjectId head) throws IOException {
  1786.         List<ObjectId> heads = head != null ? Collections.singletonList(head)
  1787.                 : null;
  1788.         writeHeadsFile(heads, Constants.ORIG_HEAD);
  1789.     }

  1790.     /**
  1791.      * Return the information stored in the file $GIT_DIR/ORIG_HEAD.
  1792.      *
  1793.      * @return object id from ORIG_HEAD file or {@code null} if this file
  1794.      *         doesn't exist. Also if the file exists but is empty {@code null}
  1795.      *         will be returned
  1796.      * @throws java.io.IOException
  1797.      * @throws org.eclipse.jgit.errors.NoWorkTreeException
  1798.      *             if this is bare, which implies it has no working directory.
  1799.      *             See {@link #isBare()}.
  1800.      */
  1801.     @Nullable
  1802.     public ObjectId readOrigHead() throws IOException, NoWorkTreeException {
  1803.         if (isBare() || getDirectory() == null)
  1804.             throw new NoWorkTreeException();

  1805.         byte[] raw = readGitDirectoryFile(Constants.ORIG_HEAD);
  1806.         return raw != null ? ObjectId.fromString(raw, 0) : null;
  1807.     }

  1808.     /**
  1809.      * Return the information stored in the file $GIT_DIR/SQUASH_MSG. In this
  1810.      * file operations triggering a squashed merge will store a template for the
  1811.      * commit message of the squash commit.
  1812.      *
  1813.      * @return a String containing the content of the SQUASH_MSG file or
  1814.      *         {@code null} if this file doesn't exist
  1815.      * @throws java.io.IOException
  1816.      * @throws NoWorkTreeException
  1817.      *             if this is bare, which implies it has no working directory.
  1818.      *             See {@link #isBare()}.
  1819.      */
  1820.     @Nullable
  1821.     public String readSquashCommitMsg() throws IOException {
  1822.         return readCommitMsgFile(Constants.SQUASH_MSG);
  1823.     }

  1824.     /**
  1825.      * Write new content to the file $GIT_DIR/SQUASH_MSG. In this file
  1826.      * operations triggering a squashed merge will store a template for the
  1827.      * commit message of the squash commit. If <code>null</code> is specified as
  1828.      * message the file will be deleted.
  1829.      *
  1830.      * @param msg
  1831.      *            the message which should be written or <code>null</code> to
  1832.      *            delete the file
  1833.      * @throws java.io.IOException
  1834.      */
  1835.     public void writeSquashCommitMsg(String msg) throws IOException {
  1836.         File squashMsgFile = new File(gitDir, Constants.SQUASH_MSG);
  1837.         writeCommitMsg(squashMsgFile, msg);
  1838.     }

  1839.     @Nullable
  1840.     private String readCommitMsgFile(String msgFilename) throws IOException {
  1841.         if (isBare() || getDirectory() == null)
  1842.             throw new NoWorkTreeException();

  1843.         File mergeMsgFile = new File(getDirectory(), msgFilename);
  1844.         try {
  1845.             return RawParseUtils.decode(IO.readFully(mergeMsgFile));
  1846.         } catch (FileNotFoundException e) {
  1847.             if (mergeMsgFile.exists()) {
  1848.                 throw e;
  1849.             }
  1850.             // the file has disappeared in the meantime ignore it
  1851.             return null;
  1852.         }
  1853.     }

  1854.     private void writeCommitMsg(File msgFile, String msg) throws IOException {
  1855.         if (msg != null) {
  1856.             try (FileOutputStream fos = new FileOutputStream(msgFile)) {
  1857.                 fos.write(msg.getBytes(UTF_8));
  1858.             }
  1859.         } else {
  1860.             FileUtils.delete(msgFile, FileUtils.SKIP_MISSING);
  1861.         }
  1862.     }

  1863.     /**
  1864.      * Read a file from the git directory.
  1865.      *
  1866.      * @param filename
  1867.      * @return the raw contents or {@code null} if the file doesn't exist or is
  1868.      *         empty
  1869.      * @throws IOException
  1870.      */
  1871.     private byte[] readGitDirectoryFile(String filename) throws IOException {
  1872.         File file = new File(getDirectory(), filename);
  1873.         try {
  1874.             byte[] raw = IO.readFully(file);
  1875.             return raw.length > 0 ? raw : null;
  1876.         } catch (FileNotFoundException notFound) {
  1877.             if (file.exists()) {
  1878.                 throw notFound;
  1879.             }
  1880.             return null;
  1881.         }
  1882.     }

  1883.     /**
  1884.      * Write the given heads to a file in the git directory.
  1885.      *
  1886.      * @param heads
  1887.      *            a list of object ids to write or null if the file should be
  1888.      *            deleted.
  1889.      * @param filename
  1890.      * @throws FileNotFoundException
  1891.      * @throws IOException
  1892.      */
  1893.     private void writeHeadsFile(List<? extends ObjectId> heads, String filename)
  1894.             throws FileNotFoundException, IOException {
  1895.         File headsFile = new File(getDirectory(), filename);
  1896.         if (heads != null) {
  1897.             try (OutputStream bos = new BufferedOutputStream(
  1898.                     new FileOutputStream(headsFile))) {
  1899.                 for (ObjectId id : heads) {
  1900.                     id.copyTo(bos);
  1901.                     bos.write('\n');
  1902.                 }
  1903.             }
  1904.         } else {
  1905.             FileUtils.delete(headsFile, FileUtils.SKIP_MISSING);
  1906.         }
  1907.     }

  1908.     /**
  1909.      * Read a file formatted like the git-rebase-todo file. The "done" file is
  1910.      * also formatted like the git-rebase-todo file. These files can be found in
  1911.      * .git/rebase-merge/ or .git/rebase-append/ folders.
  1912.      *
  1913.      * @param path
  1914.      *            path to the file relative to the repository's git-dir. E.g.
  1915.      *            "rebase-merge/git-rebase-todo" or "rebase-append/done"
  1916.      * @param includeComments
  1917.      *            <code>true</code> if also comments should be reported
  1918.      * @return the list of steps
  1919.      * @throws java.io.IOException
  1920.      * @since 3.2
  1921.      */
  1922.     @NonNull
  1923.     public List<RebaseTodoLine> readRebaseTodo(String path,
  1924.             boolean includeComments)
  1925.             throws IOException {
  1926.         return new RebaseTodoFile(this).readRebaseTodo(path, includeComments);
  1927.     }

  1928.     /**
  1929.      * Write a file formatted like a git-rebase-todo file.
  1930.      *
  1931.      * @param path
  1932.      *            path to the file relative to the repository's git-dir. E.g.
  1933.      *            "rebase-merge/git-rebase-todo" or "rebase-append/done"
  1934.      * @param steps
  1935.      *            the steps to be written
  1936.      * @param append
  1937.      *            whether to append to an existing file or to write a new file
  1938.      * @throws java.io.IOException
  1939.      * @since 3.2
  1940.      */
  1941.     public void writeRebaseTodoFile(String path, List<RebaseTodoLine> steps,
  1942.             boolean append)
  1943.             throws IOException {
  1944.         new RebaseTodoFile(this).writeRebaseTodoFile(path, steps, append);
  1945.     }

  1946.     /**
  1947.      * Get the names of all known remotes
  1948.      *
  1949.      * @return the names of all known remotes
  1950.      * @since 3.4
  1951.      */
  1952.     @NonNull
  1953.     public Set<String> getRemoteNames() {
  1954.         return getConfig()
  1955.                 .getSubsections(ConfigConstants.CONFIG_REMOTE_SECTION);
  1956.     }

  1957.     /**
  1958.      * Check whether any housekeeping is required; if yes, run garbage
  1959.      * collection; if not, exit without performing any work. Some JGit commands
  1960.      * run autoGC after performing operations that could create many loose
  1961.      * objects.
  1962.      * <p>
  1963.      * Currently this option is supported for repositories of type
  1964.      * {@code FileRepository} only. See
  1965.      * {@link org.eclipse.jgit.internal.storage.file.GC#setAuto(boolean)} for
  1966.      * configuration details.
  1967.      *
  1968.      * @param monitor
  1969.      *            to report progress
  1970.      * @since 4.6
  1971.      */
  1972.     public void autoGC(ProgressMonitor monitor) {
  1973.         // default does nothing
  1974.     }
  1975. }