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 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  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.io.Serializable;
  45. import java.text.MessageFormat;
  46. import org.eclipse.jgit.internal.JGitText;
  47. import org.eclipse.jgit.lib.Constants;
  48. import org.eclipse.jgit.lib.Ref;
  49. import org.eclipse.jgit.util.References;
  50. /**
  51. * Describes how refs in one repository copy into another repository.
  52. * <p>
  53. * A ref specification provides matching support and limited rules to rewrite a
  54. * reference in one repository to another reference in another repository.
  55. */
  56. public class RefSpec implements Serializable {
  57. private static final long serialVersionUID = 1L;
  58. /**
  59. * Suffix for wildcard ref spec component, that indicate matching all refs
  60. * with specified prefix.
  61. */
  62. public static final String WILDCARD_SUFFIX = "/*"; //$NON-NLS-1$
  63. /**
  64. * Check whether provided string is a wildcard ref spec component.
  65. *
  66. * @param s
  67. * ref spec component - string to test. Can be null.
  68. * @return true if provided string is a wildcard ref spec component.
  69. */
  70. public static boolean isWildcard(String s) {
  71. return s != null && s.contains("*"); //$NON-NLS-1$
  72. }
  73. /** Does this specification ask for forced updated (rewind/reset)? */
  74. private boolean force;
  75. /** Is this specification actually a wildcard match? */
  76. private boolean wildcard;
  77. /**
  78. * How strict to be about wildcards.
  79. *
  80. * @since 4.5
  81. */
  82. public enum WildcardMode {
  83. /**
  84. * Reject refspecs with an asterisk on the source side and not the
  85. * destination side or vice versa. This is the mode used by FetchCommand
  86. * and PushCommand to create a one-to-one mapping between source and
  87. * destination refs.
  88. */
  89. REQUIRE_MATCH,
  90. /**
  91. * Allow refspecs with an asterisk on only one side. This can create a
  92. * many-to-one mapping between source and destination refs, so
  93. * expandFromSource and expandFromDestination are not usable in this
  94. * mode.
  95. */
  96. ALLOW_MISMATCH
  97. }
  98. /** Whether a wildcard is allowed on one side but not the other. */
  99. private WildcardMode allowMismatchedWildcards;
  100. /** Name of the ref(s) we would copy from. */
  101. private String srcName;
  102. /** Name of the ref(s) we would copy into. */
  103. private String dstName;
  104. /**
  105. * Construct an empty RefSpec.
  106. * <p>
  107. * A newly created empty RefSpec is not suitable for use in most
  108. * applications, as at least one field must be set to match a source name.
  109. */
  110. public RefSpec() {
  111. force = false;
  112. wildcard = false;
  113. srcName = Constants.HEAD;
  114. dstName = null;
  115. allowMismatchedWildcards = WildcardMode.REQUIRE_MATCH;
  116. }
  117. /**
  118. * Parse a ref specification for use during transport operations.
  119. * <p>
  120. * Specifications are typically one of the following forms:
  121. * <ul>
  122. * <li><code>refs/heads/master</code></li>
  123. * <li><code>refs/heads/master:refs/remotes/origin/master</code></li>
  124. * <li><code>refs/heads/*:refs/remotes/origin/*</code></li>
  125. * <li><code>+refs/heads/master</code></li>
  126. * <li><code>+refs/heads/master:refs/remotes/origin/master</code></li>
  127. * <li><code>+refs/heads/*:refs/remotes/origin/*</code></li>
  128. * <li><code>+refs/pull/&#42;/head:refs/remotes/origin/pr/*</code></li>
  129. * <li><code>:refs/heads/master</code></li>
  130. * </ul>
  131. *
  132. * If the wildcard mode allows mismatches, then these ref specs are also
  133. * valid:
  134. * <ul>
  135. * <li><code>refs/heads/*</code></li>
  136. * <li><code>refs/heads/*:refs/heads/master</code></li>
  137. * </ul>
  138. *
  139. * @param spec
  140. * string describing the specification.
  141. * @param mode
  142. * whether to allow a wildcard on one side without a wildcard on
  143. * the other.
  144. * @throws java.lang.IllegalArgumentException
  145. * the specification is invalid.
  146. * @since 4.5
  147. */
  148. public RefSpec(String spec, WildcardMode mode) {
  149. this.allowMismatchedWildcards = mode;
  150. String s = spec;
  151. if (s.startsWith("+")) { //$NON-NLS-1$
  152. force = true;
  153. s = s.substring(1);
  154. }
  155. final int c = s.lastIndexOf(':');
  156. if (c == 0) {
  157. s = s.substring(1);
  158. if (isWildcard(s)) {
  159. wildcard = true;
  160. if (mode == WildcardMode.REQUIRE_MATCH) {
  161. throw new IllegalArgumentException(MessageFormat
  162. .format(JGitText.get().invalidWildcards, spec));
  163. }
  164. }
  165. dstName = checkValid(s);
  166. } else if (c > 0) {
  167. String src = s.substring(0, c);
  168. String dst = s.substring(c + 1);
  169. if (isWildcard(src) && isWildcard(dst)) {
  170. // Both contain wildcard
  171. wildcard = true;
  172. } else if (isWildcard(src) || isWildcard(dst)) {
  173. wildcard = true;
  174. if (mode == WildcardMode.REQUIRE_MATCH)
  175. throw new IllegalArgumentException(MessageFormat
  176. .format(JGitText.get().invalidWildcards, spec));
  177. }
  178. srcName = checkValid(src);
  179. dstName = checkValid(dst);
  180. } else {
  181. if (isWildcard(s)) {
  182. if (mode == WildcardMode.REQUIRE_MATCH) {
  183. throw new IllegalArgumentException(MessageFormat
  184. .format(JGitText.get().invalidWildcards, spec));
  185. }
  186. wildcard = true;
  187. }
  188. srcName = checkValid(s);
  189. }
  190. }
  191. /**
  192. * Parse a ref specification for use during transport operations.
  193. * <p>
  194. * Specifications are typically one of the following forms:
  195. * <ul>
  196. * <li><code>refs/heads/master</code></li>
  197. * <li><code>refs/heads/master:refs/remotes/origin/master</code></li>
  198. * <li><code>refs/heads/*:refs/remotes/origin/*</code></li>
  199. * <li><code>+refs/heads/master</code></li>
  200. * <li><code>+refs/heads/master:refs/remotes/origin/master</code></li>
  201. * <li><code>+refs/heads/*:refs/remotes/origin/*</code></li>
  202. * <li><code>+refs/pull/&#42;/head:refs/remotes/origin/pr/*</code></li>
  203. * <li><code>:refs/heads/master</code></li>
  204. * </ul>
  205. *
  206. * @param spec
  207. * string describing the specification.
  208. * @throws java.lang.IllegalArgumentException
  209. * the specification is invalid.
  210. */
  211. public RefSpec(String spec) {
  212. this(spec, WildcardMode.REQUIRE_MATCH);
  213. }
  214. private RefSpec(RefSpec p) {
  215. force = p.isForceUpdate();
  216. wildcard = p.isWildcard();
  217. srcName = p.getSource();
  218. dstName = p.getDestination();
  219. allowMismatchedWildcards = p.allowMismatchedWildcards;
  220. }
  221. /**
  222. * Check if this specification wants to forcefully update the destination.
  223. *
  224. * @return true if this specification asks for updates without merge tests.
  225. */
  226. public boolean isForceUpdate() {
  227. return force;
  228. }
  229. /**
  230. * Create a new RefSpec with a different force update setting.
  231. *
  232. * @param forceUpdate
  233. * new value for force update in the returned instance.
  234. * @return a new RefSpec with force update as specified.
  235. */
  236. public RefSpec setForceUpdate(boolean forceUpdate) {
  237. final RefSpec r = new RefSpec(this);
  238. r.force = forceUpdate;
  239. return r;
  240. }
  241. /**
  242. * Check if this specification is actually a wildcard pattern.
  243. * <p>
  244. * If this is a wildcard pattern then the source and destination names
  245. * returned by {@link #getSource()} and {@link #getDestination()} will not
  246. * be actual ref names, but instead will be patterns.
  247. *
  248. * @return true if this specification could match more than one ref.
  249. */
  250. public boolean isWildcard() {
  251. return wildcard;
  252. }
  253. /**
  254. * Get the source ref description.
  255. * <p>
  256. * During a fetch this is the name of the ref on the remote repository we
  257. * are fetching from. During a push this is the name of the ref on the local
  258. * repository we are pushing out from.
  259. *
  260. * @return name (or wildcard pattern) to match the source ref.
  261. */
  262. public String getSource() {
  263. return srcName;
  264. }
  265. /**
  266. * Create a new RefSpec with a different source name setting.
  267. *
  268. * @param source
  269. * new value for source in the returned instance.
  270. * @return a new RefSpec with source as specified.
  271. * @throws java.lang.IllegalStateException
  272. * There is already a destination configured, and the wildcard
  273. * status of the existing destination disagrees with the
  274. * wildcard status of the new source.
  275. */
  276. public RefSpec setSource(String source) {
  277. final RefSpec r = new RefSpec(this);
  278. r.srcName = checkValid(source);
  279. if (isWildcard(r.srcName) && r.dstName == null)
  280. throw new IllegalStateException(JGitText.get().destinationIsNotAWildcard);
  281. if (isWildcard(r.srcName) != isWildcard(r.dstName))
  282. throw new IllegalStateException(JGitText.get().sourceDestinationMustMatch);
  283. return r;
  284. }
  285. /**
  286. * Get the destination ref description.
  287. * <p>
  288. * During a fetch this is the local tracking branch that will be updated
  289. * with the new ObjectId after fetching is complete. During a push this is
  290. * the remote ref that will be updated by the remote's receive-pack process.
  291. * <p>
  292. * If null during a fetch no tracking branch should be updated and the
  293. * ObjectId should be stored transiently in order to prepare a merge.
  294. * <p>
  295. * If null during a push, use {@link #getSource()} instead.
  296. *
  297. * @return name (or wildcard) pattern to match the destination ref.
  298. */
  299. public String getDestination() {
  300. return dstName;
  301. }
  302. /**
  303. * Create a new RefSpec with a different destination name setting.
  304. *
  305. * @param destination
  306. * new value for destination in the returned instance.
  307. * @return a new RefSpec with destination as specified.
  308. * @throws java.lang.IllegalStateException
  309. * There is already a source configured, and the wildcard status
  310. * of the existing source disagrees with the wildcard status of
  311. * the new destination.
  312. */
  313. public RefSpec setDestination(String destination) {
  314. final RefSpec r = new RefSpec(this);
  315. r.dstName = checkValid(destination);
  316. if (isWildcard(r.dstName) && r.srcName == null)
  317. throw new IllegalStateException(JGitText.get().sourceIsNotAWildcard);
  318. if (isWildcard(r.srcName) != isWildcard(r.dstName))
  319. throw new IllegalStateException(JGitText.get().sourceDestinationMustMatch);
  320. return r;
  321. }
  322. /**
  323. * Create a new RefSpec with a different source/destination name setting.
  324. *
  325. * @param source
  326. * new value for source in the returned instance.
  327. * @param destination
  328. * new value for destination in the returned instance.
  329. * @return a new RefSpec with destination as specified.
  330. * @throws java.lang.IllegalArgumentException
  331. * The wildcard status of the new source disagrees with the
  332. * wildcard status of the new destination.
  333. */
  334. public RefSpec setSourceDestination(final String source,
  335. final String destination) {
  336. if (isWildcard(source) != isWildcard(destination))
  337. throw new IllegalStateException(JGitText.get().sourceDestinationMustMatch);
  338. final RefSpec r = new RefSpec(this);
  339. r.wildcard = isWildcard(source);
  340. r.srcName = source;
  341. r.dstName = destination;
  342. return r;
  343. }
  344. /**
  345. * Does this specification's source description match the ref name?
  346. *
  347. * @param r
  348. * ref name that should be tested.
  349. * @return true if the names match; false otherwise.
  350. */
  351. public boolean matchSource(String r) {
  352. return match(r, getSource());
  353. }
  354. /**
  355. * Does this specification's source description match the ref?
  356. *
  357. * @param r
  358. * ref whose name should be tested.
  359. * @return true if the names match; false otherwise.
  360. */
  361. public boolean matchSource(Ref r) {
  362. return match(r.getName(), getSource());
  363. }
  364. /**
  365. * Does this specification's destination description match the ref name?
  366. *
  367. * @param r
  368. * ref name that should be tested.
  369. * @return true if the names match; false otherwise.
  370. */
  371. public boolean matchDestination(String r) {
  372. return match(r, getDestination());
  373. }
  374. /**
  375. * Does this specification's destination description match the ref?
  376. *
  377. * @param r
  378. * ref whose name should be tested.
  379. * @return true if the names match; false otherwise.
  380. */
  381. public boolean matchDestination(Ref r) {
  382. return match(r.getName(), getDestination());
  383. }
  384. /**
  385. * Expand this specification to exactly match a ref name.
  386. * <p>
  387. * Callers must first verify the passed ref name matches this specification,
  388. * otherwise expansion results may be unpredictable.
  389. *
  390. * @param r
  391. * a ref name that matched our source specification. Could be a
  392. * wildcard also.
  393. * @return a new specification expanded from provided ref name. Result
  394. * specification is wildcard if and only if provided ref name is
  395. * wildcard.
  396. * @throws java.lang.IllegalStateException
  397. * when the RefSpec was constructed with wildcard mode that
  398. * doesn't require matching wildcards.
  399. */
  400. public RefSpec expandFromSource(String r) {
  401. if (allowMismatchedWildcards != WildcardMode.REQUIRE_MATCH) {
  402. throw new IllegalStateException(
  403. JGitText.get().invalidExpandWildcard);
  404. }
  405. return isWildcard() ? new RefSpec(this).expandFromSourceImp(r) : this;
  406. }
  407. private RefSpec expandFromSourceImp(String name) {
  408. final String psrc = srcName, pdst = dstName;
  409. wildcard = false;
  410. srcName = name;
  411. dstName = expandWildcard(name, psrc, pdst);
  412. return this;
  413. }
  414. /**
  415. * Expand this specification to exactly match a ref.
  416. * <p>
  417. * Callers must first verify the passed ref matches this specification,
  418. * otherwise expansion results may be unpredictable.
  419. *
  420. * @param r
  421. * a ref that matched our source specification. Could be a
  422. * wildcard also.
  423. * @return a new specification expanded from provided ref name. Result
  424. * specification is wildcard if and only if provided ref name is
  425. * wildcard.
  426. * @throws java.lang.IllegalStateException
  427. * when the RefSpec was constructed with wildcard mode that
  428. * doesn't require matching wildcards.
  429. */
  430. public RefSpec expandFromSource(Ref r) {
  431. return expandFromSource(r.getName());
  432. }
  433. /**
  434. * Expand this specification to exactly match a ref name.
  435. * <p>
  436. * Callers must first verify the passed ref name matches this specification,
  437. * otherwise expansion results may be unpredictable.
  438. *
  439. * @param r
  440. * a ref name that matched our destination specification. Could
  441. * be a wildcard also.
  442. * @return a new specification expanded from provided ref name. Result
  443. * specification is wildcard if and only if provided ref name is
  444. * wildcard.
  445. * @throws java.lang.IllegalStateException
  446. * when the RefSpec was constructed with wildcard mode that
  447. * doesn't require matching wildcards.
  448. */
  449. public RefSpec expandFromDestination(String r) {
  450. if (allowMismatchedWildcards != WildcardMode.REQUIRE_MATCH) {
  451. throw new IllegalStateException(
  452. JGitText.get().invalidExpandWildcard);
  453. }
  454. return isWildcard() ? new RefSpec(this).expandFromDstImp(r) : this;
  455. }
  456. private RefSpec expandFromDstImp(String name) {
  457. final String psrc = srcName, pdst = dstName;
  458. wildcard = false;
  459. srcName = expandWildcard(name, pdst, psrc);
  460. dstName = name;
  461. return this;
  462. }
  463. /**
  464. * Expand this specification to exactly match a ref.
  465. * <p>
  466. * Callers must first verify the passed ref matches this specification,
  467. * otherwise expansion results may be unpredictable.
  468. *
  469. * @param r
  470. * a ref that matched our destination specification.
  471. * @return a new specification expanded from provided ref name. Result
  472. * specification is wildcard if and only if provided ref name is
  473. * wildcard.
  474. * @throws java.lang.IllegalStateException
  475. * when the RefSpec was constructed with wildcard mode that
  476. * doesn't require matching wildcards.
  477. */
  478. public RefSpec expandFromDestination(Ref r) {
  479. return expandFromDestination(r.getName());
  480. }
  481. private boolean match(String name, String s) {
  482. if (s == null)
  483. return false;
  484. if (isWildcard(s)) {
  485. int wildcardIndex = s.indexOf('*');
  486. String prefix = s.substring(0, wildcardIndex);
  487. String suffix = s.substring(wildcardIndex + 1);
  488. return name.length() > prefix.length() + suffix.length()
  489. && name.startsWith(prefix) && name.endsWith(suffix);
  490. }
  491. return name.equals(s);
  492. }
  493. private static String expandWildcard(String name, String patternA,
  494. String patternB) {
  495. int a = patternA.indexOf('*');
  496. int trailingA = patternA.length() - (a + 1);
  497. int b = patternB.indexOf('*');
  498. String match = name.substring(a, name.length() - trailingA);
  499. return patternB.substring(0, b) + match + patternB.substring(b + 1);
  500. }
  501. private static String checkValid(String spec) {
  502. if (spec != null && !isValid(spec))
  503. throw new IllegalArgumentException(MessageFormat.format(
  504. JGitText.get().invalidRefSpec, spec));
  505. return spec;
  506. }
  507. private static boolean isValid(String s) {
  508. if (s.startsWith("/")) //$NON-NLS-1$
  509. return false;
  510. if (s.contains("//")) //$NON-NLS-1$
  511. return false;
  512. if (s.endsWith("/")) //$NON-NLS-1$
  513. return false;
  514. int i = s.indexOf('*');
  515. if (i != -1) {
  516. if (s.indexOf('*', i + 1) > i)
  517. return false;
  518. }
  519. return true;
  520. }
  521. /** {@inheritDoc} */
  522. @Override
  523. public int hashCode() {
  524. int hc = 0;
  525. if (getSource() != null)
  526. hc = hc * 31 + getSource().hashCode();
  527. if (getDestination() != null)
  528. hc = hc * 31 + getDestination().hashCode();
  529. return hc;
  530. }
  531. /** {@inheritDoc} */
  532. @Override
  533. public boolean equals(Object obj) {
  534. if (!(obj instanceof RefSpec))
  535. return false;
  536. final RefSpec b = (RefSpec) obj;
  537. if (isForceUpdate() != b.isForceUpdate())
  538. return false;
  539. if (isWildcard() != b.isWildcard())
  540. return false;
  541. if (!eq(getSource(), b.getSource()))
  542. return false;
  543. if (!eq(getDestination(), b.getDestination()))
  544. return false;
  545. return true;
  546. }
  547. private static boolean eq(String a, String b) {
  548. if (References.isSameObject(a, b)) {
  549. return true;
  550. }
  551. if (a == null || b == null)
  552. return false;
  553. return a.equals(b);
  554. }
  555. /** {@inheritDoc} */
  556. @Override
  557. public String toString() {
  558. final StringBuilder r = new StringBuilder();
  559. if (isForceUpdate())
  560. r.append('+');
  561. if (getSource() != null)
  562. r.append(getSource());
  563. if (getDestination() != null) {
  564. r.append(':');
  565. r.append(getDestination());
  566. }
  567. return r.toString();
  568. }
  569. }