Fix RemoteRefUpdate to delete local tracking ref upon successful deletion
[egit/zawir.git] / org.spearce.jgit / src / org / spearce / jgit / transport / RefSpec.java
blob521110b41282458370751645c08eff48b35a7d3f
1 /*
2 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
8 * conditions are met:
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
21 * written permission.
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;
43 /**
44 * Describes how refs in one repository copy into another repository.
45 * <p>
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 /**
51 * Suffix for wildcard ref spec component, that indicate matching all refs
52 * with specified prefix.
54 public static final String WILDCARD_SUFFIX = "/*";
56 /**
57 * Check whether provided string is a wildcard ref spec component.
59 * @param s
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;
79 /**
80 * Construct an empty RefSpec.
81 * <p>
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.
85 public RefSpec() {
86 force = false;
87 wildcard = false;
88 srcName = Constants.HEAD;
89 dstName = null;
92 /**
93 * Parse a ref specification for use during transport operations.
94 * <p>
95 * Specifications are typically one of the following forms:
96 * <ul>
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>
104 * </ul>
106 * @param spec
107 * string describing the specification.
108 * @throws IllegalArgumentException
109 * the specification is invalid.
111 public RefSpec(final String spec) {
112 String s = spec;
113 if (s.startsWith("+")) {
114 force = true;
115 s = s.substring(1);
118 final int c = s.indexOf(':');
119 if (c == 0) {
120 s = s.substring(1);
121 if (isWildcard(s))
122 throw new IllegalArgumentException("Invalid wildcards " + spec);
123 dstName = s;
124 } else if (c > 0) {
125 srcName = s.substring(0, c);
126 dstName = s.substring(c + 1);
127 if (isWildcard(srcName) && isWildcard(dstName))
128 wildcard = true;
129 else if (isWildcard(srcName) || isWildcard(dstName))
130 throw new IllegalArgumentException("Invalid wildcards " + spec);
131 } else {
132 if (isWildcard(s))
133 throw new IllegalArgumentException("Invalid wildcards " + spec);
134 srcName = s;
139 * Expand a wildcard specification.
141 * @param p
142 * the wildcard specification we should base ourselves on.
143 * @param name
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();
151 srcName = name;
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() {
169 return force;
173 * Create a new RefSpec with a different force update setting.
175 * @param forceUpdate
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;
182 return r;
186 * Check if this specification is actually a wildcard pattern.
187 * <p>
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() {
195 return wildcard;
199 * Get the source ref description.
200 * <p>
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() {
208 return srcName;
212 * Create a new RefSpec with a different source name setting.
214 * @param source
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);
224 r.srcName = source;
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.");
229 return r;
233 * Get the destination ref description.
234 * <p>
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.
238 * <p>
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.
241 * <p>
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() {
247 return dstName;
251 * Create a new RefSpec with a different destination name setting.
253 * @param destination
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.");
268 return r;
272 * Create a new RefSpec with a different source/destination name setting.
274 * @param source
275 * new value for source in the returned instance.
276 * @param destination
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);
289 r.srcName = source;
290 r.dstName = destination;
291 return r;
295 * Does this specification's source description match the ref name?
297 * @param r
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?
308 * @param r
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?
319 * @param r
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?
330 * @param r
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.
340 * <p>
341 * Callers must first verify the passed ref name matches this specification,
342 * otherwise expansion results may be unpredictable.
344 * @param r
345 * a ref name that matched our source specification. Could be a
346 * wildcard also.
347 * @return a new specification expanded from provided ref name. Result
348 * specification is wildcard if and only if provided ref name is
349 * wildcard.
351 public RefSpec expandFromSource(final String r) {
352 return isWildcard() ? new RefSpec(this, r) : this;
356 * Expand this specification to exactly match a ref.
357 * <p>
358 * Callers must first verify the passed ref matches this specification,
359 * otherwise expansion results may be unpredictable.
361 * @param r
362 * a ref that matched our source specification. Could be a
363 * wildcard also.
364 * @return a new specification expanded from provided ref name. Result
365 * specification is wildcard if and only if provided ref name is
366 * wildcard.
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) {
373 if (s == null)
374 return false;
375 if (isWildcard())
376 return refName.startsWith(s.substring(0, s.length() - 1));
377 return refName.equals(s);
380 public int hashCode() {
381 int hc = 0;
382 if (getSource() != null)
383 hc = hc * 31 + getSource().hashCode();
384 if (getDestination() != null)
385 hc = hc * 31 + getDestination().hashCode();
386 return hc;
389 public boolean equals(final Object obj) {
390 if (!(obj instanceof RefSpec))
391 return false;
392 final RefSpec b = (RefSpec) obj;
393 if (isForceUpdate() != b.isForceUpdate())
394 return false;
395 if (isWildcard() != b.isWildcard())
396 return false;
397 if (!eq(getSource(), b.getSource()))
398 return false;
399 if (!eq(getDestination(), b.getDestination()))
400 return false;
401 return true;
404 private static boolean eq(final String a, final String b) {
405 if (a == b)
406 return true;
407 if (a == null || b == null)
408 return false;
409 return a.equals(b);
412 public String toString() {
413 final StringBuilder r = new StringBuilder();
414 if (isForceUpdate())
415 r.append('+');
416 if (getSource() != null)
417 r.append(getSource());
418 if (getDestination() != null) {
419 r.append(':');
420 r.append(getDestination());
422 return r.toString();