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
{
51 * Suffix for wildcard ref spec component, that indicate matching all refs
52 * with specified prefix.
54 public static final String WILDCARD_SUFFIX
= "/*";
57 * Check whether provided string is a wildcard ref spec component.
60 * ref spec component - string to test. Can be null.
61 * @return true if provided string is a wildcard ref spec component.
63 public static boolean isWildcard(final String s
) {
64 return s
!= null && s
.endsWith(WILDCARD_SUFFIX
);
67 /** Does this specification ask for forced updated (rewind/reset)? */
68 private boolean force
;
70 /** Is this specification actually a wildcard match? */
71 private boolean wildcard
;
73 /** Name of the ref(s) we would copy from. */
74 private String srcName
;
76 /** Name of the ref(s) we would copy into. */
77 private String dstName
;
80 * Construct an empty RefSpec.
82 * A newly created empty RefSpec is not suitable for use in most
83 * applications, as at least one field must be set to match a source name.
88 srcName
= Constants
.HEAD
;
93 * Parse a ref specification for use during transport operations.
95 * Specifications are typically one of the following forms:
97 * <li><code>refs/head/master</code></li>
98 * <li><code>refs/head/master:refs/remotes/origin/master</code></li>
99 * <li><code>refs/head/*:refs/remotes/origin/*</code></li>
100 * <li><code>+refs/head/master</code></li>
101 * <li><code>+refs/head/master:refs/remotes/origin/master</code></li>
102 * <li><code>+refs/head/*:refs/remotes/origin/*</code></li>
103 * <li><code>:refs/head/master</code></li>
107 * string describing the specification.
108 * @throws IllegalArgumentException
109 * the specification is invalid.
111 public RefSpec(final String spec
) {
113 if (s
.startsWith("+")) {
118 final int c
= s
.indexOf(':');
122 throw new IllegalArgumentException("Invalid wildcards " + spec
);
125 srcName
= s
.substring(0, c
);
126 dstName
= s
.substring(c
+ 1);
127 if (isWildcard(srcName
) && isWildcard(dstName
))
129 else if (isWildcard(srcName
) || isWildcard(dstName
))
130 throw new IllegalArgumentException("Invalid wildcards " + spec
);
133 throw new IllegalArgumentException("Invalid wildcards " + spec
);
139 * Expand a wildcard specification.
142 * the wildcard specification we should base ourselves on.
144 * actual name that matched the source of <code>p</code>.
146 protected RefSpec(final RefSpec p
, final String name
) {
147 final String pdst
= p
.getDestination();
148 if (p
.getSource() == null || pdst
== null)
149 throw new IllegalArgumentException("Cannot expand from " + p
);
150 force
= p
.isForceUpdate();
152 dstName
= pdst
.substring(0, pdst
.length() - 1)
153 + name
.substring(p
.getSource().length() - 1);
156 private RefSpec(final RefSpec p
) {
157 force
= p
.isForceUpdate();
158 wildcard
= p
.isWildcard();
159 srcName
= p
.getSource();
160 dstName
= p
.getDestination();
164 * Check if this specification wants to forcefully update the destination.
166 * @return true if this specification asks for updates without merge tests.
168 public boolean isForceUpdate() {
173 * Create a new RefSpec with a different force update setting.
176 * new value for force update in the returned instance.
177 * @return a new RefSpec with force update as specified.
179 public RefSpec
setForceUpdate(final boolean forceUpdate
) {
180 final RefSpec r
= new RefSpec(this);
181 r
.force
= forceUpdate
;
186 * Check if this specification is actually a wildcard pattern.
188 * If this is a wildcard pattern then the source and destination names
189 * returned by {@link #getSource()} and {@link #getDestination()} will not
190 * be actual ref names, but instead will be patterns.
192 * @return true if this specification could match more than one ref.
194 public boolean isWildcard() {
199 * Get the source ref description.
201 * During a fetch this is the name of the ref on the remote repository we
202 * are fetching from. During a push this is the name of the ref on the local
203 * repository we are pushing out from.
205 * @return name (or wildcard pattern) to match the source ref.
207 public String
getSource() {
212 * Create a new RefSpec with a different source name setting.
215 * new value for source in the returned instance.
216 * @return a new RefSpec with source as specified.
217 * @throws IllegalStateException
218 * There is already a destination configured, and the wildcard
219 * status of the existing destination disagrees with the
220 * wildcard status of the new source.
222 public RefSpec
setSource(final String source
) {
223 final RefSpec r
= new RefSpec(this);
225 if (isWildcard(r
.srcName
) && r
.dstName
== null)
226 throw new IllegalStateException("Destination is not a wildcard.");
227 if (isWildcard(r
.srcName
) != isWildcard(r
.dstName
))
228 throw new IllegalStateException("Source/Destination must match.");
233 * Get the destination ref description.
235 * During a fetch this is the local tracking branch that will be updated
236 * with the new ObjectId after fetching is complete. During a push this is
237 * the remote ref that will be updated by the remote's receive-pack process.
239 * If null during a fetch no tracking branch should be updated and the
240 * ObjectId should be stored transiently in order to prepare a merge.
242 * If null during a push, use {@link #getSource()} instead.
244 * @return name (or wildcard) pattern to match the destination ref.
246 public String
getDestination() {
251 * Create a new RefSpec with a different destination name setting.
254 * new value for destination in the returned instance.
255 * @return a new RefSpec with destination as specified.
256 * @throws IllegalStateException
257 * There is already a source configured, and the wildcard status
258 * of the existing source disagrees with the wildcard status of
259 * the new destination.
261 public RefSpec
setDestination(final String destination
) {
262 final RefSpec r
= new RefSpec(this);
263 r
.dstName
= destination
;
264 if (isWildcard(r
.dstName
) && r
.srcName
== null)
265 throw new IllegalStateException("Source is not a wildcard.");
266 if (isWildcard(r
.srcName
) != isWildcard(r
.dstName
))
267 throw new IllegalStateException("Source/Destination must match.");
272 * Create a new RefSpec with a different source/destination name setting.
275 * new value for source in the returned instance.
277 * new value for destination in the returned instance.
278 * @return a new RefSpec with destination as specified.
279 * @throws IllegalArgumentException
280 * The wildcard status of the new source disagrees with the
281 * wildcard status of the new destination.
283 public RefSpec
setSourceDestination(final String source
,
284 final String destination
) {
285 if (isWildcard(source
) != isWildcard(destination
))
286 throw new IllegalArgumentException("Source/Destination must match.");
287 final RefSpec r
= new RefSpec(this);
288 r
.wildcard
= isWildcard(source
);
290 r
.dstName
= destination
;
295 * Does this specification's source description match the ref name?
298 * ref name that should be tested.
299 * @return true if the names match; false otherwise.
301 public boolean matchSource(final String r
) {
302 return match(r
, getSource());
306 * Does this specification's source description match the ref?
309 * ref whose name should be tested.
310 * @return true if the names match; false otherwise.
312 public boolean matchSource(final Ref r
) {
313 return match(r
.getName(), getSource());
317 * Does this specification's destination description match the ref name?
320 * ref name that should be tested.
321 * @return true if the names match; false otherwise.
323 public boolean matchDestination(final String r
) {
324 return match(r
, getDestination());
328 * Does this specification's destination description match the ref?
331 * ref whose name should be tested.
332 * @return true if the names match; false otherwise.
334 public boolean matchDestination(final Ref r
) {
335 return match(r
.getName(), getDestination());
339 * Expand this specification to exactly match a ref name.
341 * Callers must first verify the passed ref name matches this specification,
342 * otherwise expansion results may be unpredictable.
345 * a ref name that matched our source specification. Could be a
347 * @return a new specification expanded from provided ref name. Result
348 * specification is wildcard if and only if provided ref name is
351 public RefSpec
expandFromSource(final String r
) {
352 return isWildcard() ?
new RefSpec(this, r
) : this;
356 * Expand this specification to exactly match a ref.
358 * Callers must first verify the passed ref matches this specification,
359 * otherwise expansion results may be unpredictable.
362 * a ref that matched our source specification. Could be a
364 * @return a new specification expanded from provided ref name. Result
365 * specification is wildcard if and only if provided ref name is
368 public RefSpec
expandFromSource(final Ref r
) {
369 return isWildcard() ?
new RefSpec(this, r
.getName()) : this;
372 private boolean match(final String refName
, final String s
) {
376 return refName
.startsWith(s
.substring(0, s
.length() - 1));
377 return refName
.equals(s
);
380 public int hashCode() {
382 if (getSource() != null)
383 hc
= hc
* 31 + getSource().hashCode();
384 if (getDestination() != null)
385 hc
= hc
* 31 + getDestination().hashCode();
389 public boolean equals(final Object obj
) {
390 if (!(obj
instanceof RefSpec
))
392 final RefSpec b
= (RefSpec
) obj
;
393 if (isForceUpdate() != b
.isForceUpdate())
395 if (isWildcard() != b
.isWildcard())
397 if (!eq(getSource(), b
.getSource()))
399 if (!eq(getDestination(), b
.getDestination()))
404 private static boolean eq(final String a
, final String b
) {
407 if (a
== null || b
== null)
412 public String
toString() {
413 final StringBuilder r
= new StringBuilder();
416 if (getSource() != null)
417 r
.append(getSource());
418 if (getDestination() != null) {
420 r
.append(getDestination());