org.eclipse.jgit.transport.RefSpec Maven / Gradle / Ivy
/*
* Copyright (C) 2008, Shawn O. Pearce
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
* accompanies this distribution, is reproduced below, and is
* available at http://www.eclipse.org/org/documents/edl-v10.php
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* - Neither the name of the Eclipse Foundation, Inc. nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.eclipse.jgit.transport;
import java.text.MessageFormat;
import java.io.Serializable;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
/**
* Describes how refs in one repository copy into another repository.
*
* A ref specification provides matching support and limited rules to rewrite a
* reference in one repository to another reference in another repository.
*/
public class RefSpec implements Serializable {
private static final long serialVersionUID = 1L;
/**
* Suffix for wildcard ref spec component, that indicate matching all refs
* with specified prefix.
*/
public static final String WILDCARD_SUFFIX = "/*"; //$NON-NLS-1$
/**
* Check whether provided string is a wildcard ref spec component.
*
* @param s
* ref spec component - string to test. Can be null.
* @return true if provided string is a wildcard ref spec component.
*/
public static boolean isWildcard(final String s) {
return s != null && s.endsWith(WILDCARD_SUFFIX);
}
/** Does this specification ask for forced updated (rewind/reset)? */
private boolean force;
/** Is this specification actually a wildcard match? */
private boolean wildcard;
/** Name of the ref(s) we would copy from. */
private String srcName;
/** Name of the ref(s) we would copy into. */
private String dstName;
/**
* Construct an empty RefSpec.
*
* A newly created empty RefSpec is not suitable for use in most
* applications, as at least one field must be set to match a source name.
*/
public RefSpec() {
force = false;
wildcard = false;
srcName = Constants.HEAD;
dstName = null;
}
/**
* Parse a ref specification for use during transport operations.
*
* Specifications are typically one of the following forms:
*
* refs/head/master
* refs/head/master:refs/remotes/origin/master
* refs/head/*:refs/remotes/origin/*
* +refs/head/master
* +refs/head/master:refs/remotes/origin/master
* +refs/head/*:refs/remotes/origin/*
* :refs/head/master
*
*
* @param spec
* string describing the specification.
* @throws IllegalArgumentException
* the specification is invalid.
*/
public RefSpec(final String spec) {
String s = spec;
if (s.startsWith("+")) { //$NON-NLS-1$
force = true;
s = s.substring(1);
}
final int c = s.lastIndexOf(':');
if (c == 0) {
s = s.substring(1);
if (isWildcard(s))
throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidWildcards, spec));
dstName = s;
} else if (c > 0) {
srcName = s.substring(0, c);
dstName = s.substring(c + 1);
if (isWildcard(srcName) && isWildcard(dstName))
wildcard = true;
else if (isWildcard(srcName) || isWildcard(dstName))
throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidWildcards, spec));
} else {
if (isWildcard(s))
throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidWildcards, spec));
srcName = s;
}
}
private RefSpec(final RefSpec p) {
force = p.isForceUpdate();
wildcard = p.isWildcard();
srcName = p.getSource();
dstName = p.getDestination();
}
/**
* Check if this specification wants to forcefully update the destination.
*
* @return true if this specification asks for updates without merge tests.
*/
public boolean isForceUpdate() {
return force;
}
/**
* Create a new RefSpec with a different force update setting.
*
* @param forceUpdate
* new value for force update in the returned instance.
* @return a new RefSpec with force update as specified.
*/
public RefSpec setForceUpdate(final boolean forceUpdate) {
final RefSpec r = new RefSpec(this);
r.force = forceUpdate;
return r;
}
/**
* Check if this specification is actually a wildcard pattern.
*
* If this is a wildcard pattern then the source and destination names
* returned by {@link #getSource()} and {@link #getDestination()} will not
* be actual ref names, but instead will be patterns.
*
* @return true if this specification could match more than one ref.
*/
public boolean isWildcard() {
return wildcard;
}
/**
* Get the source ref description.
*
* During a fetch this is the name of the ref on the remote repository we
* are fetching from. During a push this is the name of the ref on the local
* repository we are pushing out from.
*
* @return name (or wildcard pattern) to match the source ref.
*/
public String getSource() {
return srcName;
}
/**
* Create a new RefSpec with a different source name setting.
*
* @param source
* new value for source in the returned instance.
* @return a new RefSpec with source as specified.
* @throws IllegalStateException
* There is already a destination configured, and the wildcard
* status of the existing destination disagrees with the
* wildcard status of the new source.
*/
public RefSpec setSource(final String source) {
final RefSpec r = new RefSpec(this);
r.srcName = source;
if (isWildcard(r.srcName) && r.dstName == null)
throw new IllegalStateException(JGitText.get().destinationIsNotAWildcard);
if (isWildcard(r.srcName) != isWildcard(r.dstName))
throw new IllegalStateException(JGitText.get().sourceDestinationMustMatch);
return r;
}
/**
* Get the destination ref description.
*
* During a fetch this is the local tracking branch that will be updated
* with the new ObjectId after fetching is complete. During a push this is
* the remote ref that will be updated by the remote's receive-pack process.
*
* If null during a fetch no tracking branch should be updated and the
* ObjectId should be stored transiently in order to prepare a merge.
*
* If null during a push, use {@link #getSource()} instead.
*
* @return name (or wildcard) pattern to match the destination ref.
*/
public String getDestination() {
return dstName;
}
/**
* Create a new RefSpec with a different destination name setting.
*
* @param destination
* new value for destination in the returned instance.
* @return a new RefSpec with destination as specified.
* @throws IllegalStateException
* There is already a source configured, and the wildcard status
* of the existing source disagrees with the wildcard status of
* the new destination.
*/
public RefSpec setDestination(final String destination) {
final RefSpec r = new RefSpec(this);
r.dstName = destination;
if (isWildcard(r.dstName) && r.srcName == null)
throw new IllegalStateException(JGitText.get().sourceIsNotAWildcard);
if (isWildcard(r.srcName) != isWildcard(r.dstName))
throw new IllegalStateException(JGitText.get().sourceDestinationMustMatch);
return r;
}
/**
* Create a new RefSpec with a different source/destination name setting.
*
* @param source
* new value for source in the returned instance.
* @param destination
* new value for destination in the returned instance.
* @return a new RefSpec with destination as specified.
* @throws IllegalArgumentException
* The wildcard status of the new source disagrees with the
* wildcard status of the new destination.
*/
public RefSpec setSourceDestination(final String source,
final String destination) {
if (isWildcard(source) != isWildcard(destination))
throw new IllegalStateException(JGitText.get().sourceDestinationMustMatch);
final RefSpec r = new RefSpec(this);
r.wildcard = isWildcard(source);
r.srcName = source;
r.dstName = destination;
return r;
}
/**
* Does this specification's source description match the ref name?
*
* @param r
* ref name that should be tested.
* @return true if the names match; false otherwise.
*/
public boolean matchSource(final String r) {
return match(r, getSource());
}
/**
* Does this specification's source description match the ref?
*
* @param r
* ref whose name should be tested.
* @return true if the names match; false otherwise.
*/
public boolean matchSource(final Ref r) {
return match(r.getName(), getSource());
}
/**
* Does this specification's destination description match the ref name?
*
* @param r
* ref name that should be tested.
* @return true if the names match; false otherwise.
*/
public boolean matchDestination(final String r) {
return match(r, getDestination());
}
/**
* Does this specification's destination description match the ref?
*
* @param r
* ref whose name should be tested.
* @return true if the names match; false otherwise.
*/
public boolean matchDestination(final Ref r) {
return match(r.getName(), getDestination());
}
/**
* Expand this specification to exactly match a ref name.
*
* Callers must first verify the passed ref name matches this specification,
* otherwise expansion results may be unpredictable.
*
* @param r
* a ref name that matched our source specification. Could be a
* wildcard also.
* @return a new specification expanded from provided ref name. Result
* specification is wildcard if and only if provided ref name is
* wildcard.
*/
public RefSpec expandFromSource(final String r) {
return isWildcard() ? new RefSpec(this).expandFromSourceImp(r) : this;
}
private RefSpec expandFromSourceImp(final String name) {
final String psrc = srcName, pdst = dstName;
wildcard = false;
srcName = name;
dstName = pdst.substring(0, pdst.length() - 1)
+ name.substring(psrc.length() - 1);
return this;
}
/**
* Expand this specification to exactly match a ref.
*
* Callers must first verify the passed ref matches this specification,
* otherwise expansion results may be unpredictable.
*
* @param r
* a ref that matched our source specification. Could be a
* wildcard also.
* @return a new specification expanded from provided ref name. Result
* specification is wildcard if and only if provided ref name is
* wildcard.
*/
public RefSpec expandFromSource(final Ref r) {
return expandFromSource(r.getName());
}
/**
* Expand this specification to exactly match a ref name.
*
* Callers must first verify the passed ref name matches this specification,
* otherwise expansion results may be unpredictable.
*
* @param r
* a ref name that matched our destination specification. Could
* be a wildcard also.
* @return a new specification expanded from provided ref name. Result
* specification is wildcard if and only if provided ref name is
* wildcard.
*/
public RefSpec expandFromDestination(final String r) {
return isWildcard() ? new RefSpec(this).expandFromDstImp(r) : this;
}
private RefSpec expandFromDstImp(final String name) {
final String psrc = srcName, pdst = dstName;
wildcard = false;
srcName = psrc.substring(0, psrc.length() - 1)
+ name.substring(pdst.length() - 1);
dstName = name;
return this;
}
/**
* Expand this specification to exactly match a ref.
*
* Callers must first verify the passed ref matches this specification,
* otherwise expansion results may be unpredictable.
*
* @param r
* a ref that matched our destination specification.
* @return a new specification expanded from provided ref name. Result
* specification is wildcard if and only if provided ref name is
* wildcard.
*/
public RefSpec expandFromDestination(final Ref r) {
return expandFromDestination(r.getName());
}
private boolean match(final String refName, final String s) {
if (s == null)
return false;
if (isWildcard())
return refName.startsWith(s.substring(0, s.length() - 1));
return refName.equals(s);
}
public int hashCode() {
int hc = 0;
if (getSource() != null)
hc = hc * 31 + getSource().hashCode();
if (getDestination() != null)
hc = hc * 31 + getDestination().hashCode();
return hc;
}
public boolean equals(final Object obj) {
if (!(obj instanceof RefSpec))
return false;
final RefSpec b = (RefSpec) obj;
if (isForceUpdate() != b.isForceUpdate())
return false;
if (isWildcard() != b.isWildcard())
return false;
if (!eq(getSource(), b.getSource()))
return false;
if (!eq(getDestination(), b.getDestination()))
return false;
return true;
}
private static boolean eq(final String a, final String b) {
if (a == b)
return true;
if (a == null || b == null)
return false;
return a.equals(b);
}
public String toString() {
final StringBuilder r = new StringBuilder();
if (isForceUpdate())
r.append('+');
if (getSource() != null)
r.append(getSource());
if (getDestination() != null) {
r.append(':');
r.append(getDestination());
}
return r.toString();
}
}