Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

RefSpec.java 18KB

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