summaryrefslogtreecommitdiffstats
path: root/java/com/google/gerrit/reviewdb/client/RefNames.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/google/gerrit/reviewdb/client/RefNames.java')
-rw-r--r--java/com/google/gerrit/reviewdb/client/RefNames.java443
1 files changed, 443 insertions, 0 deletions
diff --git a/java/com/google/gerrit/reviewdb/client/RefNames.java b/java/com/google/gerrit/reviewdb/client/RefNames.java
new file mode 100644
index 0000000000..fd2fb56b0e
--- /dev/null
+++ b/java/com/google/gerrit/reviewdb/client/RefNames.java
@@ -0,0 +1,443 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.reviewdb.client;
+
+/** Constants and utilities for Gerrit-specific ref names. */
+public class RefNames {
+ public static final String HEAD = "HEAD";
+
+ public static final String REFS = "refs/";
+
+ public static final String REFS_HEADS = "refs/heads/";
+
+ public static final String REFS_TAGS = "refs/tags/";
+
+ public static final String REFS_CHANGES = "refs/changes/";
+
+ public static final String REFS_META = "refs/meta/";
+
+ /** Note tree listing commits we refuse {@code refs/meta/reject-commits} */
+ public static final String REFS_REJECT_COMMITS = "refs/meta/reject-commits";
+
+ /** Configuration settings for a project {@code refs/meta/config} */
+ public static final String REFS_CONFIG = "refs/meta/config";
+
+ /** Note tree listing external IDs */
+ public static final String REFS_EXTERNAL_IDS = "refs/meta/external-ids";
+
+ /** Magic user branch in All-Users {@code refs/users/self} */
+ public static final String REFS_USERS_SELF = "refs/users/self";
+
+ /** Default user preference settings */
+ public static final String REFS_USERS_DEFAULT = RefNames.REFS_USERS + "default";
+
+ /** Configurations of project-specific dashboards (canned search queries). */
+ public static final String REFS_DASHBOARDS = "refs/meta/dashboards/";
+
+ /** Sequence counters in NoteDb. */
+ public static final String REFS_SEQUENCES = "refs/sequences/";
+
+ /**
+ * Prefix applied to merge commit base nodes.
+ *
+ * <p>References in this directory should take the form {@code refs/cache-automerge/xx/yyyy...}
+ * where xx is the first two digits of the merge commit's object name, and yyyyy... is the
+ * remaining 38. The reference should point to a treeish that is the automatic merge result of the
+ * merge commit's parents.
+ */
+ public static final String REFS_CACHE_AUTOMERGE = "refs/cache-automerge/";
+
+ /** Suffix of a meta ref in the NoteDb. */
+ public static final String META_SUFFIX = "/meta";
+
+ /** Suffix of a ref that stores robot comments in the NoteDb. */
+ public static final String ROBOT_COMMENTS_SUFFIX = "/robot-comments";
+
+ public static final String EDIT_PREFIX = "edit-";
+
+ /*
+ * The following refs contain an account ID and should be visible only to that account.
+ *
+ * Parsing the account ID from the ref is implemented in Account.Id#fromRef(String). This ensures
+ * that VisibleRefFilter hides those refs from other users.
+ *
+ * This applies to:
+ * - User branches (e.g. 'refs/users/23/1011123')
+ * - Draft comment refs (e.g. 'refs/draft-comments/73/67473/1011123')
+ * - Starred changes refs (e.g. 'refs/starred-changes/73/67473/1011123')
+ */
+
+ /** Preference settings for a user {@code refs/users} */
+ public static final String REFS_USERS = "refs/users/";
+
+ /** NoteDb ref for a group {@code refs/groups} */
+ public static final String REFS_GROUPS = "refs/groups/";
+
+ /** NoteDb ref for the NoteMap of all group names */
+ public static final String REFS_GROUPNAMES = "refs/meta/group-names";
+
+ /**
+ * NoteDb ref for deleted groups {@code refs/deleted-groups}. This ref namespace is foreseen as an
+ * attic for deleted groups (it's reserved but not used yet)
+ */
+ public static final String REFS_DELETED_GROUPS = "refs/deleted-groups/";
+
+ /** Draft inline comments of a user on a change */
+ public static final String REFS_DRAFT_COMMENTS = "refs/draft-comments/";
+
+ /** A change starred by a user */
+ public static final String REFS_STARRED_CHANGES = "refs/starred-changes/";
+
+ public static String fullName(String ref) {
+ return (ref.startsWith(REFS) || ref.equals(HEAD)) ? ref : REFS_HEADS + ref;
+ }
+
+ public static final String shortName(String ref) {
+ if (ref.startsWith(REFS_HEADS)) {
+ return ref.substring(REFS_HEADS.length());
+ } else if (ref.startsWith(REFS_TAGS)) {
+ return ref.substring(REFS_TAGS.length());
+ }
+ return ref;
+ }
+
+ public static String changeMetaRef(Change.Id id) {
+ StringBuilder r = newStringBuilder().append(REFS_CHANGES);
+ return shard(id.get(), r).append(META_SUFFIX).toString();
+ }
+
+ public static String robotCommentsRef(Change.Id id) {
+ StringBuilder r = newStringBuilder().append(REFS_CHANGES);
+ return shard(id.get(), r).append(ROBOT_COMMENTS_SUFFIX).toString();
+ }
+
+ public static boolean isNoteDbMetaRef(String ref) {
+ if (ref.startsWith(REFS_CHANGES)
+ && (ref.endsWith(META_SUFFIX) || ref.endsWith(ROBOT_COMMENTS_SUFFIX))) {
+ return true;
+ }
+ if (ref.startsWith(REFS_DRAFT_COMMENTS) || ref.startsWith(REFS_STARRED_CHANGES)) {
+ return true;
+ }
+ return false;
+ }
+
+ public static String refsGroups(AccountGroup.UUID groupUuid) {
+ return REFS_GROUPS + shardUuid(groupUuid.get());
+ }
+
+ public static String refsDeletedGroups(AccountGroup.UUID groupUuid) {
+ return REFS_DELETED_GROUPS + shardUuid(groupUuid.get());
+ }
+
+ public static String refsUsers(Account.Id accountId) {
+ StringBuilder r = newStringBuilder().append(REFS_USERS);
+ return shard(accountId.get(), r).toString();
+ }
+
+ public static String refsDraftComments(Change.Id changeId, Account.Id accountId) {
+ return buildRefsPrefix(REFS_DRAFT_COMMENTS, changeId.get()).append(accountId.get()).toString();
+ }
+
+ public static String refsDraftCommentsPrefix(Change.Id changeId) {
+ return buildRefsPrefix(REFS_DRAFT_COMMENTS, changeId.get()).toString();
+ }
+
+ public static String refsStarredChanges(Change.Id changeId, Account.Id accountId) {
+ return buildRefsPrefix(REFS_STARRED_CHANGES, changeId.get()).append(accountId.get()).toString();
+ }
+
+ public static String refsStarredChangesPrefix(Change.Id changeId) {
+ return buildRefsPrefix(REFS_STARRED_CHANGES, changeId.get()).toString();
+ }
+
+ private static StringBuilder buildRefsPrefix(String prefix, int id) {
+ StringBuilder r = newStringBuilder().append(prefix);
+ return shard(id, r).append('/');
+ }
+
+ public static String refsCacheAutomerge(String hash) {
+ return REFS_CACHE_AUTOMERGE + hash.substring(0, 2) + '/' + hash.substring(2);
+ }
+
+ public static String shard(int id) {
+ if (id < 0) {
+ return null;
+ }
+ return shard(id, newStringBuilder()).toString();
+ }
+
+ private static StringBuilder shard(int id, StringBuilder sb) {
+ int n = id % 100;
+ if (n < 10) {
+ sb.append('0');
+ }
+ sb.append(n);
+ sb.append('/');
+ sb.append(id);
+ return sb;
+ }
+
+ private static String shardUuid(String uuid) {
+ if (uuid == null || uuid.length() < 2) {
+ throw new IllegalArgumentException("UUIDs must consist of at least two characters");
+ }
+ return uuid.substring(0, 2) + '/' + uuid;
+ }
+
+ /**
+ * Returns reference for this change edit with sharded user and change number:
+ * refs/users/UU/UUUU/edit-CCCC/P.
+ *
+ * @param accountId account id
+ * @param changeId change number
+ * @param psId patch set number
+ * @return reference for this change edit
+ */
+ public static String refsEdit(Account.Id accountId, Change.Id changeId, PatchSet.Id psId) {
+ return refsEditPrefix(accountId, changeId) + psId.get();
+ }
+
+ /**
+ * Returns reference prefix for this change edit with sharded user and change number:
+ * refs/users/UU/UUUU/edit-CCCC/.
+ *
+ * @param accountId account id
+ * @param changeId change number
+ * @return reference prefix for this change edit
+ */
+ public static String refsEditPrefix(Account.Id accountId, Change.Id changeId) {
+ return refsEditPrefix(accountId) + changeId.get() + '/';
+ }
+
+ public static String refsEditPrefix(Account.Id accountId) {
+ return refsUsers(accountId) + '/' + EDIT_PREFIX;
+ }
+
+ public static boolean isRefsEdit(String ref) {
+ return ref != null && ref.startsWith(REFS_USERS) && ref.contains(EDIT_PREFIX);
+ }
+
+ public static boolean isRefsUsers(String ref) {
+ return ref.startsWith(REFS_USERS);
+ }
+
+ /**
+ * Whether the ref is a group branch that stores NoteDb data of a group. Returns {@code true} for
+ * all refs that start with {@code refs/groups/}.
+ */
+ public static boolean isRefsGroups(String ref) {
+ return ref.startsWith(REFS_GROUPS);
+ }
+
+ /**
+ * Whether the ref is a group branch that stores NoteDb data of a deleted group. Returns {@code
+ * true} for all refs that start with {@code refs/deleted-groups/}.
+ */
+ public static boolean isRefsDeletedGroups(String ref) {
+ return ref.startsWith(REFS_DELETED_GROUPS);
+ }
+
+ /**
+ * Whether the ref is used for storing group data in NoteDb. Returns {@code true} for all group
+ * branches, refs/meta/group-names and deleted group branches.
+ */
+ public static boolean isGroupRef(String ref) {
+ return isRefsGroups(ref) || isRefsDeletedGroups(ref) || REFS_GROUPNAMES.equals(ref);
+ }
+
+ /** Whether the ref is the configuration branch, i.e. {@code refs/meta/config}, for a project. */
+ public static boolean isConfigRef(String ref) {
+ return REFS_CONFIG.equals(ref);
+ }
+
+ static Integer parseShardedRefPart(String name) {
+ if (name == null) {
+ return null;
+ }
+
+ String[] parts = name.split("/");
+ int n = parts.length;
+ if (n < 2) {
+ return null;
+ }
+
+ // Last 2 digits.
+ int le;
+ for (le = 0; le < parts[0].length(); le++) {
+ if (!Character.isDigit(parts[0].charAt(le))) {
+ return null;
+ }
+ }
+ if (le != 2) {
+ return null;
+ }
+
+ // Full ID.
+ int ie;
+ for (ie = 0; ie < parts[1].length(); ie++) {
+ if (!Character.isDigit(parts[1].charAt(ie))) {
+ if (ie == 0) {
+ return null;
+ }
+ break;
+ }
+ }
+
+ int shard = Integer.parseInt(parts[0]);
+ int id = Integer.parseInt(parts[1].substring(0, ie));
+
+ if (id % 100 != shard) {
+ return null;
+ }
+ return id;
+ }
+
+ static String parseShardedUuidFromRefPart(String name) {
+ if (name == null) {
+ return null;
+ }
+
+ String[] parts = name.split("/");
+ int n = parts.length;
+ if (n != 2) {
+ return null;
+ }
+
+ // First 2 chars.
+ if (parts[0].length() != 2) {
+ return null;
+ }
+
+ // Full UUID.
+ String uuid = parts[1];
+ if (!uuid.startsWith(parts[0])) {
+ return null;
+ }
+
+ return uuid;
+ }
+
+ /**
+ * Skips a sharded ref part at the beginning of the name.
+ *
+ * <p>E.g.: "01/1" -> "", "01/1/" -> "/", "01/1/2" -> "/2", "01/1-edit" -> "-edit"
+ *
+ * @param name ref part name
+ * @return the rest of the name, {@code null} if the ref name part doesn't start with a valid
+ * sharded ID
+ */
+ static String skipShardedRefPart(String name) {
+ if (name == null) {
+ return null;
+ }
+
+ String[] parts = name.split("/");
+ int n = parts.length;
+ if (n < 2) {
+ return null;
+ }
+
+ // Last 2 digits.
+ int le;
+ for (le = 0; le < parts[0].length(); le++) {
+ if (!Character.isDigit(parts[0].charAt(le))) {
+ return null;
+ }
+ }
+ if (le != 2) {
+ return null;
+ }
+
+ // Full ID.
+ int ie;
+ for (ie = 0; ie < parts[1].length(); ie++) {
+ if (!Character.isDigit(parts[1].charAt(ie))) {
+ if (ie == 0) {
+ return null;
+ }
+ break;
+ }
+ }
+
+ int shard = Integer.parseInt(parts[0]);
+ int id = Integer.parseInt(parts[1].substring(0, ie));
+
+ if (id % 100 != shard) {
+ return null;
+ }
+
+ return name.substring(2 + 1 + ie); // 2 for the length of the shard, 1 for the '/'
+ }
+
+ /**
+ * Parses an ID that follows a sharded ref part at the beginning of the name.
+ *
+ * <p>E.g.: "01/1/2" -> 2, "01/1/2/4" -> 2, ""01/1/2-edit" -> 2
+ *
+ * @param name ref part name
+ * @return ID that follows the sharded ref part at the beginning of the name, {@code null} if the
+ * ref name part doesn't start with a valid sharded ID or if no valid ID follows the sharded
+ * ref part
+ */
+ static Integer parseAfterShardedRefPart(String name) {
+ String rest = skipShardedRefPart(name);
+ if (rest == null || !rest.startsWith("/")) {
+ return null;
+ }
+
+ rest = rest.substring(1);
+
+ int ie;
+ for (ie = 0; ie < rest.length(); ie++) {
+ if (!Character.isDigit(rest.charAt(ie))) {
+ break;
+ }
+ }
+ if (ie == 0) {
+ return null;
+ }
+ return Integer.parseInt(rest.substring(0, ie));
+ }
+
+ static Integer parseRefSuffix(String name) {
+ if (name == null) {
+ return null;
+ }
+ int i = name.length();
+ while (i > 0) {
+ char c = name.charAt(i - 1);
+ if (c == '/') {
+ break;
+ } else if (!Character.isDigit(c)) {
+ return null;
+ }
+ i--;
+ }
+ if (i == 0) {
+ return null;
+ }
+ return Integer.valueOf(name.substring(i, name.length()));
+ }
+
+ private static StringBuilder newStringBuilder() {
+ // Many refname types in this file are always are longer than the default of 16 chars, so
+ // presize StringBuilders larger by default. This hurts readability less than accurate
+ // calculations would, at a negligible cost to memory overhead.
+ return new StringBuilder(64);
+ }
+
+ private RefNames() {}
+}