AbbrevConfig.java

  1. /*
  2.  * Copyright (C) 2022,  Matthias Sohn <matthias.sohn@sap.com> 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.lib;

  11. import static org.eclipse.jgit.lib.Constants.OBJECT_ID_ABBREV_STRING_LENGTH;
  12. import static org.eclipse.jgit.lib.TypedConfigGetter.UNSET_INT;

  13. import java.text.MessageFormat;

  14. import org.eclipse.jgit.api.errors.InvalidConfigurationException;
  15. import org.eclipse.jgit.internal.JGitText;

  16. /**
  17.  * Git configuration option <a
  18.  * href=https://git-scm.com/docs/git-config#Documentation/git-config.txt-coreabbrev">
  19.  * core.abbrev</a>
  20.  *
  21.  * @since 6.1
  22.  */
  23. public final class AbbrevConfig {
  24.     private static final String VALUE_NO = "no"; //$NON-NLS-1$

  25.     private static final String VALUE_AUTO = "auto"; //$NON-NLS-1$

  26.     /**
  27.      * The minimum value of abbrev
  28.      */
  29.     public static final int MIN_ABBREV = 4;

  30.     /**
  31.      * Cap configured core.abbrev to range between minimum of 4 and number of
  32.      * hex-digits of a full object id.
  33.      *
  34.      * @param len
  35.      *            configured number of hex-digits to abbreviate object ids to
  36.      * @return core.abbrev capped to range between minimum of 4 and number of
  37.      *         hex-digits of a full object id
  38.      */
  39.     public static int capAbbrev(int len) {
  40.         return Math.min(Math.max(MIN_ABBREV, len),
  41.                 Constants.OBJECT_ID_STRING_LENGTH);
  42.     }

  43.     /**
  44.      * No abbreviation
  45.      */
  46.     public final static AbbrevConfig NO = new AbbrevConfig(
  47.             Constants.OBJECT_ID_STRING_LENGTH);

  48.     /**
  49.      * Parse string value of core.abbrev git option for a given repository
  50.      *
  51.      * @param repo
  52.      *            repository
  53.      * @return the parsed AbbrevConfig
  54.      * @throws InvalidConfigurationException
  55.      *             if value of core.abbrev is invalid
  56.      */
  57.     public static AbbrevConfig parseFromConfig(Repository repo)
  58.             throws InvalidConfigurationException {
  59.         Config config = repo.getConfig();
  60.         String value = config.getString(ConfigConstants.CONFIG_CORE_SECTION,
  61.                 null, ConfigConstants.CONFIG_KEY_ABBREV);
  62.         if (value == null || value.equalsIgnoreCase(VALUE_AUTO)) {
  63.             return auto(repo);
  64.         }
  65.         if (value.equalsIgnoreCase(VALUE_NO)) {
  66.             return NO;
  67.         }
  68.         try {
  69.             int len = config.getIntInRange(ConfigConstants.CONFIG_CORE_SECTION,
  70.                     ConfigConstants.CONFIG_KEY_ABBREV, MIN_ABBREV,
  71.                     Constants.OBJECT_ID_STRING_LENGTH, UNSET_INT);
  72.             if (len == UNSET_INT) {
  73.                 // Unset was checked above. If we get UNSET_INT here, then
  74.                 // either the value was UNSET_INT, or it was an invalid value
  75.                 // (not an integer, or out of range), and EGit's
  76.                 // ReportingTypedGetter caught the exception and has logged a
  77.                 // warning. In either case we should fall back to some sane
  78.                 // default.
  79.                 len = OBJECT_ID_ABBREV_STRING_LENGTH;
  80.             }
  81.             return new AbbrevConfig(len);
  82.         } catch (IllegalArgumentException e) {
  83.             throw new InvalidConfigurationException(MessageFormat
  84.                     .format(JGitText.get().invalidCoreAbbrev, value), e);
  85.         }
  86.     }

  87.     /**
  88.      * An appropriate value is computed based on the approximate number of
  89.      * packed objects in a repository, which hopefully is enough for abbreviated
  90.      * object names to stay unique for some time.
  91.      *
  92.      * @param repo
  93.      * @return appropriate value computed based on the approximate number of
  94.      *         packed objects in a repository
  95.      */
  96.     private static AbbrevConfig auto(Repository repo) {
  97.         long count = repo.getObjectDatabase().getApproximateObjectCount();
  98.         if (count == -1) {
  99.             return new AbbrevConfig(OBJECT_ID_ABBREV_STRING_LENGTH);
  100.         }
  101.         // find msb, round to next power of 2
  102.         int len = 63 - Long.numberOfLeadingZeros(count) + 1;
  103.         // With the order of 2^len objects, we expect a collision at
  104.         // 2^(len/2). But we also care about hex chars, not bits, and
  105.         // there are 4 bits per hex. So all together we need to divide
  106.         // by 2; but we also want to round odd numbers up, hence adding
  107.         // one before dividing.
  108.         len = (len + 1) / 2;
  109.         // for small repos use at least fallback length
  110.         return new AbbrevConfig(Math.max(len, OBJECT_ID_ABBREV_STRING_LENGTH));
  111.     }

  112.     /**
  113.      * All other possible abbreviation lengths. Valid range 4 to number of
  114.      * hex-digits of an unabbreviated object id (40 for SHA1 object ids, jgit
  115.      * doesn't support SHA256 yet).
  116.      */
  117.     private int abbrev;

  118.     /**
  119.      * @param abbrev
  120.      */
  121.     private AbbrevConfig(int abbrev) {
  122.         this.abbrev = capAbbrev(abbrev);
  123.     }

  124.     /**
  125.      * Get the configured abbreviation length for object ids.
  126.      *
  127.      * @return the configured abbreviation length for object ids
  128.      */
  129.     public int get() {
  130.         return abbrev;
  131.     }

  132.     @Override
  133.     public String toString() {
  134.         return Integer.toString(abbrev);
  135.     }
  136. }