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> and others
  8.  *
  9.  * This program and the accompanying materials are made available under the
  10.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  11.  * https://www.eclipse.org/org/documents/edl-v10.php.
  12.  *
  13.  * SPDX-License-Identifier: BSD-3-Clause
  14.  */

  15. package org.eclipse.jgit.lib;

  16. import static org.eclipse.jgit.lib.Constants.LOCK_SUFFIX;
  17. import static java.nio.charset.StandardCharsets.UTF_8;

  18. import java.io.BufferedOutputStream;
  19. import java.io.File;
  20. import java.io.FileNotFoundException;
  21. import java.io.FileOutputStream;
  22. import java.io.IOException;
  23. import java.io.OutputStream;
  24. import java.io.UncheckedIOException;
  25. import java.net.URISyntaxException;
  26. import java.text.MessageFormat;
  27. import java.util.Collection;
  28. import java.util.Collections;
  29. import java.util.HashMap;
  30. import java.util.HashSet;
  31. import java.util.LinkedList;
  32. import java.util.List;
  33. import java.util.Map;
  34. import java.util.Set;
  35. import java.util.concurrent.atomic.AtomicInteger;
  36. import java.util.concurrent.atomic.AtomicLong;
  37. import java.util.regex.Pattern;

  38. import org.eclipse.jgit.annotations.NonNull;
  39. import org.eclipse.jgit.annotations.Nullable;
  40. import org.eclipse.jgit.attributes.AttributesNodeProvider;
  41. import org.eclipse.jgit.dircache.DirCache;
  42. import org.eclipse.jgit.errors.AmbiguousObjectException;
  43. import org.eclipse.jgit.errors.CorruptObjectException;
  44. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  45. import org.eclipse.jgit.errors.MissingObjectException;
  46. import org.eclipse.jgit.errors.NoWorkTreeException;
  47. import org.eclipse.jgit.errors.RevisionSyntaxException;
  48. import org.eclipse.jgit.events.IndexChangedEvent;
  49. import org.eclipse.jgit.events.IndexChangedListener;
  50. import org.eclipse.jgit.events.ListenerList;
  51. import org.eclipse.jgit.events.RepositoryEvent;
  52. import org.eclipse.jgit.internal.JGitText;
  53. import org.eclipse.jgit.revwalk.RevBlob;
  54. import org.eclipse.jgit.revwalk.RevCommit;
  55. import org.eclipse.jgit.revwalk.RevObject;
  56. import org.eclipse.jgit.revwalk.RevTree;
  57. import org.eclipse.jgit.revwalk.RevWalk;
  58. import org.eclipse.jgit.transport.RefSpec;
  59. import org.eclipse.jgit.transport.RemoteConfig;
  60. import org.eclipse.jgit.treewalk.TreeWalk;
  61. import org.eclipse.jgit.util.FS;
  62. import org.eclipse.jgit.util.FileUtils;
  63. import org.eclipse.jgit.util.IO;
  64. import org.eclipse.jgit.util.RawParseUtils;
  65. import org.eclipse.jgit.util.SystemReader;
  66. import org.slf4j.Logger;
  67. import org.slf4j.LoggerFactory;

  68. /**
  69.  * Represents a Git repository.
  70.  * <p>
  71.  * A repository holds all objects and refs used for managing source code (could
  72.  * be any type of file, but source code is what SCM's are typically used for).
  73.  * <p>
  74.  * The thread-safety of a {@link org.eclipse.jgit.lib.Repository} very much
  75.  * depends on the concrete implementation. Applications working with a generic
  76.  * {@code Repository} type must not assume the instance is thread-safe.
  77.  * <ul>
  78.  * <li>{@code FileRepository} is thread-safe.
  79.  * <li>{@code DfsRepository} thread-safety is determined by its subclass.
  80.  * </ul>
  81.  */
  82. public abstract class Repository implements AutoCloseable {
  83.     private static final Logger LOG = LoggerFactory.getLogger(Repository.class);
  84.     private static final ListenerList globalListeners = new ListenerList();

  85.     /**
  86.      * Branch names containing slashes should not have a name component that is
  87.      * one of the reserved device names on Windows.
  88.      *
  89.      * @see #normalizeBranchName(String)
  90.      */
  91.     private static final Pattern FORBIDDEN_BRANCH_NAME_COMPONENTS = Pattern
  92.             .compile(
  93.                     "(^|/)(aux|com[1-9]|con|lpt[1-9]|nul|prn)(\\.[^/]*)?", //$NON-NLS-1$
  94.                     Pattern.CASE_INSENSITIVE);

  95.     /**
  96.      * Get the global listener list observing all events in this JVM.
  97.      *
  98.      * @return the global listener list observing all events in this JVM.
  99.      */
  100.     public static ListenerList getGlobalListenerList() {
  101.         return globalListeners;
  102.     }

  103.     /** Use counter */
  104.     final AtomicInteger useCnt = new AtomicInteger(1);

  105.     final AtomicLong closedAt = new AtomicLong();

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

  108.     /** File abstraction used to resolve paths. */
  109.     private final FS fs;

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

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

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

  115.     private final String initialBranch;

  116.     /**
  117.      * Initialize a new repository instance.
  118.      *
  119.      * @param options
  120.      *            options to configure the repository.
  121.      */
  122.     protected Repository(BaseRepositoryBuilder options) {
  123.         gitDir = options.getGitDir();
  124.         fs = options.getFS();
  125.         workTree = options.getWorkTree();
  126.         indexFile = options.getIndexFile();
  127.         initialBranch = options.getInitialBranch();
  128.     }

  129.     /**
  130.      * Get listeners observing only events on this repository.
  131.      *
  132.      * @return listeners observing only events on this repository.
  133.      */
  134.     @NonNull
  135.     public ListenerList getListenerList() {
  136.         return myListeners;
  137.     }

  138.     /**
  139.      * Fire an event to all registered listeners.
  140.      * <p>
  141.      * The source repository of the event is automatically set to this
  142.      * repository, before the event is delivered to any listeners.
  143.      *
  144.      * @param event
  145.      *            the event to deliver.
  146.      */
  147.     public void fireEvent(RepositoryEvent<?> event) {
  148.         event.setRepository(this);
  149.         myListeners.dispatch(event);
  150.         globalListeners.dispatch(event);
  151.     }

  152.     /**
  153.      * Create a new Git repository.
  154.      * <p>
  155.      * Repository with working tree is created using this method. This method is
  156.      * the same as {@code create(false)}.
  157.      *
  158.      * @throws java.io.IOException
  159.      * @see #create(boolean)
  160.      */
  161.     public void create() throws IOException {
  162.         create(false);
  163.     }

  164.     /**
  165.      * Create a new Git repository initializing the necessary files and
  166.      * directories.
  167.      *
  168.      * @param bare
  169.      *            if true, a bare repository (a repository without a working
  170.      *            directory) is created.
  171.      * @throws java.io.IOException
  172.      *             in case of IO problem
  173.      */
  174.     public abstract void create(boolean bare) throws IOException;

  175.     /**
  176.      * Get local metadata directory
  177.      *
  178.      * @return local metadata directory; {@code null} if repository isn't local.
  179.      */
  180.     /*
  181.      * TODO This method should be annotated as Nullable, because in some
  182.      * specific configurations metadata is not located in the local file system
  183.      * (for example in memory databases). In "usual" repositories this
  184.      * annotation would only cause compiler errors at places where the actual
  185.      * directory can never be null.
  186.      */
  187.     public File getDirectory() {
  188.         return gitDir;
  189.     }

  190.     /**
  191.      * Get repository identifier.
  192.      *
  193.      * @return repository identifier. The returned identifier has to be unique
  194.      *         within a given Git server.
  195.      * @since 5.4
  196.      */
  197.     public abstract String getIdentifier();

  198.     /**
  199.      * Get the object database which stores this repository's data.
  200.      *
  201.      * @return the object database which stores this repository's data.
  202.      */
  203.     @NonNull
  204.     public abstract ObjectDatabase getObjectDatabase();

  205.     /**
  206.      * Create a new inserter to create objects in {@link #getObjectDatabase()}.
  207.      *
  208.      * @return a new inserter to create objects in {@link #getObjectDatabase()}.
  209.      */
  210.     @NonNull
  211.     public ObjectInserter newObjectInserter() {
  212.         return getObjectDatabase().newInserter();
  213.     }

  214.     /**
  215.      * Create a new reader to read objects from {@link #getObjectDatabase()}.
  216.      *
  217.      * @return a new reader to read objects from {@link #getObjectDatabase()}.
  218.      */
  219.     @NonNull
  220.     public ObjectReader newObjectReader() {
  221.         return getObjectDatabase().newReader();
  222.     }

  223.     /**
  224.      * Get the reference database which stores the reference namespace.
  225.      *
  226.      * @return the reference database which stores the reference namespace.
  227.      */
  228.     @NonNull
  229.     public abstract RefDatabase getRefDatabase();

  230.     /**
  231.      * Get the configuration of this repository.
  232.      *
  233.      * @return the configuration of this repository.
  234.      */
  235.     @NonNull
  236.     public abstract StoredConfig getConfig();

  237.     /**
  238.      * Create a new {@link org.eclipse.jgit.attributes.AttributesNodeProvider}.
  239.      *
  240.      * @return a new {@link org.eclipse.jgit.attributes.AttributesNodeProvider}.
  241.      *         This {@link org.eclipse.jgit.attributes.AttributesNodeProvider}
  242.      *         is lazy loaded only once. It means that it will not be updated
  243.      *         after loading. Prefer creating new instance for each use.
  244.      * @since 4.2
  245.      */
  246.     @NonNull
  247.     public abstract AttributesNodeProvider createAttributesNodeProvider();

  248.     /**
  249.      * Get the used file system abstraction.
  250.      *
  251.      * @return the used file system abstraction, or {@code null} if
  252.      *         repository isn't local.
  253.      */
  254.     /*
  255.      * TODO This method should be annotated as Nullable, because in some
  256.      * specific configurations metadata is not located in the local file system
  257.      * (for example in memory databases). In "usual" repositories this
  258.      * annotation would only cause compiler errors at places where the actual
  259.      * directory can never be null.
  260.      */
  261.     public FS getFS() {
  262.         return fs;
  263.     }

  264.     /**
  265.      * Whether the specified object is stored in this repo or any of the known
  266.      * shared repositories.
  267.      *
  268.      * @param objectId
  269.      *            a {@link org.eclipse.jgit.lib.AnyObjectId} object.
  270.      * @return true if the specified object is stored in this repo or any of the
  271.      *         known shared repositories.
  272.      * @deprecated use {@code getObjectDatabase().has(objectId)}
  273.      */
  274.     @Deprecated
  275.     public boolean hasObject(AnyObjectId objectId) {
  276.         try {
  277.             return getObjectDatabase().has(objectId);
  278.         } catch (IOException e) {
  279.             throw new UncheckedIOException(e);
  280.         }
  281.     }

  282.     /**
  283.      * Open an object from this repository.
  284.      * <p>
  285.      * This is a one-shot call interface which may be faster than allocating a
  286.      * {@link #newObjectReader()} to perform the lookup.
  287.      *
  288.      * @param objectId
  289.      *            identity of the object to open.
  290.      * @return a {@link org.eclipse.jgit.lib.ObjectLoader} for accessing the
  291.      *         object.
  292.      * @throws org.eclipse.jgit.errors.MissingObjectException
  293.      *             the object does not exist.
  294.      * @throws java.io.IOException
  295.      *             the object store cannot be accessed.
  296.      */
  297.     @NonNull
  298.     public ObjectLoader open(AnyObjectId objectId)
  299.             throws MissingObjectException, IOException {
  300.         return getObjectDatabase().open(objectId);
  301.     }

  302.     /**
  303.      * Open an object from this repository.
  304.      * <p>
  305.      * This is a one-shot call interface which may be faster than allocating a
  306.      * {@link #newObjectReader()} to perform the lookup.
  307.      *
  308.      * @param objectId
  309.      *            identity of the object to open.
  310.      * @param typeHint
  311.      *            hint about the type of object being requested, e.g.
  312.      *            {@link org.eclipse.jgit.lib.Constants#OBJ_BLOB};
  313.      *            {@link org.eclipse.jgit.lib.ObjectReader#OBJ_ANY} if the
  314.      *            object type is not known, or does not matter to the caller.
  315.      * @return a {@link org.eclipse.jgit.lib.ObjectLoader} for accessing the
  316.      *         object.
  317.      * @throws org.eclipse.jgit.errors.MissingObjectException
  318.      *             the object does not exist.
  319.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  320.      *             typeHint was not OBJ_ANY, and the object's actual type does
  321.      *             not match typeHint.
  322.      * @throws java.io.IOException
  323.      *             the object store cannot be accessed.
  324.      */
  325.     @NonNull
  326.     public ObjectLoader open(AnyObjectId objectId, int typeHint)
  327.             throws MissingObjectException, IncorrectObjectTypeException,
  328.             IOException {
  329.         return getObjectDatabase().open(objectId, typeHint);
  330.     }

  331.     /**
  332.      * Create a command to update, create or delete a ref in this repository.
  333.      *
  334.      * @param ref
  335.      *            name of the ref the caller wants to modify.
  336.      * @return an update command. The caller must finish populating this command
  337.      *         and then invoke one of the update methods to actually make a
  338.      *         change.
  339.      * @throws java.io.IOException
  340.      *             a symbolic ref was passed in and could not be resolved back
  341.      *             to the base ref, as the symbolic ref could not be read.
  342.      */
  343.     @NonNull
  344.     public RefUpdate updateRef(String ref) throws IOException {
  345.         return updateRef(ref, false);
  346.     }

  347.     /**
  348.      * Create a command to update, create or delete a ref in this repository.
  349.      *
  350.      * @param ref
  351.      *            name of the ref the caller wants to modify.
  352.      * @param detach
  353.      *            true to create a detached head
  354.      * @return an update command. The caller must finish populating this command
  355.      *         and then invoke one of the update methods to actually make a
  356.      *         change.
  357.      * @throws java.io.IOException
  358.      *             a symbolic ref was passed in and could not be resolved back
  359.      *             to the base ref, as the symbolic ref could not be read.
  360.      */
  361.     @NonNull
  362.     public RefUpdate updateRef(String ref, boolean detach) throws IOException {
  363.         return getRefDatabase().newUpdate(ref, detach);
  364.     }

  365.     /**
  366.      * Create a command to rename a ref in this repository
  367.      *
  368.      * @param fromRef
  369.      *            name of ref to rename from
  370.      * @param toRef
  371.      *            name of ref to rename to
  372.      * @return an update command that knows how to rename a branch to another.
  373.      * @throws java.io.IOException
  374.      *             the rename could not be performed.
  375.      */
  376.     @NonNull
  377.     public RefRename renameRef(String fromRef, String toRef) throws IOException {
  378.         return getRefDatabase().newRename(fromRef, toRef);
  379.     }

  380.     /**
  381.      * Parse a git revision string and return an object id.
  382.      *
  383.      * Combinations of these operators are supported:
  384.      * <ul>
  385.      * <li><b>HEAD</b>, <b>MERGE_HEAD</b>, <b>FETCH_HEAD</b></li>
  386.      * <li><b>SHA-1</b>: a complete or abbreviated SHA-1</li>
  387.      * <li><b>refs/...</b>: a complete reference name</li>
  388.      * <li><b>short-name</b>: a short reference name under {@code refs/heads},
  389.      * {@code refs/tags}, or {@code refs/remotes} namespace</li>
  390.      * <li><b>tag-NN-gABBREV</b>: output from describe, parsed by treating
  391.      * {@code ABBREV} as an abbreviated SHA-1.</li>
  392.      * <li><i>id</i><b>^</b>: first parent of commit <i>id</i>, this is the same
  393.      * as {@code id^1}</li>
  394.      * <li><i>id</i><b>^0</b>: ensure <i>id</i> is a commit</li>
  395.      * <li><i>id</i><b>^n</b>: n-th parent of commit <i>id</i></li>
  396.      * <li><i>id</i><b>~n</b>: n-th historical ancestor of <i>id</i>, by first
  397.      * parent. {@code id~3} is equivalent to {@code id^1^1^1} or {@code id^^^}.</li>
  398.      * <li><i>id</i><b>:path</b>: Lookup path under tree named by <i>id</i></li>
  399.      * <li><i>id</i><b>^{commit}</b>: ensure <i>id</i> is a commit</li>
  400.      * <li><i>id</i><b>^{tree}</b>: ensure <i>id</i> is a tree</li>
  401.      * <li><i>id</i><b>^{tag}</b>: ensure <i>id</i> is a tag</li>
  402.      * <li><i>id</i><b>^{blob}</b>: ensure <i>id</i> is a blob</li>
  403.      * </ul>
  404.      *
  405.      * <p>
  406.      * The following operators are specified by Git conventions, but are not
  407.      * supported by this method:
  408.      * <ul>
  409.      * <li><b>ref@{n}</b>: n-th version of ref as given by its reflog</li>
  410.      * <li><b>ref@{time}</b>: value of ref at the designated time</li>
  411.      * </ul>
  412.      *
  413.      * @param revstr
  414.      *            A git object references expression
  415.      * @return an ObjectId or {@code null} if revstr can't be resolved to any
  416.      *         ObjectId
  417.      * @throws org.eclipse.jgit.errors.AmbiguousObjectException
  418.      *             {@code revstr} contains an abbreviated ObjectId and this
  419.      *             repository contains more than one object which match to the
  420.      *             input abbreviation.
  421.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  422.      *             the id parsed does not meet the type required to finish
  423.      *             applying the operators in the expression.
  424.      * @throws org.eclipse.jgit.errors.RevisionSyntaxException
  425.      *             the expression is not supported by this implementation, or
  426.      *             does not meet the standard syntax.
  427.      * @throws java.io.IOException
  428.      *             on serious errors
  429.      */
  430.     @Nullable
  431.     public ObjectId resolve(String revstr)
  432.             throws AmbiguousObjectException, IncorrectObjectTypeException,
  433.             RevisionSyntaxException, IOException {
  434.         try (RevWalk rw = new RevWalk(this)) {
  435.             rw.setRetainBody(false);
  436.             Object resolved = resolve(rw, revstr);
  437.             if (resolved instanceof String) {
  438.                 final Ref ref = findRef((String) resolved);
  439.                 return ref != null ? ref.getLeaf().getObjectId() : null;
  440.             }
  441.             return (ObjectId) resolved;
  442.         }
  443.     }

  444.     /**
  445.      * Simplify an expression, but unlike {@link #resolve(String)} it will not
  446.      * resolve a branch passed or resulting from the expression, such as @{-}.
  447.      * Thus this method can be used to process an expression to a method that
  448.      * expects a branch or revision id.
  449.      *
  450.      * @param revstr a {@link java.lang.String} object.
  451.      * @return object id or ref name from resolved expression or {@code null} if
  452.      *         given expression cannot be resolved
  453.      * @throws org.eclipse.jgit.errors.AmbiguousObjectException
  454.      * @throws java.io.IOException
  455.      */
  456.     @Nullable
  457.     public String simplify(String revstr)
  458.             throws AmbiguousObjectException, IOException {
  459.         try (RevWalk rw = new RevWalk(this)) {
  460.             rw.setRetainBody(true);
  461.             Object resolved = resolve(rw, revstr);
  462.             if (resolved != null) {
  463.                 if (resolved instanceof String) {
  464.                     return (String) resolved;
  465.                 }
  466.                 return ((AnyObjectId) resolved).getName();
  467.             }
  468.             return null;
  469.         }
  470.     }

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

  673.                         RemoteConfig remoteConfig;
  674.                         try {
  675.                             remoteConfig = new RemoteConfig(getConfig(),
  676.                                     "origin"); //$NON-NLS-1$
  677.                         } catch (URISyntaxException e) {
  678.                             RevisionSyntaxException rse = new RevisionSyntaxException(
  679.                                     revstr);
  680.                             rse.initCause(e);
  681.                             throw rse;
  682.                         }
  683.                         String remoteBranchName = getConfig()
  684.                                 .getString(
  685.                                         ConfigConstants.CONFIG_BRANCH_SECTION,
  686.                                 Repository.shortenRefName(ref.getName()),
  687.                                         ConfigConstants.CONFIG_KEY_MERGE);
  688.                         List<RefSpec> fetchRefSpecs = remoteConfig
  689.                                 .getFetchRefSpecs();
  690.                         for (RefSpec refSpec : fetchRefSpecs) {
  691.                             if (refSpec.matchSource(remoteBranchName)) {
  692.                                 RefSpec expandFromSource = refSpec
  693.                                         .expandFromSource(remoteBranchName);
  694.                                 name = expandFromSource.getDestination();
  695.                                 break;
  696.                             }
  697.                         }
  698.                         if (name == null)
  699.                             throw new RevisionSyntaxException(revstr);
  700.                     } else if (time.matches("^-\\d+$")) { //$NON-NLS-1$
  701.                         if (name != null) {
  702.                             throw new RevisionSyntaxException(revstr);
  703.                         }
  704.                         String previousCheckout = resolveReflogCheckout(
  705.                                 -Integer.parseInt(time));
  706.                         if (ObjectId.isId(previousCheckout)) {
  707.                             rev = parseSimple(rw, previousCheckout);
  708.                         } else {
  709.                             name = previousCheckout;
  710.                         }
  711.                     } else {
  712.                         if (name == null)
  713.                             name = new String(revChars, done, i);
  714.                         if (name.isEmpty())
  715.                             name = Constants.HEAD;
  716.                         if (!Repository.isValidRefName("x/" + name)) //$NON-NLS-1$
  717.                             throw new RevisionSyntaxException(MessageFormat
  718.                                     .format(JGitText.get().invalidRefName,
  719.                                             name),
  720.                                     revstr);
  721.                         Ref ref = findRef(name);
  722.                         name = null;
  723.                         if (ref == null)
  724.                             return null;
  725.                         // @{n} means current branch, not HEAD@{1} unless
  726.                         // detached
  727.                         if (ref.isSymbolic())
  728.                             ref = ref.getLeaf();
  729.                         rev = resolveReflog(rw, ref, time);
  730.                     }
  731.                     i = m;
  732.                 } else
  733.                     throw new RevisionSyntaxException(revstr);
  734.                 break;
  735.             case ':': {
  736.                 RevTree tree;
  737.                 if (rev == null) {
  738.                     if (name == null)
  739.                         name = new String(revChars, done, i);
  740.                     if (name.isEmpty())
  741.                         name = Constants.HEAD;
  742.                     rev = parseSimple(rw, name);
  743.                     name = null;
  744.                 }
  745.                 if (rev == null)
  746.                     return null;
  747.                 tree = rw.parseTree(rev);
  748.                 if (i == revChars.length - 1)
  749.                     return tree.copy();

  750.                 TreeWalk tw = TreeWalk.forPath(rw.getObjectReader(),
  751.                         new String(revChars, i + 1, revChars.length - i - 1),
  752.                         tree);
  753.                 return tw != null ? tw.getObjectId(0) : null;
  754.             }
  755.             default:
  756.                 if (rev != null)
  757.                     throw new RevisionSyntaxException(revstr);
  758.             }
  759.         }
  760.         if (rev != null)
  761.             return rev.copy();
  762.         if (name != null)
  763.             return name;
  764.         if (done == revstr.length())
  765.             return null;
  766.         name = revstr.substring(done);
  767.         if (!Repository.isValidRefName("x/" + name)) //$NON-NLS-1$
  768.             throw new RevisionSyntaxException(
  769.                     MessageFormat.format(JGitText.get().invalidRefName, name),
  770.                     revstr);
  771.         if (findRef(name) != null)
  772.             return name;
  773.         return resolveSimple(name);
  774.     }

  775.     private static boolean isHex(char c) {
  776.         return ('0' <= c && c <= '9') //
  777.                 || ('a' <= c && c <= 'f') //
  778.                 || ('A' <= c && c <= 'F');
  779.     }

  780.     private static boolean isAllHex(String str, int ptr) {
  781.         while (ptr < str.length()) {
  782.             if (!isHex(str.charAt(ptr++)))
  783.                 return false;
  784.         }
  785.         return true;
  786.     }

  787.     @Nullable
  788.     private RevObject parseSimple(RevWalk rw, String revstr) throws IOException {
  789.         ObjectId id = resolveSimple(revstr);
  790.         return id != null ? rw.parseAny(id) : null;
  791.     }

  792.     @Nullable
  793.     private ObjectId resolveSimple(String revstr) throws IOException {
  794.         if (ObjectId.isId(revstr))
  795.             return ObjectId.fromString(revstr);

  796.         if (Repository.isValidRefName("x/" + revstr)) { //$NON-NLS-1$
  797.             Ref r = getRefDatabase().findRef(revstr);
  798.             if (r != null)
  799.                 return r.getObjectId();
  800.         }

  801.         if (AbbreviatedObjectId.isId(revstr))
  802.             return resolveAbbreviation(revstr);

  803.         int dashg = revstr.indexOf("-g"); //$NON-NLS-1$
  804.         if ((dashg + 5) < revstr.length() && 0 <= dashg
  805.                 && isHex(revstr.charAt(dashg + 2))
  806.                 && isHex(revstr.charAt(dashg + 3))
  807.                 && isAllHex(revstr, dashg + 4)) {
  808.             // Possibly output from git describe?
  809.             String s = revstr.substring(dashg + 2);
  810.             if (AbbreviatedObjectId.isId(s))
  811.                 return resolveAbbreviation(s);
  812.         }

  813.         return null;
  814.     }

  815.     @Nullable
  816.     private String resolveReflogCheckout(int checkoutNo)
  817.             throws IOException {
  818.         ReflogReader reader = getReflogReader(Constants.HEAD);
  819.         if (reader == null) {
  820.             return null;
  821.         }
  822.         List<ReflogEntry> reflogEntries = reader.getReverseEntries();
  823.         for (ReflogEntry entry : reflogEntries) {
  824.             CheckoutEntry checkout = entry.parseCheckout();
  825.             if (checkout != null)
  826.                 if (checkoutNo-- == 1)
  827.                     return checkout.getFromBranch();
  828.         }
  829.         return null;
  830.     }

  831.     private RevCommit resolveReflog(RevWalk rw, Ref ref, String time)
  832.             throws IOException {
  833.         int number;
  834.         try {
  835.             number = Integer.parseInt(time);
  836.         } catch (NumberFormatException nfe) {
  837.             RevisionSyntaxException rse = new RevisionSyntaxException(
  838.                     MessageFormat.format(JGitText.get().invalidReflogRevision,
  839.                             time));
  840.             rse.initCause(nfe);
  841.             throw rse;
  842.         }
  843.         assert number >= 0;
  844.         ReflogReader reader = getReflogReader(ref.getName());
  845.         if (reader == null) {
  846.             throw new RevisionSyntaxException(
  847.                     MessageFormat.format(JGitText.get().reflogEntryNotFound,
  848.                             Integer.valueOf(number), ref.getName()));
  849.         }
  850.         ReflogEntry entry = reader.getReverseEntry(number);
  851.         if (entry == null)
  852.             throw new RevisionSyntaxException(MessageFormat.format(
  853.                     JGitText.get().reflogEntryNotFound,
  854.                     Integer.valueOf(number), ref.getName()));

  855.         return rw.parseCommit(entry.getNewId());
  856.     }

  857.     @Nullable
  858.     private ObjectId resolveAbbreviation(String revstr) throws IOException,
  859.             AmbiguousObjectException {
  860.         AbbreviatedObjectId id = AbbreviatedObjectId.fromString(revstr);
  861.         try (ObjectReader reader = newObjectReader()) {
  862.             Collection<ObjectId> matches = reader.resolve(id);
  863.             if (matches.isEmpty())
  864.                 return null;
  865.             else if (matches.size() == 1)
  866.                 return matches.iterator().next();
  867.             else
  868.                 throw new AmbiguousObjectException(id, matches);
  869.         }
  870.     }

  871.     /**
  872.      * Increment the use counter by one, requiring a matched {@link #close()}.
  873.      */
  874.     public void incrementOpen() {
  875.         useCnt.incrementAndGet();
  876.     }

  877.     /**
  878.      * {@inheritDoc}
  879.      * <p>
  880.      * Decrement the use count, and maybe close resources.
  881.      */
  882.     @Override
  883.     public void close() {
  884.         int newCount = useCnt.decrementAndGet();
  885.         if (newCount == 0) {
  886.             if (RepositoryCache.isCached(this)) {
  887.                 closedAt.set(System.currentTimeMillis());
  888.             } else {
  889.                 doClose();
  890.             }
  891.         } else if (newCount == -1) {
  892.             // should not happen, only log when useCnt became negative to
  893.             // minimize number of log entries
  894.             String message = MessageFormat.format(JGitText.get().corruptUseCnt,
  895.                     toString());
  896.             if (LOG.isDebugEnabled()) {
  897.                 LOG.debug(message, new IllegalStateException());
  898.             } else {
  899.                 LOG.warn(message);
  900.             }
  901.             if (RepositoryCache.isCached(this)) {
  902.                 closedAt.set(System.currentTimeMillis());
  903.             }
  904.         }
  905.     }

  906.     /**
  907.      * Invoked when the use count drops to zero during {@link #close()}.
  908.      * <p>
  909.      * The default implementation closes the object and ref databases.
  910.      */
  911.     protected void doClose() {
  912.         getObjectDatabase().close();
  913.         getRefDatabase().close();
  914.     }

  915.     /** {@inheritDoc} */
  916.     @Override
  917.     @NonNull
  918.     public String toString() {
  919.         String desc;
  920.         File directory = getDirectory();
  921.         if (directory != null)
  922.             desc = directory.getPath();
  923.         else
  924.             desc = getClass().getSimpleName() + "-" //$NON-NLS-1$
  925.                     + System.identityHashCode(this);
  926.         return "Repository[" + desc + "]"; //$NON-NLS-1$ //$NON-NLS-2$
  927.     }

  928.     /**
  929.      * Get the name of the reference that {@code HEAD} points to.
  930.      * <p>
  931.      * This is essentially the same as doing:
  932.      *
  933.      * <pre>
  934.      * return exactRef(Constants.HEAD).getTarget().getName()
  935.      * </pre>
  936.      *
  937.      * Except when HEAD is detached, in which case this method returns the
  938.      * current ObjectId in hexadecimal string format.
  939.      *
  940.      * @return name of current branch (for example {@code refs/heads/master}),
  941.      *         an ObjectId in hex format if the current branch is detached, or
  942.      *         {@code null} if the repository is corrupt and has no HEAD
  943.      *         reference.
  944.      * @throws java.io.IOException
  945.      */
  946.     @Nullable
  947.     public String getFullBranch() throws IOException {
  948.         Ref head = exactRef(Constants.HEAD);
  949.         if (head == null) {
  950.             return null;
  951.         }
  952.         if (head.isSymbolic()) {
  953.             return head.getTarget().getName();
  954.         }
  955.         ObjectId objectId = head.getObjectId();
  956.         if (objectId != null) {
  957.             return objectId.name();
  958.         }
  959.         return null;
  960.     }

  961.     /**
  962.      * Get the short name of the current branch that {@code HEAD} points to.
  963.      * <p>
  964.      * This is essentially the same as {@link #getFullBranch()}, except the
  965.      * leading prefix {@code refs/heads/} is removed from the reference before
  966.      * it is returned to the caller.
  967.      *
  968.      * @return name of current branch (for example {@code master}), an ObjectId
  969.      *         in hex format if the current branch is detached, or {@code null}
  970.      *         if the repository is corrupt and has no HEAD reference.
  971.      * @throws java.io.IOException
  972.      */
  973.     @Nullable
  974.     public String getBranch() throws IOException {
  975.         String name = getFullBranch();
  976.         if (name != null)
  977.             return shortenRefName(name);
  978.         return null;
  979.     }

  980.     /**
  981.      * Get the initial branch name of a new repository
  982.      *
  983.      * @return the initial branch name of a new repository
  984.      * @since 5.11
  985.      */
  986.     protected @NonNull String getInitialBranch() {
  987.         return initialBranch;
  988.     }

  989.     /**
  990.      * Objects known to exist but not expressed by {@link #getAllRefs()}.
  991.      * <p>
  992.      * When a repository borrows objects from another repository, it can
  993.      * advertise that it safely has that other repository's references, without
  994.      * exposing any other details about the other repository. This may help a
  995.      * client trying to push changes avoid pushing more than it needs to.
  996.      *
  997.      * @return unmodifiable collection of other known objects.
  998.      * @throws IOException
  999.      */
  1000.     @NonNull
  1001.     public Set<ObjectId> getAdditionalHaves() throws IOException {
  1002.         return Collections.emptySet();
  1003.     }

  1004.     /**
  1005.      * Get a ref by name.
  1006.      *
  1007.      * @param name
  1008.      *            the name of the ref to lookup. Must not be a short-hand
  1009.      *            form; e.g., "master" is not automatically expanded to
  1010.      *            "refs/heads/master".
  1011.      * @return the Ref with the given name, or {@code null} if it does not exist
  1012.      * @throws java.io.IOException
  1013.      * @since 4.2
  1014.      */
  1015.     @Nullable
  1016.     public final Ref exactRef(String name) throws IOException {
  1017.         return getRefDatabase().exactRef(name);
  1018.     }

  1019.     /**
  1020.      * Search for a ref by (possibly abbreviated) name.
  1021.      *
  1022.      * @param name
  1023.      *            the name of the ref to lookup. May be a short-hand form, e.g.
  1024.      *            "master" which is automatically expanded to
  1025.      *            "refs/heads/master" if "refs/heads/master" already exists.
  1026.      * @return the Ref with the given name, or {@code null} if it does not exist
  1027.      * @throws java.io.IOException
  1028.      * @since 4.2
  1029.      */
  1030.     @Nullable
  1031.     public final Ref findRef(String name) throws IOException {
  1032.         return getRefDatabase().findRef(name);
  1033.     }

  1034.     /**
  1035.      * Get mutable map of all known refs, including symrefs like HEAD that may
  1036.      * not point to any object yet.
  1037.      *
  1038.      * @return mutable map of all known refs (heads, tags, remotes).
  1039.      * @deprecated use {@code getRefDatabase().getRefs()} instead.
  1040.      */
  1041.     @Deprecated
  1042.     @NonNull
  1043.     public Map<String, Ref> getAllRefs() {
  1044.         try {
  1045.             return getRefDatabase().getRefs(RefDatabase.ALL);
  1046.         } catch (IOException e) {
  1047.             throw new UncheckedIOException(e);
  1048.         }
  1049.     }

  1050.     /**
  1051.      * Get mutable map of all tags
  1052.      *
  1053.      * @return mutable map of all tags; key is short tag name ("v1.0") and value
  1054.      *         of the entry contains the ref with the full tag name
  1055.      *         ("refs/tags/v1.0").
  1056.      * @deprecated use {@code getRefDatabase().getRefsByPrefix(R_TAGS)} instead
  1057.      */
  1058.     @Deprecated
  1059.     @NonNull
  1060.     public Map<String, Ref> getTags() {
  1061.         try {
  1062.             return getRefDatabase().getRefs(Constants.R_TAGS);
  1063.         } catch (IOException e) {
  1064.             throw new UncheckedIOException(e);
  1065.         }
  1066.     }

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

  1094.     /**
  1095.      * Get a map with all objects referenced by a peeled ref.
  1096.      *
  1097.      * @return a map with all objects referenced by a peeled ref.
  1098.      * @throws IOException
  1099.      */
  1100.     @NonNull
  1101.     public Map<AnyObjectId, Set<Ref>> getAllRefsByPeeledObjectId()
  1102.             throws IOException {
  1103.         List<Ref> allRefs = getRefDatabase().getRefs();
  1104.         Map<AnyObjectId, Set<Ref>> ret = new HashMap<>(allRefs.size());
  1105.         for (Ref ref : allRefs) {
  1106.             ref = peel(ref);
  1107.             AnyObjectId target = ref.getPeeledObjectId();
  1108.             if (target == null)
  1109.                 target = ref.getObjectId();
  1110.             // We assume most Sets here are singletons
  1111.             Set<Ref> oset = ret.put(target, Collections.singleton(ref));
  1112.             if (oset != null) {
  1113.                 // that was not the case (rare)
  1114.                 if (oset.size() == 1) {
  1115.                     // Was a read-only singleton, we must copy to a new Set
  1116.                     oset = new HashSet<>(oset);
  1117.                 }
  1118.                 ret.put(target, oset);
  1119.                 oset.add(ref);
  1120.             }
  1121.         }
  1122.         return ret;
  1123.     }

  1124.     /**
  1125.      * Get the index file location or {@code null} if repository isn't local.
  1126.      *
  1127.      * @return the index file location or {@code null} if repository isn't
  1128.      *         local.
  1129.      * @throws org.eclipse.jgit.errors.NoWorkTreeException
  1130.      *             if this is bare, which implies it has no working directory.
  1131.      *             See {@link #isBare()}.
  1132.      */
  1133.     @NonNull
  1134.     public File getIndexFile() throws NoWorkTreeException {
  1135.         if (isBare())
  1136.             throw new NoWorkTreeException();
  1137.         return indexFile;
  1138.     }

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

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

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

  1217.     /**
  1218.      * Get the repository state
  1219.      *
  1220.      * @return the repository state
  1221.      */
  1222.     @NonNull
  1223.     public RepositoryState getRepositoryState() {
  1224.         if (isBare() || getDirectory() == null)
  1225.             return RepositoryState.BARE;

  1226.         // Pre Git-1.6 logic
  1227.         if (new File(getWorkTree(), ".dotest").exists()) //$NON-NLS-1$
  1228.             return RepositoryState.REBASING;
  1229.         if (new File(getDirectory(), ".dotest-merge").exists()) //$NON-NLS-1$
  1230.             return RepositoryState.REBASING_INTERACTIVE;

  1231.         // From 1.6 onwards
  1232.         if (new File(getDirectory(),"rebase-apply/rebasing").exists()) //$NON-NLS-1$
  1233.             return RepositoryState.REBASING_REBASING;
  1234.         if (new File(getDirectory(),"rebase-apply/applying").exists()) //$NON-NLS-1$
  1235.             return RepositoryState.APPLY;
  1236.         if (new File(getDirectory(),"rebase-apply").exists()) //$NON-NLS-1$
  1237.             return RepositoryState.REBASING;

  1238.         if (new File(getDirectory(),"rebase-merge/interactive").exists()) //$NON-NLS-1$
  1239.             return RepositoryState.REBASING_INTERACTIVE;
  1240.         if (new File(getDirectory(),"rebase-merge").exists()) //$NON-NLS-1$
  1241.             return RepositoryState.REBASING_MERGE;

  1242.         // Both versions
  1243.         if (new File(getDirectory(), Constants.MERGE_HEAD).exists()) {
  1244.             // we are merging - now check whether we have unmerged paths
  1245.             try {
  1246.                 if (!readDirCache().hasUnmergedPaths()) {
  1247.                     // no unmerged paths -> return the MERGING_RESOLVED state
  1248.                     return RepositoryState.MERGING_RESOLVED;
  1249.                 }
  1250.             } catch (IOException e) {
  1251.                 throw new UncheckedIOException(e);
  1252.             }
  1253.             return RepositoryState.MERGING;
  1254.         }

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

  1257.         if (new File(getDirectory(), Constants.CHERRY_PICK_HEAD).exists()) {
  1258.             try {
  1259.                 if (!readDirCache().hasUnmergedPaths()) {
  1260.                     // no unmerged paths
  1261.                     return RepositoryState.CHERRY_PICKING_RESOLVED;
  1262.                 }
  1263.             } catch (IOException e) {
  1264.                 throw new UncheckedIOException(e);
  1265.             }

  1266.             return RepositoryState.CHERRY_PICKING;
  1267.         }

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

  1277.             return RepositoryState.REVERTING;
  1278.         }

  1279.         return RepositoryState.SAFE;
  1280.     }

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

  1299.         // Refs may be stored as loose files so invalid paths
  1300.         // on the local system must also be invalid refs.
  1301.         try {
  1302.             SystemReader.getInstance().checkPath(refName);
  1303.         } catch (CorruptObjectException e) {
  1304.             return false;
  1305.         }

  1306.         int components = 1;
  1307.         char p = '\0';
  1308.         for (int i = 0; i < len; i++) {
  1309.             final char c = refName.charAt(i);
  1310.             if (c <= ' ')
  1311.                 return false;
  1312.             switch (c) {
  1313.             case '.':
  1314.                 switch (p) {
  1315.                 case '\0': case '/': case '.':
  1316.                     return false;
  1317.                 }
  1318.                 if (i == len -1)
  1319.                     return false;
  1320.                 break;
  1321.             case '/':
  1322.                 if (i == 0 || i == len - 1)
  1323.                     return false;
  1324.                 if (p == '/')
  1325.                     return false;
  1326.                 components++;
  1327.                 break;
  1328.             case '{':
  1329.                 if (p == '@')
  1330.                     return false;
  1331.                 break;
  1332.             case '~': case '^': case ':':
  1333.             case '?': case '[': case '*':
  1334.             case '\\':
  1335.             case '\u007F':
  1336.                 return false;
  1337.             }
  1338.             p = c;
  1339.         }
  1340.         return components > 1;
  1341.     }

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

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

  1430.     /**
  1431.      * Strip work dir and return normalized repository path.
  1432.      *
  1433.      * @param workDir
  1434.      *            Work dir
  1435.      * @param file
  1436.      *            File whose path shall be stripped of its workdir
  1437.      * @return normalized repository relative path or the empty string if the
  1438.      *         file is not relative to the work directory.
  1439.      */
  1440.     @NonNull
  1441.     public static String stripWorkDir(File workDir, File file) {
  1442.         final String filePath = file.getPath();
  1443.         final String workDirPath = workDir.getPath();

  1444.         if (filePath.length() <= workDirPath.length()
  1445.                 || filePath.charAt(workDirPath.length()) != File.separatorChar
  1446.                 || !filePath.startsWith(workDirPath)) {
  1447.             File absWd = workDir.isAbsolute() ? workDir
  1448.                     : workDir.getAbsoluteFile();
  1449.             File absFile = file.isAbsolute() ? file : file.getAbsoluteFile();
  1450.             if (absWd.equals(workDir) && absFile.equals(file)) {
  1451.                 return ""; //$NON-NLS-1$
  1452.             }
  1453.             return stripWorkDir(absWd, absFile);
  1454.         }

  1455.         String relName = filePath.substring(workDirPath.length() + 1);
  1456.         if (File.separatorChar != '/') {
  1457.             relName = relName.replace(File.separatorChar, '/');
  1458.         }
  1459.         return relName;
  1460.     }

  1461.     /**
  1462.      * Whether this repository is bare
  1463.      *
  1464.      * @return true if this is bare, which implies it has no working directory.
  1465.      */
  1466.     public boolean isBare() {
  1467.         return workTree == null;
  1468.     }

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

  1485.     /**
  1486.      * Force a scan for changed refs. Fires an IndexChangedEvent(false) if
  1487.      * changes are detected.
  1488.      *
  1489.      * @throws java.io.IOException
  1490.      */
  1491.     public abstract void scanForRepoChanges() throws IOException;

  1492.     /**
  1493.      * Notify that the index changed by firing an IndexChangedEvent.
  1494.      *
  1495.      * @param internal
  1496.      *                     {@code true} if the index was changed by the same
  1497.      *                     JGit process
  1498.      * @since 5.0
  1499.      */
  1500.     public abstract void notifyIndexChanged(boolean internal);

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

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

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

  1558.     /**
  1559.      * Read the {@code GIT_DIR/description} file for gitweb.
  1560.      *
  1561.      * @return description text; null if no description has been configured.
  1562.      * @throws java.io.IOException
  1563.      *             description cannot be accessed.
  1564.      * @since 4.6
  1565.      */
  1566.     @Nullable
  1567.     public String getGitwebDescription() throws IOException {
  1568.         return null;
  1569.     }

  1570.     /**
  1571.      * Set the {@code GIT_DIR/description} file for gitweb.
  1572.      *
  1573.      * @param description
  1574.      *            new description; null to clear the description.
  1575.      * @throws java.io.IOException
  1576.      *             description cannot be persisted.
  1577.      * @since 4.6
  1578.      */
  1579.     public void setGitwebDescription(@Nullable String description)
  1580.             throws IOException {
  1581.         throw new IOException(JGitText.get().unsupportedRepositoryDescription);
  1582.     }

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

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

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

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

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

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

  1677.         byte[] raw = readGitDirectoryFile(Constants.MERGE_HEAD);
  1678.         if (raw == null)
  1679.             return null;

  1680.         LinkedList<ObjectId> heads = new LinkedList<>();
  1681.         for (int p = 0; p < raw.length;) {
  1682.             heads.add(ObjectId.fromString(raw, p));
  1683.             p = RawParseUtils
  1684.                     .nextLF(raw, p + Constants.OBJECT_ID_STRING_LENGTH);
  1685.         }
  1686.         return heads;
  1687.     }

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

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

  1718.         byte[] raw = readGitDirectoryFile(Constants.CHERRY_PICK_HEAD);
  1719.         if (raw == null)
  1720.             return null;

  1721.         return ObjectId.fromString(raw, 0);
  1722.     }

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

  1738.         byte[] raw = readGitDirectoryFile(Constants.REVERT_HEAD);
  1739.         if (raw == null)
  1740.             return null;
  1741.         return ObjectId.fromString(raw, 0);
  1742.     }

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

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

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

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

  1799.         byte[] raw = readGitDirectoryFile(Constants.ORIG_HEAD);
  1800.         return raw != null ? ObjectId.fromString(raw, 0) : null;
  1801.     }

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

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

  1833.     @Nullable
  1834.     private String readCommitMsgFile(String msgFilename) throws IOException {
  1835.         if (isBare() || getDirectory() == null)
  1836.             throw new NoWorkTreeException();

  1837.         File mergeMsgFile = new File(getDirectory(), msgFilename);
  1838.         try {
  1839.             return RawParseUtils.decode(IO.readFully(mergeMsgFile));
  1840.         } catch (FileNotFoundException e) {
  1841.             if (mergeMsgFile.exists()) {
  1842.                 throw e;
  1843.             }
  1844.             // the file has disappeared in the meantime ignore it
  1845.             return null;
  1846.         }
  1847.     }

  1848.     private void writeCommitMsg(File msgFile, String msg) throws IOException {
  1849.         if (msg != null) {
  1850.             try (FileOutputStream fos = new FileOutputStream(msgFile)) {
  1851.                 fos.write(msg.getBytes(UTF_8));
  1852.             }
  1853.         } else {
  1854.             FileUtils.delete(msgFile, FileUtils.SKIP_MISSING);
  1855.         }
  1856.     }

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

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

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

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

  1940.     /**
  1941.      * Get the names of all known remotes
  1942.      *
  1943.      * @return the names of all known remotes
  1944.      * @since 3.4
  1945.      */
  1946.     @NonNull
  1947.     public Set<String> getRemoteNames() {
  1948.         return getConfig()
  1949.                 .getSubsections(ConfigConstants.CONFIG_REMOTE_SECTION);
  1950.     }

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