You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

RefSpec.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. /*
  2. * Copyright (C) 2008, 2013 Shawn O. Pearce <spearce@spearce.org>
  3. * and other copyright owners as documented in the project's IP log.
  4. *
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Distribution License v1.0 which
  7. * accompanies this distribution, is reproduced below, and is
  8. * available at http://www.eclipse.org/org/documents/edl-v10.php
  9. *
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or
  13. * without modification, are permitted provided that the following
  14. * conditions are met:
  15. *
  16. * - Redistributions of source code must retain the above copyright
  17. * notice, this list of conditions and the following disclaimer.
  18. *
  19. * - Redistributions in binary form must reproduce the above
  20. * copyright notice, this list of conditions and the following
  21. * disclaimer in the documentation and/or other materials provided
  22. * with the distribution.
  23. *
  24. * - Neither the name of the Eclipse Foundation, Inc. nor the
  25. * names of its contributors may be used to endorse or promote
  26. * products derived from this software without specific prior
  27. * written permission.
  28. *
  29. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  30. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  31. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  34. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  38. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  41. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42. */
  43. package org.eclipse.jgit.transport;
  44. import java.text.MessageFormat;
  45. import java.io.Serializable;
  46. import org.eclipse.jgit.internal.JGitText;
  47. import org.eclipse.jgit.lib.Constants;
  48. import org.eclipse.jgit.lib.Ref;
  49. /**
  50. * Describes how refs in one repository copy into another repository.
  51. * <p>
  52. * A ref specification provides matching support and limited rules to rewrite a
  53. * reference in one repository to another reference in another repository.
  54. */
  55. public class RefSpec implements Serializable {
  56. private static final long serialVersionUID = 1L;
  57. /**
  58. * Suffix for wildcard ref spec component, that indicate matching all refs
  59. * with specified prefix.
  60. */
  61. public static final String WILDCARD_SUFFIX = "/*"; //$NON-NLS-1$
  62. /**
  63. * Check whether provided string is a wildcard ref spec component.
  64. *
  65. * @param s
  66. * ref spec component - string to test. Can be null.
  67. * @return true if provided string is a wildcard ref spec component.
  68. */
  69. public static boolean isWildcard(final String s) {
  70. return s != null && s.endsWith(WILDCARD_SUFFIX);
  71. }
  72. /** Does this specification ask for forced updated (rewind/reset)? */
  73. private boolean force;
  74. /** Is this specification actually a wildcard match? */
  75. private boolean wildcard;
  76. /** Name of the ref(s) we would copy from. */
  77. private String srcName;
  78. /** Name of the ref(s) we would copy into. */
  79. private String dstName;
  80. /**
  81. * Construct an empty RefSpec.
  82. * <p>
  83. * A newly created empty RefSpec is not suitable for use in most
  84. * applications, as at least one field must be set to match a source name.
  85. */
  86. public RefSpec() {
  87. force = false;
  88. wildcard = false;
  89. srcName = Constants.HEAD;
  90. dstName = null;
  91. }
  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/heads/master</code></li>
  98. * <li><code>refs/heads/master:refs/remotes/origin/master</code></li>
  99. * <li><code>refs/heads/*:refs/remotes/origin/*</code></li>
  100. * <li><code>+refs/heads/master</code></li>
  101. * <li><code>+refs/heads/master:refs/remotes/origin/master</code></li>
  102. * <li><code>+refs/heads/*:refs/remotes/origin/*</code></li>
  103. * <li><code>:refs/heads/master</code></li>
  104. * </ul>
  105. *
  106. * @param spec
  107. * string describing the specification.
  108. * @throws IllegalArgumentException
  109. * the specification is invalid.
  110. */
  111. public RefSpec(final String spec) {
  112. String s = spec;
  113. if (s.startsWith("+")) { //$NON-NLS-1$
  114. force = true;
  115. s = s.substring(1);
  116. }
  117. final int c = s.lastIndexOf(':');
  118. if (c == 0) {
  119. s = s.substring(1);
  120. if (isWildcard(s))
  121. throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidWildcards, spec));
  122. dstName = s;
  123. } else if (c > 0) {
  124. srcName = s.substring(0, c);
  125. dstName = s.substring(c + 1);
  126. if (isWildcard(srcName) && isWildcard(dstName))
  127. wildcard = true;
  128. else if (isWildcard(srcName) || isWildcard(dstName))
  129. throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidWildcards, spec));
  130. } else {
  131. if (isWildcard(s))
  132. throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidWildcards, spec));
  133. srcName = s;
  134. }
  135. }
  136. private RefSpec(final RefSpec p) {
  137. force = p.isForceUpdate();
  138. wildcard = p.isWildcard();
  139. srcName = p.getSource();
  140. dstName = p.getDestination();
  141. }
  142. /**
  143. * Check if this specification wants to forcefully update the destination.
  144. *
  145. * @return true if this specification asks for updates without merge tests.
  146. */
  147. public boolean isForceUpdate() {
  148. return force;
  149. }
  150. /**
  151. * Create a new RefSpec with a different force update setting.
  152. *
  153. * @param forceUpdate
  154. * new value for force update in the returned instance.
  155. * @return a new RefSpec with force update as specified.
  156. */
  157. public RefSpec setForceUpdate(final boolean forceUpdate) {
  158. final RefSpec r = new RefSpec(this);
  159. r.force = forceUpdate;
  160. return r;
  161. }
  162. /**
  163. * Check if this specification is actually a wildcard pattern.
  164. * <p>
  165. * If this is a wildcard pattern then the source and destination names
  166. * returned by {@link #getSource()} and {@link #getDestination()} will not
  167. * be actual ref names, but instead will be patterns.
  168. *
  169. * @return true if this specification could match more than one ref.
  170. */
  171. public boolean isWildcard() {
  172. return wildcard;
  173. }
  174. /**
  175. * Get the source ref description.
  176. * <p>
  177. * During a fetch this is the name of the ref on the remote repository we
  178. * are fetching from. During a push this is the name of the ref on the local
  179. * repository we are pushing out from.
  180. *
  181. * @return name (or wildcard pattern) to match the source ref.
  182. */
  183. public String getSource() {
  184. return srcName;
  185. }
  186. /**
  187. * Create a new RefSpec with a different source name setting.
  188. *
  189. * @param source
  190. * new value for source in the returned instance.
  191. * @return a new RefSpec with source as specified.
  192. * @throws IllegalStateException
  193. * There is already a destination configured, and the wildcard
  194. * status of the existing destination disagrees with the
  195. * wildcard status of the new source.
  196. */
  197. public RefSpec setSource(final String source) {
  198. final RefSpec r = new RefSpec(this);
  199. r.srcName = source;
  200. if (isWildcard(r.srcName) && r.dstName == null)
  201. throw new IllegalStateException(JGitText.get().destinationIsNotAWildcard);
  202. if (isWildcard(r.srcName) != isWildcard(r.dstName))
  203. throw new IllegalStateException(JGitText.get().sourceDestinationMustMatch);
  204. return r;
  205. }
  206. /**
  207. * Get the destination ref description.
  208. * <p>
  209. * During a fetch this is the local tracking branch that will be updated
  210. * with the new ObjectId after fetching is complete. During a push this is
  211. * the remote ref that will be updated by the remote's receive-pack process.
  212. * <p>
  213. * If null during a fetch no tracking branch should be updated and the
  214. * ObjectId should be stored transiently in order to prepare a merge.
  215. * <p>
  216. * If null during a push, use {@link #getSource()} instead.
  217. *
  218. * @return name (or wildcard) pattern to match the destination ref.
  219. */
  220. public String getDestination() {
  221. return dstName;
  222. }
  223. /**
  224. * Create a new RefSpec with a different destination name setting.
  225. *
  226. * @param destination
  227. * new value for destination in the returned instance.
  228. * @return a new RefSpec with destination as specified.
  229. * @throws IllegalStateException
  230. * There is already a source configured, and the wildcard status
  231. * of the existing source disagrees with the wildcard status of
  232. * the new destination.
  233. */
  234. public RefSpec setDestination(final String destination) {
  235. final RefSpec r = new RefSpec(this);
  236. r.dstName = destination;
  237. if (isWildcard(r.dstName) && r.srcName == null)
  238. throw new IllegalStateException(JGitText.get().sourceIsNotAWildcard);
  239. if (isWildcard(r.srcName) != isWildcard(r.dstName))
  240. throw new IllegalStateException(JGitText.get().sourceDestinationMustMatch);
  241. return r;
  242. }
  243. /**
  244. * Create a new RefSpec with a different source/destination name setting.
  245. *
  246. * @param source
  247. * new value for source in the returned instance.
  248. * @param destination
  249. * new value for destination in the returned instance.
  250. * @return a new RefSpec with destination as specified.
  251. * @throws IllegalArgumentException
  252. * The wildcard status of the new source disagrees with the
  253. * wildcard status of the new destination.
  254. */
  255. public RefSpec setSourceDestination(final String source,
  256. final String destination) {
  257. if (isWildcard(source) != isWildcard(destination))
  258. throw new IllegalStateException(JGitText.get().sourceDestinationMustMatch);
  259. final RefSpec r = new RefSpec(this);
  260. r.wildcard = isWildcard(source);
  261. r.srcName = source;
  262. r.dstName = destination;
  263. return r;
  264. }
  265. /**
  266. * Does this specification's source description match the ref name?
  267. *
  268. * @param r
  269. * ref name that should be tested.
  270. * @return true if the names match; false otherwise.
  271. */
  272. public boolean matchSource(final String r) {
  273. return match(r, getSource());
  274. }
  275. /**
  276. * Does this specification's source description match the ref?
  277. *
  278. * @param r
  279. * ref whose name should be tested.
  280. * @return true if the names match; false otherwise.
  281. */
  282. public boolean matchSource(final Ref r) {
  283. return match(r.getName(), getSource());
  284. }
  285. /**
  286. * Does this specification's destination description match the ref name?
  287. *
  288. * @param r
  289. * ref name that should be tested.
  290. * @return true if the names match; false otherwise.
  291. */
  292. public boolean matchDestination(final String r) {
  293. return match(r, getDestination());
  294. }
  295. /**
  296. * Does this specification's destination description match the ref?
  297. *
  298. * @param r
  299. * ref whose name should be tested.
  300. * @return true if the names match; false otherwise.
  301. */
  302. public boolean matchDestination(final Ref r) {
  303. return match(r.getName(), getDestination());
  304. }
  305. /**
  306. * Expand this specification to exactly match a ref name.
  307. * <p>
  308. * Callers must first verify the passed ref name matches this specification,
  309. * otherwise expansion results may be unpredictable.
  310. *
  311. * @param r
  312. * a ref name that matched our source specification. Could be a
  313. * wildcard also.
  314. * @return a new specification expanded from provided ref name. Result
  315. * specification is wildcard if and only if provided ref name is
  316. * wildcard.
  317. */
  318. public RefSpec expandFromSource(final String r) {
  319. return isWildcard() ? new RefSpec(this).expandFromSourceImp(r) : this;
  320. }
  321. private RefSpec expandFromSourceImp(final String name) {
  322. final String psrc = srcName, pdst = dstName;
  323. wildcard = false;
  324. srcName = name;
  325. dstName = pdst.substring(0, pdst.length() - 1)
  326. + name.substring(psrc.length() - 1);
  327. return this;
  328. }
  329. /**
  330. * Expand this specification to exactly match a ref.
  331. * <p>
  332. * Callers must first verify the passed ref matches this specification,
  333. * otherwise expansion results may be unpredictable.
  334. *
  335. * @param r
  336. * a ref that matched our source specification. Could be a
  337. * wildcard also.
  338. * @return a new specification expanded from provided ref name. Result
  339. * specification is wildcard if and only if provided ref name is
  340. * wildcard.
  341. */
  342. public RefSpec expandFromSource(final Ref r) {
  343. return expandFromSource(r.getName());
  344. }
  345. /**
  346. * Expand this specification to exactly match a ref name.
  347. * <p>
  348. * Callers must first verify the passed ref name matches this specification,
  349. * otherwise expansion results may be unpredictable.
  350. *
  351. * @param r
  352. * a ref name that matched our destination specification. Could
  353. * be a wildcard also.
  354. * @return a new specification expanded from provided ref name. Result
  355. * specification is wildcard if and only if provided ref name is
  356. * wildcard.
  357. */
  358. public RefSpec expandFromDestination(final String r) {
  359. return isWildcard() ? new RefSpec(this).expandFromDstImp(r) : this;
  360. }
  361. private RefSpec expandFromDstImp(final String name) {
  362. final String psrc = srcName, pdst = dstName;
  363. wildcard = false;
  364. srcName = psrc.substring(0, psrc.length() - 1)
  365. + name.substring(pdst.length() - 1);
  366. dstName = name;
  367. return this;
  368. }
  369. /**
  370. * Expand this specification to exactly match a ref.
  371. * <p>
  372. * Callers must first verify the passed ref matches this specification,
  373. * otherwise expansion results may be unpredictable.
  374. *
  375. * @param r
  376. * a ref that matched our destination specification.
  377. * @return a new specification expanded from provided ref name. Result
  378. * specification is wildcard if and only if provided ref name is
  379. * wildcard.
  380. */
  381. public RefSpec expandFromDestination(final Ref r) {
  382. return expandFromDestination(r.getName());
  383. }
  384. private boolean match(final String refName, final String s) {
  385. if (s == null)
  386. return false;
  387. if (isWildcard())
  388. return refName.startsWith(s.substring(0, s.length() - 1));
  389. return refName.equals(s);
  390. }
  391. public int hashCode() {
  392. int hc = 0;
  393. if (getSource() != null)
  394. hc = hc * 31 + getSource().hashCode();
  395. if (getDestination() != null)
  396. hc = hc * 31 + getDestination().hashCode();
  397. return hc;
  398. }
  399. public boolean equals(final Object obj) {
  400. if (!(obj instanceof RefSpec))
  401. return false;
  402. final RefSpec b = (RefSpec) obj;
  403. if (isForceUpdate() != b.isForceUpdate())
  404. return false;
  405. if (isWildcard() != b.isWildcard())
  406. return false;
  407. if (!eq(getSource(), b.getSource()))
  408. return false;
  409. if (!eq(getDestination(), b.getDestination()))
  410. return false;
  411. return true;
  412. }
  413. private static boolean eq(final String a, final String b) {
  414. if (a == b)
  415. return true;
  416. if (a == null || b == null)
  417. return false;
  418. return a.equals(b);
  419. }
  420. public String toString() {
  421. final StringBuilder r = new StringBuilder();
  422. if (isForceUpdate())
  423. r.append('+');
  424. if (getSource() != null)
  425. r.append(getSource());
  426. if (getDestination() != null) {
  427. r.append(':');
  428. r.append(getDestination());
  429. }
  430. return r.toString();
  431. }
  432. }