2 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
18 * - Neither the name of the Git Development Community nor the
19 * names of its contributors may be used to endorse or promote
20 * products derived from this software without specific prior
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
24 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 package org
.spearce
.jgit
.transport
;
40 import org
.spearce
.jgit
.lib
.Constants
;
41 import org
.spearce
.jgit
.lib
.Ref
;
44 * Describes how refs in one repository copy into another repository.
46 * A ref specification provides matching support and limited rules to rewrite a
47 * reference in one repository to another reference in another repository.
49 public class RefSpec
{
50 private static final String WILDCARD_SUFFIX
= "/*";
52 private static boolean isWildcard(final String s
) {
53 return s
!= null && s
.endsWith(WILDCARD_SUFFIX
);
56 /** Does this specification ask for forced updated (rewind/reset)? */
57 private boolean force
;
59 /** Is this specification actually a wildcard match? */
60 private boolean wildcard
;
62 /** Name of the ref(s) we would copy from. */
63 private String srcName
;
65 /** Name of the ref(s) we would copy into. */
66 private String dstName
;
69 * Construct an empty RefSpec.
71 * A newly created empty RefSpec is not suitable for use in most
72 * applications, as at least one field must be set to match a source name.
77 srcName
= Constants
.HEAD
;
82 * Parse a ref specification for use during transport operations.
84 * Specifications are typically one of the following forms:
86 * <li><code>refs/head/master</code></li>
87 * <li><code>refs/head/master:refs/remotes/origin/master</code></li>
88 * <li><code>refs/head/*:refs/remotes/origin/*</code></li>
89 * <li><code>+refs/head/master</code></li>
90 * <li><code>+refs/head/master:refs/remotes/origin/master</code></li>
91 * <li><code>+refs/head/*:refs/remotes/origin/*</code></li>
92 * <li><code>:refs/head/master</code></li>
96 * string describing the specification.
97 * @throws IllegalArgumentException
98 * the specification is invalid.
100 public RefSpec(final String spec
) {
102 if (s
.startsWith("+")) {
107 final int c
= s
.indexOf(':');
111 throw new IllegalArgumentException("Invalid wildcards " + spec
);
114 srcName
= s
.substring(0, c
);
115 dstName
= s
.substring(c
+ 1);
116 if (isWildcard(srcName
) && isWildcard(dstName
))
118 else if (isWildcard(srcName
) || isWildcard(dstName
))
119 throw new IllegalArgumentException("Invalid wildcards " + spec
);
122 throw new IllegalArgumentException("Invalid wildcards " + spec
);
128 * Expand a wildcard specification.
131 * the wildcard specification we should base ourselves on.
133 * actual name that matched the source of <code>p</code>.
135 protected RefSpec(final RefSpec p
, final String name
) {
136 final String pdst
= p
.getDestination();
137 if (p
.getSource() == null || pdst
== null)
138 throw new IllegalArgumentException("Cannot expand from " + p
);
139 force
= p
.isForceUpdate();
141 dstName
= pdst
.substring(0, pdst
.length() - 1)
142 + name
.substring(p
.getSource().length() - 1);
145 private RefSpec(final RefSpec p
) {
146 force
= p
.isForceUpdate();
147 wildcard
= p
.isWildcard();
148 srcName
= p
.getSource();
149 dstName
= p
.getDestination();
153 * Check if this specification wants to forcefully update the destination.
155 * @return true if this specification asks for updates without merge tests.
157 public boolean isForceUpdate() {
162 * Create a new RefSpec with a different force update setting.
165 * new value for force update in the returned instance.
166 * @return a new RefSpec with force update as specified.
168 public RefSpec
setForceUpdate(final boolean forceUpdate
) {
169 final RefSpec r
= new RefSpec(this);
170 r
.force
= forceUpdate
;
175 * Check if this specification is actually a wildcard pattern.
177 * If this is a wildcard pattern then the source and destination names
178 * returned by {@link #getSource()} and {@link #getDestination()} will not
179 * be actual ref names, but instead will be patterns.
181 * @return true if this specification could match more than one ref.
183 public boolean isWildcard() {
188 * Get the source ref description.
190 * During a fetch this is the name of the ref on the remote repository we
191 * are fetching from. During a push this is the name of the ref on the local
192 * repository we are pushing out from.
194 * @return name (or wildcard pattern) to match the source ref.
196 public String
getSource() {
201 * Create a new RefSpec with a different source name setting.
204 * new value for source in the returned instance.
205 * @return a new RefSpec with source as specified.
206 * @throws IllegalStateException
207 * There is already a destination configured, and the wildcard
208 * status of the existing destination disagrees with the
209 * wildcard status of the new source.
211 public RefSpec
setSource(final String source
) {
212 final RefSpec r
= new RefSpec(this);
214 if (isWildcard(r
.srcName
) && r
.dstName
== null)
215 throw new IllegalStateException("Destination is not a wildcard.");
216 if (isWildcard(r
.srcName
) != isWildcard(r
.dstName
))
217 throw new IllegalStateException("Source/Destination must match.");
222 * Get the destination ref description.
224 * During a fetch this is the local tracking branch that will be updated
225 * with the new ObjectId after feching is complete. During a push this is
226 * the remote ref that will be updated by the remote's receive-pack process.
228 * If null during a fetch no tracking branch should be updated and the
229 * ObjectId should be stored transiently in order to prepare a merge.
231 * If null during a push, use {@link #getSource()} instead.
233 * @return name (or wildcard) pattern to match the destination ref.
235 public String
getDestination() {
240 * Create a new RefSpec with a different destination name setting.
243 * new value for destination in the returned instance.
244 * @return a new RefSpec with destination as specified.
245 * @throws IllegalStateException
246 * There is already a source configured, and the wildcard status
247 * of the existing source disagrees with the wildcard status of
248 * the new destination.
250 public RefSpec
setDestination(final String destination
) {
251 final RefSpec r
= new RefSpec(this);
252 r
.dstName
= destination
;
253 if (isWildcard(r
.dstName
) && r
.srcName
== null)
254 throw new IllegalStateException("Source is not a wildcard.");
255 if (isWildcard(r
.srcName
) != isWildcard(r
.dstName
))
256 throw new IllegalStateException("Source/Destination must match.");
261 * Create a new RefSpec with a different source/destination name setting.
264 * new value for source in the returned instance.
266 * new value for destination in the returned instance.
267 * @return a new RefSpec with destination as specified.
268 * @throws IllegalArgumentException
269 * The wildcard status of the new source disagrees with the
270 * wildcard status of the new destination.
272 public RefSpec
setSourceDestination(final String source
,
273 final String destination
) {
274 if (isWildcard(source
) != isWildcard(destination
))
275 throw new IllegalArgumentException("Source/Destination must match.");
276 final RefSpec r
= new RefSpec(this);
277 r
.wildcard
= isWildcard(source
);
279 r
.dstName
= destination
;
284 * Does this specification's source description match the ref name?
287 * ref name that should be tested.
288 * @return true if the names match; false otherwise.
290 public boolean matchSource(final String r
) {
291 return match(r
, getSource());
295 * Does this specification's source description match the ref?
298 * ref whose name should be tested.
299 * @return true if the names match; false otherwise.
301 public boolean matchSource(final Ref r
) {
302 return match(r
.getName(), getSource());
306 * Does this specification's destination description match the ref name?
309 * ref name that should be tested.
310 * @return true if the names match; false otherwise.
312 public boolean matchDestination(final String r
) {
313 return match(r
, getDestination());
317 * Does this specification's destination description match the ref?
320 * ref whose name should be tested.
321 * @return true if the names match; false otherwise.
323 public boolean matchDestination(final Ref r
) {
324 return match(r
.getName(), getDestination());
328 * Expand this specification to exactly match a ref name.
330 * Callers must first verify the passed ref name matches this specification,
331 * otherwise expansion results may be unpredictable.
334 * a ref name that matched our source specification.
335 * @return a new specification that is not a wildcard.
337 public RefSpec
expandFromSource(final String r
) {
338 return isWildcard() ?
new RefSpec(this, r
) : this;
342 * Expand this specification to exactly match a ref.
344 * Callers must first verify the passed ref matches this specification,
345 * otherwise expansion results may be unpredictable.
348 * a ref that matched our source specification.
349 * @return a new specification that is not a wildcard.
351 public RefSpec
expandFromSource(final Ref r
) {
352 return isWildcard() ?
new RefSpec(this, r
.getName()) : this;
355 private boolean match(final String refName
, final String s
) {
359 return refName
.startsWith(s
.substring(0, s
.length() - 1));
360 return refName
.equals(s
);
363 public int hashCode() {
365 if (getSource() != null)
366 hc
= hc
* 31 + getSource().hashCode();
367 if (getDestination() != null)
368 hc
= hc
* 31 + getDestination().hashCode();
372 public boolean equals(final Object obj
) {
373 if (!(obj
instanceof RefSpec
))
375 final RefSpec b
= (RefSpec
) obj
;
376 if (isForceUpdate() != b
.isForceUpdate())
378 if (isWildcard() != b
.isWildcard())
380 if (!eq(getSource(), b
.getSource()))
382 if (!eq(getDestination(), b
.getDestination()))
387 private static boolean eq(final String a
, final String b
) {
390 if (a
== null || b
== null)
395 public String
toString() {
396 final StringBuilder r
= new StringBuilder();
399 if (getSource() != null)
400 r
.append(getSource());
401 if (getDestination() != null) {
403 r
.append(getDestination());