aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbbrevConfig.java
blob: 9109cfd769de4354ed56afabe4c19ee245d5758d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/*
 * Copyright (C) 2022,  Matthias Sohn <matthias.sohn@sap.com> and others
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Distribution License v. 1.0 which is available at
 * https://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
package org.eclipse.jgit.lib;

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

import java.text.MessageFormat;

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

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

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

	/**
	 * The minimum value of abbrev
	 */
	public static final int MIN_ABBREV = 4;

	/**
	 * Cap configured core.abbrev to range between minimum of 4 and number of
	 * hex-digits of a full object id.
	 *
	 * @param len
	 *            configured number of hex-digits to abbreviate object ids to
	 * @return core.abbrev capped to range between minimum of 4 and number of
	 *         hex-digits of a full object id
	 */
	public static int capAbbrev(int len) {
		return Math.min(Math.max(MIN_ABBREV, len),
				Constants.OBJECT_ID_STRING_LENGTH);
	}

	/**
	 * No abbreviation
	 */
	public final static AbbrevConfig NO = new AbbrevConfig(
			Constants.OBJECT_ID_STRING_LENGTH);

	/**
	 * Parse string value of core.abbrev git option for a given repository
	 *
	 * @param repo
	 *            repository
	 * @return the parsed AbbrevConfig
	 * @throws InvalidConfigurationException
	 *             if value of core.abbrev is invalid
	 */
	public static AbbrevConfig parseFromConfig(Repository repo)
			throws InvalidConfigurationException {
		Config config = repo.getConfig();
		String value = config.getString(ConfigConstants.CONFIG_CORE_SECTION,
				null, ConfigConstants.CONFIG_KEY_ABBREV);
		if (value == null || value.equalsIgnoreCase(VALUE_AUTO)) {
			return auto(repo);
		}
		if (value.equalsIgnoreCase(VALUE_NO)) {
			return NO;
		}
		try {
			int len = config.getIntInRange(ConfigConstants.CONFIG_CORE_SECTION,
					ConfigConstants.CONFIG_KEY_ABBREV, MIN_ABBREV,
					Constants.OBJECT_ID_STRING_LENGTH, UNSET_INT);
			if (len == UNSET_INT) {
				// Unset was checked above. If we get UNSET_INT here, then
				// either the value was UNSET_INT, or it was an invalid value
				// (not an integer, or out of range), and EGit's
				// ReportingTypedGetter caught the exception and has logged a
				// warning. In either case we should fall back to some sane
				// default.
				len = OBJECT_ID_ABBREV_STRING_LENGTH;
			}
			return new AbbrevConfig(len);
		} catch (IllegalArgumentException e) {
			throw new InvalidConfigurationException(MessageFormat
					.format(JGitText.get().invalidCoreAbbrev, value), e);
		}
	}

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

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

	/**
	 * @param abbrev
	 */
	private AbbrevConfig(int abbrev) {
		this.abbrev = capAbbrev(abbrev);
	}

	/**
	 * Get the configured abbreviation length for object ids.
	 *
	 * @return the configured abbreviation length for object ids
	 */
	public int get() {
		return abbrev;
	}

	@Override
	public String toString() {
		return Integer.toString(abbrev);
	}
}