2 * Copyright (C) 2008 Shawn Pearce <spearce@spearce.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License, version 2, as published by the Free Software Foundation.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
17 package org
.spearce
.jgit
.transport
;
19 import org
.spearce
.jgit
.lib
.Constants
;
20 import org
.spearce
.jgit
.lib
.Ref
;
23 * Describes how refs in one repository copy into another repository.
25 * A ref specification provides matching support and limited rules to rewrite a
26 * reference in one repository to another reference in another repository.
28 public class RefSpec
{
29 private static final String WILDCARD_SUFFIX
= "/*";
31 private static boolean isWildcard(final String s
) {
32 return s
!= null && s
.endsWith(WILDCARD_SUFFIX
);
35 /** Does this specification ask for forced updated (rewind/reset)? */
36 private boolean force
;
38 /** Is this specification actually a wildcard match? */
39 private boolean wildcard
;
41 /** Name of the ref(s) we would copy from. */
42 private String srcName
;
44 /** Name of the ref(s) we would copy into. */
45 private String dstName
;
48 * Construct an empty RefSpec.
50 * A newly created empty RefSpec is not suitable for use in most
51 * applications, as at least one field must be set to match a source name.
56 srcName
= Constants
.HEAD
;
61 * Parse a ref specification for use during transport operations.
63 * Specifications are typically one of the following forms:
65 * <li><code>refs/head/master</code></li>
66 * <li><code>refs/head/master:refs/remotes/origin/master</code></li>
67 * <li><code>refs/head/*:refs/remotes/origin/*</code></li>
68 * <li><code>+refs/head/master</code></li>
69 * <li><code>+refs/head/master:refs/remotes/origin/master</code></li>
70 * <li><code>+refs/head/*:refs/remotes/origin/*</code></li>
71 * <li><code>:refs/head/master</code></li>
75 * string describing the specification.
76 * @throws IllegalArgumentException
77 * the specification is invalid.
79 public RefSpec(final String spec
) {
81 if (s
.startsWith("+")) {
86 final int c
= s
.indexOf(':');
90 throw new IllegalArgumentException("Invalid wildcards " + spec
);
93 srcName
= s
.substring(0, c
);
94 dstName
= s
.substring(c
+ 1);
95 if (isWildcard(srcName
) && isWildcard(dstName
))
97 else if (isWildcard(srcName
) || isWildcard(dstName
))
98 throw new IllegalArgumentException("Invalid wildcards " + spec
);
101 throw new IllegalArgumentException("Invalid wildcards " + spec
);
107 * Expand a wildcard specification.
110 * the wildcard specification we should base ourselves on.
112 * actual name that matched the source of <code>p</code>.
114 protected RefSpec(final RefSpec p
, final String name
) {
115 final String pdst
= p
.getDestination();
116 if (p
.getSource() == null || pdst
== null)
117 throw new IllegalArgumentException("Cannot expand from " + p
);
118 force
= p
.isForceUpdate();
120 dstName
= pdst
.substring(0, pdst
.length() - 1)
121 + name
.substring(p
.getSource().length() - 1);
124 private RefSpec(final RefSpec p
) {
125 force
= p
.isForceUpdate();
126 wildcard
= p
.isWildcard();
127 srcName
= p
.getSource();
128 dstName
= p
.getDestination();
132 * Check if this specification wants to forcefully update the destination.
134 * @return true if this specification asks for updates without merge tests.
136 public boolean isForceUpdate() {
141 * Create a new RefSpec with a different force update setting.
144 * new value for force update in the returned instance.
145 * @return a new RefSpec with force update as specified.
147 public RefSpec
setForceUpdate(final boolean forceUpdate
) {
148 final RefSpec r
= new RefSpec(this);
149 r
.force
= forceUpdate
;
154 * Check if this specification is actually a wildcard pattern.
156 * If this is a wildcard pattern then the source and destination names
157 * returned by {@link #getSource()} and {@link #getDestination()} will not
158 * be actual ref names, but instead will be patterns.
160 * @return true if this specification could match more than one ref.
162 public boolean isWildcard() {
167 * Get the source ref description.
169 * During a fetch this is the name of the ref on the remote repository we
170 * are fetching from. During a push this is the name of the ref on the local
171 * repository we are pushing out from.
173 * @return name (or wildcard pattern) to match the source ref.
175 public String
getSource() {
180 * Create a new RefSpec with a different source name setting.
183 * new value for source in the returned instance.
184 * @return a new RefSpec with source as specified.
185 * @throws IllegalStateException
186 * There is already a destination configured, and the wildcard
187 * status of the existing destination disagrees with the
188 * wildcard status of the new source.
190 public RefSpec
setSource(final String source
) {
191 final RefSpec r
= new RefSpec(this);
193 if (isWildcard(r
.srcName
) && r
.dstName
== null)
194 throw new IllegalStateException("Destination is not a wildcard.");
195 if (isWildcard(r
.srcName
) != isWildcard(r
.dstName
))
196 throw new IllegalStateException("Source/Destination must match.");
201 * Get the destination ref description.
203 * During a fetch this is the local tracking branch that will be updated
204 * with the new ObjectId after feching is complete. During a push this is
205 * the remote ref that will be updated by the remote's receive-pack process.
207 * If null during a fetch no tracking branch should be updated and the
208 * ObjectId should be stored transiently in order to prepare a merge.
210 * If null during a push, use {@link #getSource()} instead.
212 * @return name (or wildcard) pattern to match the destination ref.
214 public String
getDestination() {
219 * Create a new RefSpec with a different destination name setting.
222 * new value for destination in the returned instance.
223 * @return a new RefSpec with destination as specified.
224 * @throws IllegalStateException
225 * There is already a source configured, and the wildcard status
226 * of the existing source disagrees with the wildcard status of
227 * the new destination.
229 public RefSpec
setDestination(final String destination
) {
230 final RefSpec r
= new RefSpec(this);
231 r
.dstName
= destination
;
232 if (isWildcard(r
.dstName
) && r
.srcName
== null)
233 throw new IllegalStateException("Source is not a wildcard.");
234 if (isWildcard(r
.srcName
) != isWildcard(r
.dstName
))
235 throw new IllegalStateException("Source/Destination must match.");
240 * Create a new RefSpec with a different source/destination name setting.
243 * new value for source in the returned instance.
245 * new value for destination in the returned instance.
246 * @return a new RefSpec with destination as specified.
247 * @throws IllegalArgumentException
248 * The wildcard status of the new source disagrees with the
249 * wildcard status of the new destination.
251 public RefSpec
setSourceDestination(final String source
,
252 final String destination
) {
253 if (isWildcard(source
) != isWildcard(destination
))
254 throw new IllegalArgumentException("Source/Destination must match.");
255 final RefSpec r
= new RefSpec(this);
256 r
.wildcard
= isWildcard(source
);
258 r
.dstName
= destination
;
263 * Does this specification's source description match the ref?
266 * ref whose name should be tested.
267 * @return true if the names match; false otherwise.
269 public boolean matchSource(final Ref r
) {
270 return match(r
, getSource());
274 * Does this specification's destination description match the ref?
277 * ref whose name should be tested.
278 * @return true if the names match; false otherwise.
280 public boolean matchDestination(final Ref r
) {
281 return match(r
, getDestination());
285 * Expand this specification to exactly match a ref.
287 * Callers must first verify the passed ref matches this specification,
288 * otherwise expansion results may be unpredictable.
291 * a ref that matched our source specification.
292 * @return a new specification that is not a wildcard.
294 public RefSpec
expandFromSource(final Ref r
) {
295 return isWildcard() ?
new RefSpec(this, r
.getName()) : this;
298 private boolean match(final Ref r
, final String s
) {
302 return r
.getName().startsWith(s
.substring(0, s
.length() - 1));
303 return r
.getName().equals(s
);
306 public int hashCode() {
308 if (getSource() != null)
309 hc
= hc
* 31 + getSource().hashCode();
310 if (getDestination() != null)
311 hc
= hc
* 31 + getDestination().hashCode();
315 public boolean equals(final Object obj
) {
316 if (!(obj
instanceof RefSpec
))
318 final RefSpec b
= (RefSpec
) obj
;
319 if (isForceUpdate() != b
.isForceUpdate())
321 if (isWildcard() != b
.isWildcard())
323 if (!eq(getSource(), b
.getSource()))
325 if (!eq(getDestination(), b
.getDestination()))
330 private static boolean eq(final String a
, final String b
) {
333 if (a
== null || b
== null)
338 public String
toString() {
339 final StringBuilder r
= new StringBuilder();
342 if (getSource() != null)
343 r
.append(getSource());
344 if (getDestination() != null) {
346 r
.append(getDestination());