Reftable.java

  1. /*
  2.  * Copyright (C) 2017, Google Inc. and others
  3.  *
  4.  * This program and the accompanying materials are made available under the
  5.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  6.  * https://www.eclipse.org/org/documents/edl-v10.php.
  7.  *
  8.  * SPDX-License-Identifier: BSD-3-Clause
  9.  */

  10. package org.eclipse.jgit.internal.storage.reftable;

  11. import static org.eclipse.jgit.lib.RefDatabase.MAX_SYMBOLIC_REF_DEPTH;

  12. import java.io.ByteArrayOutputStream;
  13. import java.io.IOException;
  14. import java.util.Collection;

  15. import org.eclipse.jgit.annotations.Nullable;
  16. import org.eclipse.jgit.internal.storage.io.BlockSource;
  17. import org.eclipse.jgit.lib.AnyObjectId;
  18. import org.eclipse.jgit.lib.Ref;
  19. import org.eclipse.jgit.lib.SymbolicRef;

  20. /**
  21.  * Abstract table of references.
  22.  */
  23. public abstract class Reftable {
  24.     /**
  25.      * References to convert into a reftable
  26.      *
  27.      * @param refs
  28.      *            references to convert into a reftable; may be empty.
  29.      * @return a reader for the supplied references.
  30.      */
  31.     public static Reftable from(Collection<Ref> refs) {
  32.         try {
  33.             ReftableConfig cfg = new ReftableConfig();
  34.             cfg.setIndexObjects(false);
  35.             cfg.setAlignBlocks(false);
  36.             ByteArrayOutputStream buf = new ByteArrayOutputStream();
  37.             new ReftableWriter(buf)
  38.                 .setConfig(cfg)
  39.                 .begin()
  40.                 .sortAndWriteRefs(refs)
  41.                 .finish();
  42.             return new ReftableReader(BlockSource.from(buf.toByteArray()));
  43.         } catch (IOException e) {
  44.             throw new RuntimeException(e);
  45.         }
  46.     }

  47.     /** {@code true} if deletions should be included in results. */
  48.     protected boolean includeDeletes;

  49.     /**
  50.      * Whether deleted references will be returned.
  51.      *
  52.      * @param deletes
  53.      *            if {@code true} deleted references will be returned. If
  54.      *            {@code false} (default behavior), deleted references will be
  55.      *            skipped, and not returned.
  56.      */
  57.     public void setIncludeDeletes(boolean deletes) {
  58.         includeDeletes = deletes;
  59.     }

  60.     /**
  61.      * Get the maximum update index for ref entries that appear in this
  62.      * reftable.
  63.      *
  64.      * @return the maximum update index for ref entries that appear in this
  65.      *         reftable.
  66.      * @throws java.io.IOException
  67.      *             file cannot be read.
  68.      */
  69.     public abstract long maxUpdateIndex() throws IOException;

  70.     /**
  71.      * Get the minimum update index for ref entries that appear in this
  72.      * reftable.
  73.      *
  74.      * @return the minimum update index for ref entries that appear in this
  75.      *         reftable.
  76.      * @throws java.io.IOException
  77.      *             file cannot be read.
  78.      */
  79.     public abstract long minUpdateIndex() throws IOException;

  80.     /**
  81.      * Seek to the first reference, to iterate in order.
  82.      *
  83.      * @return cursor to iterate.
  84.      * @throws java.io.IOException
  85.      *             if references cannot be read.
  86.      */
  87.     public abstract RefCursor allRefs() throws IOException;

  88.     /**
  89.      * Seek to a reference.
  90.      * <p>
  91.      * This method will seek to the reference {@code refName}. If present, the
  92.      * returned cursor will iterate exactly one entry. If not found, an empty
  93.      * cursor is returned.
  94.      *
  95.      * @param refName
  96.      *            reference name.
  97.      * @return cursor to iterate; empty cursor if no references match.
  98.      * @throws java.io.IOException
  99.      *             if references cannot be read.
  100.      */
  101.     public abstract RefCursor seekRef(String refName) throws IOException;

  102.     /**
  103.      * Seek references with prefix.
  104.      * <p>
  105.      * The method will seek all the references starting with {@code prefix} as a
  106.      * prefix. If no references start with this prefix, an empty cursor is
  107.      * returned.
  108.      *
  109.      * @param prefix
  110.      *            prefix to find.
  111.      * @return cursor to iterate; empty cursor if no references match.
  112.      * @throws java.io.IOException
  113.      *             if references cannot be read.
  114.      */
  115.     public abstract RefCursor seekRefsWithPrefix(String prefix) throws IOException;

  116.     /**
  117.      * Match references pointing to a specific object.
  118.      *
  119.      * @param id
  120.      *            object to find.
  121.      * @return cursor to iterate; empty cursor if no references match.
  122.      * @throws java.io.IOException
  123.      *             if references cannot be read.
  124.      */
  125.     public abstract RefCursor byObjectId(AnyObjectId id) throws IOException;

  126.     /**
  127.      * @return whether this reftable can do a fast SHA1 => ref lookup.
  128.      * @throws IOException on I/O problems.
  129.      */
  130.     public abstract boolean hasObjectMap() throws IOException;

  131.     /**
  132.      * Seek reader to read log records.
  133.      *
  134.      * @return cursor to iterate; empty cursor if no logs are present.
  135.      * @throws java.io.IOException
  136.      *             if logs cannot be read.
  137.      */
  138.     public abstract LogCursor allLogs() throws IOException;

  139.     /**
  140.      * Read a single reference's log.
  141.      *
  142.      * @param refName
  143.      *            exact name of the reference whose log to read.
  144.      * @return cursor to iterate; empty cursor if no logs match.
  145.      * @throws java.io.IOException
  146.      *             if logs cannot be read.
  147.      */
  148.     public LogCursor seekLog(String refName) throws IOException {
  149.         return seekLog(refName, Long.MAX_VALUE);
  150.     }

  151.     /**
  152.      * Seek to an update index in a reference's log.
  153.      *
  154.      * @param refName
  155.      *            exact name of the reference whose log to read.
  156.      * @param updateIndex
  157.      *            most recent index to return first in the log cursor. Log
  158.      *            records at or before {@code updateIndex} will be returned.
  159.      * @return cursor to iterate; empty cursor if no logs match.
  160.      * @throws java.io.IOException
  161.      *             if logs cannot be read.
  162.      */
  163.     public abstract LogCursor seekLog(String refName, long updateIndex)
  164.             throws IOException;

  165.     /**
  166.      * Lookup a reference, or null if not found.
  167.      *
  168.      * @param refName
  169.      *            reference name to find.
  170.      * @return the reference, or {@code null} if not found.
  171.      * @throws java.io.IOException
  172.      *             if references cannot be read.
  173.      */
  174.     @Nullable
  175.     public Ref exactRef(String refName) throws IOException {
  176.         try (RefCursor rc = seekRef(refName)) {
  177.             return rc.next() ? rc.getRef() : null;
  178.         }
  179.     }

  180.     /**
  181.      * Test if a reference exists.
  182.      *
  183.      * @param refName
  184.      *            reference name or subtree to find.
  185.      * @return {@code true} if the reference exists.
  186.      * @throws java.io.IOException
  187.      *             if references cannot be read.
  188.      */
  189.     public boolean hasRef(String refName) throws IOException {
  190.         try (RefCursor rc = seekRef(refName)) {
  191.             return rc.next();
  192.         }
  193.     }

  194.     /**
  195.      * Test if any reference starts with {@code prefix} as a prefix.
  196.      *
  197.      * @param prefix
  198.      *            prefix to find.
  199.      * @return {@code true} if at least one reference exists with prefix.
  200.      * @throws java.io.IOException
  201.      *             if references cannot be read.
  202.      */
  203.     public boolean hasRefsWithPrefix(String prefix) throws IOException {
  204.         try (RefCursor rc = seekRefsWithPrefix(prefix)) {
  205.             return rc.next();
  206.         }
  207.     }

  208.     /**
  209.      * Test if any reference directly refers to the object.
  210.      *
  211.      * @param id
  212.      *            ObjectId to find.
  213.      * @return {@code true} if any reference exists directly referencing
  214.      *         {@code id}, or a annotated tag that peels to {@code id}.
  215.      * @throws java.io.IOException
  216.      *             if references cannot be read.
  217.      */
  218.     public boolean hasId(AnyObjectId id) throws IOException {
  219.         try (RefCursor rc = byObjectId(id)) {
  220.             return rc.next();
  221.         }
  222.     }

  223.     /**
  224.      * Resolve a symbolic reference to populate its value.
  225.      *
  226.      * @param symref
  227.      *            reference to resolve.
  228.      * @return resolved {@code symref}, or {@code null}.
  229.      * @throws java.io.IOException
  230.      *             if references cannot be read.
  231.      */
  232.     @Nullable
  233.     public Ref resolve(Ref symref) throws IOException {
  234.         return resolve(symref, 0);
  235.     }

  236.     private Ref resolve(Ref ref, int depth) throws IOException {
  237.         if (!ref.isSymbolic()) {
  238.             return ref;
  239.         }

  240.         Ref dst = ref.getTarget();
  241.         if (MAX_SYMBOLIC_REF_DEPTH <= depth) {
  242.             return null; // claim it doesn't exist
  243.         }

  244.         dst = exactRef(dst.getName());
  245.         if (dst == null) {
  246.             return ref;
  247.         }

  248.         dst = resolve(dst, depth + 1);
  249.         if (dst == null) {
  250.             return null; // claim it doesn't exist
  251.         }
  252.         return new SymbolicRef(ref.getName(), dst, ref.getUpdateIndex());
  253.     }
  254. }