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