/* Copyright (c) 2012-2014 Boundless and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Distribution License v1.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/org/documents/edl-v10.html * * Contributors: * Gabriel Roldan (Boundless) - initial implementation */ package org.locationtech.geogig.api; import com.google.common.base.Preconditions; /** * Pairing of a name and the {@link ObjectId} it currently has. * <p> * A ref in Git is (more or less) a variable that holds a single object identifier. The object * identifier can be any valid Git object (blob, tree, commit, annotated tag, ...). * <p> * The ref name has the attributes of the ref that was asked for as well as the ref it was resolved * to for symbolic refs plus the object id it points to and (for tags) the peeled target object id, * i.e. the tag resolved recursively until a non-tag object is referenced. */ public class Ref implements Comparable<Ref> { /** * By convention, name of the main branch */ public static final String MASTER = "refs/heads/master"; /** * Pointer to the latest commit in the current branch */ public static final String HEAD = "HEAD"; /** * Pointer to the current tree in the staging index */ public static final String STAGE_HEAD = "STAGE_HEAD"; /** * Pointer to the current tree in the working directory */ public static final String WORK_HEAD = "WORK_HEAD"; /** * Pointer to the commit to be merged during a merge operation */ public static final String MERGE_HEAD = "MERGE_HEAD"; /** * Pointer to the commit to be picked during a cherry-pick operation */ public static final String CHERRY_PICK_HEAD = "CHERRY_PICK_HEAD"; /** * Pointer to the commit onto which another commit is to be merged during a merge operation */ public static final String ORIG_HEAD = "ORIG_HEAD"; /** * Directory prefix for refs. */ public static final String REFS_PREFIX = "refs/"; /** * Directory prefix for remotes. */ public static final String REMOTES_PREFIX = REFS_PREFIX + "remotes/"; /** * Directory prefix for tags. */ public static final String TAGS_PREFIX = REFS_PREFIX + "tags/"; /** * Directory prefix for heads. */ public static final String HEADS_PREFIX = REFS_PREFIX + "heads/"; /** * Directory prefix for transaction refs (i.e. where all refs are copied for a specific * transaction under {@code TRANSACTIONS_PREFIX + "/<transaction id>/} */ public static final String TRANSACTIONS_PREFIX = REFS_PREFIX + "transactions/"; /** * By convention, the origin of the repository */ public static final String ORIGIN = "refs/remotes/origin"; private String name; private ObjectId objectId; /** * Constructs a new Ref with the given parameters. * * @param name name of this ref * @param oid object id for this ref */ public Ref(final String name, final ObjectId oid) { Preconditions.checkNotNull(name); Preconditions.checkNotNull(oid); this.name = name; this.objectId = oid; } /** * @return the name for this ref */ public String getName() { return name; } /** * @return the name for this ref with the prefix removed */ public String localName() { return localName(name); } /** * @param ref the ref string * @return the local name of the given ref string */ public static String localName(final String ref) { if (ref.startsWith(HEADS_PREFIX)) { return ref.substring(HEADS_PREFIX.length()); } else if (ref.startsWith(TAGS_PREFIX)) { return ref.substring(TAGS_PREFIX.length()); } else if (ref.startsWith(REMOTES_PREFIX)) { String remoteWithBranch = ref.substring(REMOTES_PREFIX.length()); return remoteWithBranch.substring(remoteWithBranch.indexOf('/') + 1); } else if (ref.startsWith(REFS_PREFIX)) { return ref.substring(REFS_PREFIX.length()); } return ref; } /** * @return the namespace for this ref */ public String namespace() { return namespace(name); } /** * @param ref the ref string * @return the namespace of the given ref string */ public static String namespace(final String ref) { if (ref.startsWith(HEADS_PREFIX)) { return HEADS_PREFIX; } else if (ref.startsWith(TAGS_PREFIX)) { return TAGS_PREFIX; } else if (ref.startsWith(REMOTES_PREFIX)) { String remote = ref.substring(REMOTES_PREFIX.length()); remote = remote.substring(0, remote.indexOf('/')); return REMOTES_PREFIX + "/" + remote; } else if (ref.startsWith(REFS_PREFIX)) { return REFS_PREFIX; } return ref; } /** * @return the object id being referenced */ public ObjectId getObjectId() { return objectId; } /** * @param o object to compare against * @return whether or not this ref is equal to the target object */ @Override public boolean equals(Object o) { if (!(o instanceof Ref)) { return false; } Ref r = (Ref) o; return name.equals(r.getName()) && objectId.equals(r.getObjectId()); } /** * @return a hash code for this ref */ @Override public int hashCode() { return name.hashCode() * objectId.hashCode(); } /** * @see java.lang.Comparable#compareTo(java.lang.Object) */ @Override public int compareTo(Ref o) { return name.compareTo(o.getName()); } /** * @return the ref represented by a readable string */ @Override public String toString() { return new StringBuilder("Ref").append('[').append(name).append(" -> ").append(objectId) .append(']').toString(); } public static String append(String namespace, String child) { StringBuilder sb = new StringBuilder(); if (namespace.endsWith("/")) { namespace = namespace.substring(0, namespace.length() - 1); } sb.append(namespace); if (child.length() > 0 && child.charAt(0) == '/') { child = child.substring(1); } if (child.endsWith("/")) { child = child.substring(0, child.length() - 1); } if (!child.isEmpty()) { if (!namespace.isEmpty()) { sb.append('/'); } sb.append(child); } return sb.toString(); } /** * @return the relative name of the ref given by its full name and the namespace to truncate */ public static String child(String namespace, String ref) { Preconditions.checkState(ref.startsWith(namespace)); String relative = ref.substring(namespace.length()); if (relative.length() > 0 && relative.charAt(0) == '/') { relative = relative.substring(1); } return relative; } }