1 /* 2 * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com> 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 44 package org.eclipse.jgit.transport; 45 46 import java.io.IOException; 47 import java.text.MessageFormat; 48 49 import org.eclipse.jgit.internal.JGitText; 50 import org.eclipse.jgit.lib.ObjectId; 51 import org.eclipse.jgit.lib.Ref; 52 import org.eclipse.jgit.lib.RefUpdate; 53 import org.eclipse.jgit.lib.Repository; 54 import org.eclipse.jgit.revwalk.RevWalk; 55 56 /** 57 * Represent request and status of a remote ref update. Specification is 58 * provided by client, while status is handled by 59 * {@link org.eclipse.jgit.transport.PushProcess} class, being read-only for 60 * client. 61 * <p> 62 * Client can create instances of this class directly, basing on user 63 * specification and advertised refs 64 * ({@link org.eclipse.jgit.transport.Connection} or through 65 * {@link org.eclipse.jgit.transport.Transport} helper methods. Apply this 66 * specification on remote repository using 67 * {@link org.eclipse.jgit.transport.Transport#push(org.eclipse.jgit.lib.ProgressMonitor, java.util.Collection)} 68 * method. 69 * </p> 70 */ 71 public class RemoteRefUpdate { 72 /** 73 * Represent current status of a remote ref update. 74 */ 75 public static enum Status { 76 /** 77 * Push process hasn't yet attempted to update this ref. This is the 78 * default status, prior to push process execution. 79 */ 80 NOT_ATTEMPTED, 81 82 /** 83 * Remote ref was up to date, there was no need to update anything. 84 */ 85 UP_TO_DATE, 86 87 /** 88 * Remote ref update was rejected, as it would cause non fast-forward 89 * update. 90 */ 91 REJECTED_NONFASTFORWARD, 92 93 /** 94 * Remote ref update was rejected, because remote side doesn't 95 * support/allow deleting refs. 96 */ 97 REJECTED_NODELETE, 98 99 /** 100 * Remote ref update was rejected, because old object id on remote 101 * repository wasn't the same as defined expected old object. 102 */ 103 REJECTED_REMOTE_CHANGED, 104 105 /** 106 * Remote ref update was rejected for other reason, possibly described 107 * in {@link RemoteRefUpdate#getMessage()}. 108 */ 109 REJECTED_OTHER_REASON, 110 111 /** 112 * Remote ref didn't exist. Can occur on delete request of a non 113 * existing ref. 114 */ 115 NON_EXISTING, 116 117 /** 118 * Push process is awaiting update report from remote repository. This 119 * is a temporary state or state after critical error in push process. 120 */ 121 AWAITING_REPORT, 122 123 /** 124 * Remote ref was successfully updated. 125 */ 126 OK; 127 } 128 129 private ObjectId expectedOldObjectId; 130 131 private final ObjectId newObjectId; 132 133 private final String remoteName; 134 135 private final TrackingRefUpdate trackingRefUpdate; 136 137 private final String srcRef; 138 139 private final boolean forceUpdate; 140 141 private Status status; 142 143 private boolean fastForward; 144 145 private String message; 146 147 private final Repository localDb; 148 149 private RefUpdate localUpdate; 150 151 /** 152 * Construct remote ref update request by providing an update specification. 153 * Object is created with default 154 * {@link org.eclipse.jgit.transport.RemoteRefUpdate.Status#NOT_ATTEMPTED} 155 * status and no message. 156 * 157 * @param localDb 158 * local repository to push from. 159 * @param srcRef 160 * source revision - any string resolvable by 161 * {@link org.eclipse.jgit.lib.Repository#resolve(String)}. This 162 * resolves to the new object that the caller want remote ref to 163 * be after update. Use null or 164 * {@link org.eclipse.jgit.lib.ObjectId#zeroId()} string for 165 * delete request. 166 * @param remoteName 167 * full name of a remote ref to update, e.g. "refs/heads/master" 168 * (no wildcard, no short name). 169 * @param forceUpdate 170 * true when caller want remote ref to be updated regardless 171 * whether it is fast-forward update (old object is ancestor of 172 * new object). 173 * @param localName 174 * optional full name of a local stored tracking branch, to 175 * update after push, e.g. "refs/remotes/zawir/dirty" (no 176 * wildcard, no short name); null if no local tracking branch 177 * should be updated. 178 * @param expectedOldObjectId 179 * optional object id that caller is expecting, requiring to be 180 * advertised by remote side before update; update will take 181 * place ONLY if remote side advertise exactly this expected id; 182 * null if caller doesn't care what object id remote side 183 * advertise. Use {@link org.eclipse.jgit.lib.ObjectId#zeroId()} 184 * when expecting no remote ref with this name. 185 * @throws java.io.IOException 186 * when I/O error occurred during creating 187 * {@link org.eclipse.jgit.transport.TrackingRefUpdate} for 188 * local tracking branch or srcRef can't be resolved to any 189 * object. 190 * @throws java.lang.IllegalArgumentException 191 * if some required parameter was null 192 */ 193 public RemoteRefUpdate(final Repository localDb, final String srcRef, 194 final String remoteName, final boolean forceUpdate, 195 final String localName, final ObjectId expectedOldObjectId) 196 throws IOException { 197 this(localDb, srcRef, srcRef != null ? localDb.resolve(srcRef) 198 : ObjectId.zeroId(), remoteName, forceUpdate, localName, 199 expectedOldObjectId); 200 } 201 202 /** 203 * Construct remote ref update request by providing an update specification. 204 * Object is created with default 205 * {@link org.eclipse.jgit.transport.RemoteRefUpdate.Status#NOT_ATTEMPTED} 206 * status and no message. 207 * 208 * @param localDb 209 * local repository to push from. 210 * @param srcRef 211 * source revision. Use null to delete. 212 * @param remoteName 213 * full name of a remote ref to update, e.g. "refs/heads/master" 214 * (no wildcard, no short name). 215 * @param forceUpdate 216 * true when caller want remote ref to be updated regardless 217 * whether it is fast-forward update (old object is ancestor of 218 * new object). 219 * @param localName 220 * optional full name of a local stored tracking branch, to 221 * update after push, e.g. "refs/remotes/zawir/dirty" (no 222 * wildcard, no short name); null if no local tracking branch 223 * should be updated. 224 * @param expectedOldObjectId 225 * optional object id that caller is expecting, requiring to be 226 * advertised by remote side before update; update will take 227 * place ONLY if remote side advertise exactly this expected id; 228 * null if caller doesn't care what object id remote side 229 * advertise. Use {@link org.eclipse.jgit.lib.ObjectId#zeroId()} 230 * when expecting no remote ref with this name. 231 * @throws java.io.IOException 232 * when I/O error occurred during creating 233 * {@link org.eclipse.jgit.transport.TrackingRefUpdate} for 234 * local tracking branch or srcRef can't be resolved to any 235 * object. 236 * @throws java.lang.IllegalArgumentException 237 * if some required parameter was null 238 */ 239 public RemoteRefUpdate(final Repository localDb, final Ref srcRef, 240 final String remoteName, final boolean forceUpdate, 241 final String localName, final ObjectId expectedOldObjectId) 242 throws IOException { 243 this(localDb, srcRef != null ? srcRef.getName() : null, 244 srcRef != null ? srcRef.getObjectId() : null, remoteName, 245 forceUpdate, localName, expectedOldObjectId); 246 } 247 248 /** 249 * Construct remote ref update request by providing an update specification. 250 * Object is created with default 251 * {@link org.eclipse.jgit.transport.RemoteRefUpdate.Status#NOT_ATTEMPTED} 252 * status and no message. 253 * 254 * @param localDb 255 * local repository to push from. 256 * @param srcRef 257 * source revision to label srcId with. If null srcId.name() will 258 * be used instead. 259 * @param srcId 260 * The new object that the caller wants remote ref to be after 261 * update. Use null or 262 * {@link org.eclipse.jgit.lib.ObjectId#zeroId()} for delete 263 * request. 264 * @param remoteName 265 * full name of a remote ref to update, e.g. "refs/heads/master" 266 * (no wildcard, no short name). 267 * @param forceUpdate 268 * true when caller want remote ref to be updated regardless 269 * whether it is fast-forward update (old object is ancestor of 270 * new object). 271 * @param localName 272 * optional full name of a local stored tracking branch, to 273 * update after push, e.g. "refs/remotes/zawir/dirty" (no 274 * wildcard, no short name); null if no local tracking branch 275 * should be updated. 276 * @param expectedOldObjectId 277 * optional object id that caller is expecting, requiring to be 278 * advertised by remote side before update; update will take 279 * place ONLY if remote side advertise exactly this expected id; 280 * null if caller doesn't care what object id remote side 281 * advertise. Use {@link org.eclipse.jgit.lib.ObjectId#zeroId()} 282 * when expecting no remote ref with this name. 283 * @throws java.io.IOException 284 * when I/O error occurred during creating 285 * {@link org.eclipse.jgit.transport.TrackingRefUpdate} for 286 * local tracking branch or srcRef can't be resolved to any 287 * object. 288 * @throws java.lang.IllegalArgumentException 289 * if some required parameter was null 290 */ 291 public RemoteRefUpdate(final Repository localDb, final String srcRef, 292 final ObjectId srcId, final String remoteName, 293 final boolean forceUpdate, final String localName, 294 final ObjectId expectedOldObjectId) throws IOException { 295 if (remoteName == null) 296 throw new IllegalArgumentException(JGitText.get().remoteNameCannotBeNull); 297 if (srcId == null && srcRef != null) 298 throw new IOException(MessageFormat.format( 299 JGitText.get().sourceRefDoesntResolveToAnyObject, srcRef)); 300 301 if (srcRef != null) 302 this.srcRef = srcRef; 303 else if (srcId != null && !srcId.equals(ObjectId.zeroId())) 304 this.srcRef = srcId.name(); 305 else 306 this.srcRef = null; 307 308 if (srcId != null) 309 this.newObjectId = srcId; 310 else 311 this.newObjectId = ObjectId.zeroId(); 312 313 this.remoteName = remoteName; 314 this.forceUpdate = forceUpdate; 315 if (localName != null && localDb != null) { 316 localUpdate = localDb.updateRef(localName); 317 localUpdate.setForceUpdate(true); 318 localUpdate.setRefLogMessage("push", true); //$NON-NLS-1$ 319 localUpdate.setNewObjectId(newObjectId); 320 trackingRefUpdate = new TrackingRefUpdate( 321 true, 322 remoteName, 323 localName, 324 localUpdate.getOldObjectId() != null 325 ? localUpdate.getOldObjectId() 326 : ObjectId.zeroId(), 327 newObjectId); 328 } else 329 trackingRefUpdate = null; 330 this.localDb = localDb; 331 this.expectedOldObjectId = expectedOldObjectId; 332 this.status = Status.NOT_ATTEMPTED; 333 } 334 335 /** 336 * Create a new instance of this object basing on existing instance for 337 * configuration. State (like {@link #getMessage()}, {@link #getStatus()}) 338 * of base object is not shared. Expected old object id is set up from 339 * scratch, as this constructor may be used for 2-stage push: first one 340 * being dry run, second one being actual push. 341 * 342 * @param base 343 * configuration base. 344 * @param newExpectedOldObjectId 345 * new expected object id value. 346 * @throws java.io.IOException 347 * when I/O error occurred during creating 348 * {@link org.eclipse.jgit.transport.TrackingRefUpdate} for 349 * local tracking branch or srcRef of base object no longer can 350 * be resolved to any object. 351 */ 352 public RemoteRefUpdateoteRefUpdate.html#RemoteRefUpdate">RemoteRefUpdate(final RemoteRefUpdate base, 353 final ObjectId newExpectedOldObjectId) throws IOException { 354 this(base.localDb, base.srcRef, base.remoteName, base.forceUpdate, 355 (base.trackingRefUpdate == null ? null : base.trackingRefUpdate 356 .getLocalName()), newExpectedOldObjectId); 357 } 358 359 /** 360 * Get expected old object id 361 * 362 * @return expectedOldObjectId required to be advertised by remote side, as 363 * set in constructor; may be null. 364 */ 365 public ObjectId getExpectedOldObjectId() { 366 return expectedOldObjectId; 367 } 368 369 /** 370 * Whether some object is required to be advertised by remote side, as set 371 * in constructor 372 * 373 * @return true if some object is required to be advertised by remote side, 374 * as set in constructor; false otherwise. 375 */ 376 public boolean isExpectingOldObjectId() { 377 return expectedOldObjectId != null; 378 } 379 380 /** 381 * Get new object id 382 * 383 * @return newObjectId for remote ref, as set in constructor. 384 */ 385 public ObjectId getNewObjectId() { 386 return newObjectId; 387 } 388 389 /** 390 * Whether this update is a deleting update 391 * 392 * @return true if this update is deleting update; false otherwise. 393 */ 394 public boolean isDelete() { 395 return ObjectId.zeroId().equals(newObjectId); 396 } 397 398 /** 399 * Get name of remote ref to update 400 * 401 * @return name of remote ref to update, as set in constructor. 402 */ 403 public String getRemoteName() { 404 return remoteName; 405 } 406 407 /** 408 * Get tracking branch update if localName was set in constructor. 409 * 410 * @return local tracking branch update if localName was set in constructor. 411 */ 412 public TrackingRefUpdate getTrackingRefUpdate() { 413 return trackingRefUpdate; 414 } 415 416 /** 417 * Get source revision as specified by user (in constructor) 418 * 419 * @return source revision as specified by user (in constructor), could be 420 * any string parseable by 421 * {@link org.eclipse.jgit.lib.Repository#resolve(String)}; can be 422 * null if specified that way in constructor - this stands for 423 * delete request. 424 */ 425 public String getSrcRef() { 426 return srcRef; 427 } 428 429 /** 430 * Whether user specified a local tracking branch for remote update 431 * 432 * @return true if user specified a local tracking branch for remote update; 433 * false otherwise. 434 */ 435 public boolean hasTrackingRefUpdate() { 436 return trackingRefUpdate != null; 437 } 438 439 /** 440 * Whether this update is forced regardless of old remote ref object 441 * 442 * @return true if this update is forced regardless of old remote ref 443 * object; false otherwise. 444 */ 445 public boolean isForceUpdate() { 446 return forceUpdate; 447 } 448 449 /** 450 * Get status of remote ref update operation. 451 * 452 * @return status of remote ref update operation. 453 */ 454 public Status getStatus() { 455 return status; 456 } 457 458 /** 459 * Check whether update was fast-forward. Note that this result is 460 * meaningful only after successful update (when status is 461 * {@link org.eclipse.jgit.transport.RemoteRefUpdate.Status#OK}). 462 * 463 * @return true if update was fast-forward; false otherwise. 464 */ 465 public boolean isFastForward() { 466 return fastForward; 467 } 468 469 /** 470 * Get message describing reasons of status when needed/possible; may be 471 * null. 472 * 473 * @return message describing reasons of status when needed/possible; may be 474 * null. 475 */ 476 public String getMessage() { 477 return message; 478 } 479 480 void setExpectedOldObjectId(ObjectId id) { 481 expectedOldObjectId = id; 482 } 483 484 void setStatus(Status status) { 485 this.status = status; 486 } 487 488 void setFastForward(boolean fastForward) { 489 this.fastForward = fastForward; 490 } 491 492 void setMessage(String message) { 493 this.message = message; 494 } 495 496 /** 497 * Update locally stored tracking branch with the new object. 498 * 499 * @param walk 500 * walker used for checking update properties. 501 * @throws java.io.IOException 502 * when I/O error occurred during update 503 */ 504 protected void updateTrackingRef(RevWalk walk) throws IOException { 505 if (isDelete()) 506 trackingRefUpdate.setResult(localUpdate.delete(walk)); 507 else 508 trackingRefUpdate.setResult(localUpdate.update(walk)); 509 } 510 511 /** {@inheritDoc} */ 512 @SuppressWarnings("nls") 513 @Override 514 public String toString() { 515 return "RemoteRefUpdate[remoteName=" 516 + remoteName 517 + ", " 518 + status 519 + ", " 520 + (expectedOldObjectId != null ? expectedOldObjectId.name() 521 : "(null)") + "..." 522 + (newObjectId != null ? newObjectId.name() : "(null)") 523 + (fastForward ? ", fastForward" : "") 524 + ", srcRef=" + srcRef 525 + (forceUpdate ? ", forceUpdate" : "") + ", message=" 526 + (message != null ? "\"" + message + "\"" : "null") + "]"; 527 } 528 }