1 /* 2 * Copyright (C) 2008-2010, Google Inc. 3 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> 4 * and other copyright owners as documented in the project's IP log. 5 * 6 * This program and the accompanying materials are made available 7 * under the terms of the Eclipse Distribution License v1.0 which 8 * accompanies this distribution, is reproduced below, and is 9 * available at http://www.eclipse.org/org/documents/edl-v10.php 10 * 11 * All rights reserved. 12 * 13 * Redistribution and use in source and binary forms, with or 14 * without modification, are permitted provided that the following 15 * conditions are met: 16 * 17 * - Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 20 * - Redistributions in binary form must reproduce the above 21 * copyright notice, this list of conditions and the following 22 * disclaimer in the documentation and/or other materials provided 23 * with the distribution. 24 * 25 * - Neither the name of the Eclipse Foundation, Inc. nor the 26 * names of its contributors may be used to endorse or promote 27 * products derived from this software without specific prior 28 * written permission. 29 * 30 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 31 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 32 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 33 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 34 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 35 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 36 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 37 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 38 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 39 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 40 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 41 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 42 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 43 */ 44 45 package org.eclipse.jgit.lib; 46 47 import java.io.IOException; 48 import java.text.MessageFormat; 49 50 import org.eclipse.jgit.errors.MissingObjectException; 51 import org.eclipse.jgit.internal.JGitText; 52 import org.eclipse.jgit.revwalk.RevCommit; 53 import org.eclipse.jgit.revwalk.RevObject; 54 import org.eclipse.jgit.revwalk.RevWalk; 55 import org.eclipse.jgit.transport.PushCertificate; 56 57 /** 58 * Creates, updates or deletes any reference. 59 */ 60 public abstract class RefUpdate { 61 /** Status of an update request. */ 62 public static enum Result { 63 /** The ref update/delete has not been attempted by the caller. */ 64 NOT_ATTEMPTED, 65 66 /** 67 * The ref could not be locked for update/delete. 68 * <p> 69 * This is generally a transient failure and is usually caused by 70 * another process trying to access the ref at the same time as this 71 * process was trying to update it. It is possible a future operation 72 * will be successful. 73 */ 74 LOCK_FAILURE, 75 76 /** 77 * Same value already stored. 78 * <p> 79 * Both the old value and the new value are identical. No change was 80 * necessary for an update. For delete the branch is removed. 81 */ 82 NO_CHANGE, 83 84 /** 85 * The ref was created locally for an update, but ignored for delete. 86 * <p> 87 * The ref did not exist when the update started, but it was created 88 * successfully with the new value. 89 */ 90 NEW, 91 92 /** 93 * The ref had to be forcefully updated/deleted. 94 * <p> 95 * The ref already existed but its old value was not fully merged into 96 * the new value. The configuration permitted a forced update to take 97 * place, so ref now contains the new value. History associated with the 98 * objects not merged may no longer be reachable. 99 */ 100 FORCED, 101 102 /** 103 * The ref was updated/deleted in a fast-forward way. 104 * <p> 105 * The tracking ref already existed and its old value was fully merged 106 * into the new value. No history was made unreachable. 107 */ 108 FAST_FORWARD, 109 110 /** 111 * Not a fast-forward and not stored. 112 * <p> 113 * The tracking ref already existed but its old value was not fully 114 * merged into the new value. The configuration did not allow a forced 115 * update/delete to take place, so ref still contains the old value. No 116 * previous history was lost. 117 */ 118 REJECTED, 119 120 /** 121 * Rejected because trying to delete the current branch. 122 * <p> 123 * Has no meaning for update. 124 */ 125 REJECTED_CURRENT_BRANCH, 126 127 /** 128 * The ref was probably not updated/deleted because of I/O error. 129 * <p> 130 * Unexpected I/O error occurred when writing new ref. Such error may 131 * result in uncertain state, but most probably ref was not updated. 132 * <p> 133 * This kind of error doesn't include {@link #LOCK_FAILURE}, which is a 134 * different case. 135 */ 136 IO_FAILURE, 137 138 /** 139 * The ref was renamed from another name 140 * <p> 141 */ 142 RENAMED 143 } 144 145 /** New value the caller wants this ref to have. */ 146 private ObjectId newValue; 147 148 /** Does this specification ask for forced updated (rewind/reset)? */ 149 private boolean force; 150 151 /** Identity to record action as within the reflog. */ 152 private PersonIdent refLogIdent; 153 154 /** Message the caller wants included in the reflog. */ 155 private String refLogMessage; 156 157 /** Should the Result value be appended to {@link #refLogMessage}. */ 158 private boolean refLogIncludeResult; 159 160 /** Old value of the ref, obtained after we lock it. */ 161 private ObjectId oldValue; 162 163 /** If non-null, the value {@link #oldValue} must have to continue. */ 164 private ObjectId expValue; 165 166 /** Result of the update operation. */ 167 private Result result = Result.NOT_ATTEMPTED; 168 169 /** Push certificate associated with this update. */ 170 private PushCertificate pushCert; 171 172 private final Ref ref; 173 174 /** 175 * Is this RefUpdate detaching a symbolic ref? 176 * 177 * We need this info since this.ref will normally be peeled of in case of 178 * detaching a symbolic ref (HEAD for example). 179 * 180 * Without this flag we cannot decide whether the ref has to be updated or 181 * not in case when it was a symbolic ref and the newValue == oldValue. 182 */ 183 private boolean detachingSymbolicRef; 184 185 private boolean checkConflicting = true; 186 187 /** 188 * Construct a new update operation for the reference. 189 * <p> 190 * {@code ref.getObjectId()} will be used to seed {@link #getOldObjectId()}, 191 * which callers can use as part of their own update logic. 192 * 193 * @param ref 194 * the reference that will be updated by this operation. 195 */ 196 protected RefUpdate(final Ref ref) { 197 this.ref = ref; 198 oldValue = ref.getObjectId(); 199 refLogMessage = ""; //$NON-NLS-1$ 200 } 201 202 /** @return the reference database this update modifies. */ 203 protected abstract RefDatabase getRefDatabase(); 204 205 /** @return the repository storing the database's objects. */ 206 protected abstract Repository getRepository(); 207 208 /** 209 * Try to acquire the lock on the reference. 210 * <p> 211 * If the locking was successful the implementor must set the current 212 * identity value by calling {@link #setOldObjectId(ObjectId)}. 213 * 214 * @param deref 215 * true if the lock should be taken against the leaf level 216 * reference; false if it should be taken exactly against the 217 * current reference. 218 * @return true if the lock was acquired and the reference is likely 219 * protected from concurrent modification; false if it failed. 220 * @throws IOException 221 * the lock couldn't be taken due to an unexpected storage 222 * failure, and not because of a concurrent update. 223 */ 224 protected abstract boolean tryLock(boolean deref) throws IOException; 225 226 /** Releases the lock taken by {@link #tryLock} if it succeeded. */ 227 protected abstract void unlock(); 228 229 /** 230 * @param desiredResult 231 * @return {@code result} 232 * @throws IOException 233 */ 234 protected abstract Result doUpdate(Result desiredResult) throws IOException; 235 236 /** 237 * @param desiredResult 238 * @return {@code result} 239 * @throws IOException 240 */ 241 protected abstract Result doDelete(Result desiredResult) throws IOException; 242 243 /** 244 * @param target 245 * @return {@link Result#NEW} on success. 246 * @throws IOException 247 */ 248 protected abstract Result doLink(String target) throws IOException; 249 250 /** 251 * Get the name of the ref this update will operate on. 252 * 253 * @return name of underlying ref. 254 */ 255 public String getName() { 256 return getRef().getName(); 257 } 258 259 /** @return the reference this update will create or modify. */ 260 public Ref getRef() { 261 return ref; 262 } 263 264 /** 265 * Get the new value the ref will be (or was) updated to. 266 * 267 * @return new value. Null if the caller has not configured it. 268 */ 269 public ObjectId getNewObjectId() { 270 return newValue; 271 } 272 273 /** 274 * Tells this RefUpdate that it is actually detaching a symbolic ref. 275 */ 276 public void setDetachingSymbolicRef() { 277 detachingSymbolicRef = true; 278 } 279 280 /** 281 * Set the new value the ref will update to. 282 * 283 * @param id 284 * the new value. 285 */ 286 public void setNewObjectId(final AnyObjectId id) { 287 newValue = id.copy(); 288 } 289 290 /** 291 * @return the expected value of the ref after the lock is taken, but before 292 * update occurs. Null to avoid the compare and swap test. Use 293 * {@link ObjectId#zeroId()} to indicate expectation of a 294 * non-existant ref. 295 */ 296 public ObjectId getExpectedOldObjectId() { 297 return expValue; 298 } 299 300 /** 301 * @param id 302 * the expected value of the ref after the lock is taken, but 303 * before update occurs. Null to avoid the compare and swap test. 304 * Use {@link ObjectId#zeroId()} to indicate expectation of a 305 * non-existant ref. 306 */ 307 public void setExpectedOldObjectId(final AnyObjectId id) { 308 expValue = id != null ? id.toObjectId() : null; 309 } 310 311 /** 312 * Check if this update wants to forcefully change the ref. 313 * 314 * @return true if this update should ignore merge tests. 315 */ 316 public boolean isForceUpdate() { 317 return force; 318 } 319 320 /** 321 * Set if this update wants to forcefully change the ref. 322 * 323 * @param b 324 * true if this update should ignore merge tests. 325 */ 326 public void setForceUpdate(final boolean b) { 327 force = b; 328 } 329 330 /** @return identity of the user making the change in the reflog. */ 331 public PersonIdent getRefLogIdent() { 332 return refLogIdent; 333 } 334 335 /** 336 * Set the identity of the user appearing in the reflog. 337 * <p> 338 * The timestamp portion of the identity is ignored. A new identity with the 339 * current timestamp will be created automatically when the update occurs 340 * and the log record is written. 341 * 342 * @param pi 343 * identity of the user. If null the identity will be 344 * automatically determined based on the repository 345 * configuration. 346 */ 347 public void setRefLogIdent(final PersonIdent pi) { 348 refLogIdent = pi; 349 } 350 351 /** 352 * Get the message to include in the reflog. 353 * 354 * @return message the caller wants to include in the reflog; null if the 355 * update should not be logged. 356 */ 357 public String getRefLogMessage() { 358 return refLogMessage; 359 } 360 361 /** @return {@code true} if the ref log message should show the result. */ 362 protected boolean isRefLogIncludingResult() { 363 return refLogIncludeResult; 364 } 365 366 /** 367 * Set the message to include in the reflog. 368 * 369 * @param msg 370 * the message to describe this change. It may be null if 371 * appendStatus is null in order not to append to the reflog 372 * @param appendStatus 373 * true if the status of the ref change (fast-forward or 374 * forced-update) should be appended to the user supplied 375 * message. 376 */ 377 public void setRefLogMessage(final String msg, final boolean appendStatus) { 378 if (msg == null && !appendStatus) 379 disableRefLog(); 380 else if (msg == null && appendStatus) { 381 refLogMessage = ""; //$NON-NLS-1$ 382 refLogIncludeResult = true; 383 } else { 384 refLogMessage = msg; 385 refLogIncludeResult = appendStatus; 386 } 387 } 388 389 /** Don't record this update in the ref's associated reflog. */ 390 public void disableRefLog() { 391 refLogMessage = null; 392 refLogIncludeResult = false; 393 } 394 395 /** 396 * The old value of the ref, prior to the update being attempted. 397 * <p> 398 * This value may differ before and after the update method. Initially it is 399 * populated with the value of the ref before the lock is taken, but the old 400 * value may change if someone else modified the ref between the time we 401 * last read it and when the ref was locked for update. 402 * 403 * @return the value of the ref prior to the update being attempted; null if 404 * the updated has not been attempted yet. 405 */ 406 public ObjectId getOldObjectId() { 407 return oldValue; 408 } 409 410 /** 411 * Set the old value of the ref. 412 * 413 * @param old 414 * the old value. 415 */ 416 protected void setOldObjectId(ObjectId old) { 417 oldValue = old; 418 } 419 420 /** 421 * Set a push certificate associated with this update. 422 * <p> 423 * This usually includes a command to update this ref, but is not required to. 424 * 425 * @param cert 426 * push certificate, may be null. 427 * @since 4.1 428 */ 429 public void setPushCertificate(PushCertificate cert) { 430 pushCert = cert; 431 } 432 433 /** 434 * Set the push certificate associated with this update. 435 * <p> 436 * This usually includes a command to update this ref, but is not required to. 437 * 438 * @return push certificate, may be null. 439 * @since 4.1 440 */ 441 protected PushCertificate getPushCertificate() { 442 return pushCert; 443 } 444 445 /** 446 * Get the status of this update. 447 * <p> 448 * The same value that was previously returned from an update method. 449 * 450 * @return the status of the update. 451 */ 452 public Result getResult() { 453 return result; 454 } 455 456 private void requireCanDoUpdate() { 457 if (newValue == null) 458 throw new IllegalStateException(JGitText.get().aNewObjectIdIsRequired); 459 } 460 461 /** 462 * Force the ref to take the new value. 463 * <p> 464 * This is just a convenient helper for setting the force flag, and as such 465 * the merge test is performed. 466 * 467 * @return the result status of the update. 468 * @throws IOException 469 * an unexpected IO error occurred while writing changes. 470 */ 471 public Result forceUpdate() throws IOException { 472 force = true; 473 return update(); 474 } 475 476 /** 477 * Gracefully update the ref to the new value. 478 * <p> 479 * Merge test will be performed according to {@link #isForceUpdate()}. 480 * <p> 481 * This is the same as: 482 * 483 * <pre> 484 * return update(new RevWalk(getRepository())); 485 * </pre> 486 * 487 * @return the result status of the update. 488 * @throws IOException 489 * an unexpected IO error occurred while writing changes. 490 */ 491 public Result update() throws IOException { 492 try (RevWalk rw = new RevWalk(getRepository())) { 493 return update(rw); 494 } 495 } 496 497 /** 498 * Gracefully update the ref to the new value. 499 * <p> 500 * Merge test will be performed according to {@link #isForceUpdate()}. 501 * 502 * @param walk 503 * a RevWalk instance this update command can borrow to perform 504 * the merge test. The walk will be reset to perform the test. 505 * @return the result status of the update. 506 * @throws IOException 507 * an unexpected IO error occurred while writing changes. 508 */ 509 public Result update(final RevWalk walk) throws IOException { 510 requireCanDoUpdate(); 511 try { 512 return result = updateImpl(walk, new Store() { 513 @Override 514 Result execute(Result status) throws IOException { 515 if (status == Result.NO_CHANGE) 516 return status; 517 return doUpdate(status); 518 } 519 }); 520 } catch (IOException x) { 521 result = Result.IO_FAILURE; 522 throw x; 523 } 524 } 525 526 /** 527 * Delete the ref. 528 * <p> 529 * This is the same as: 530 * 531 * <pre> 532 * return delete(new RevWalk(getRepository())); 533 * </pre> 534 * 535 * @return the result status of the delete. 536 * @throws IOException 537 */ 538 public Result delete() throws IOException { 539 try (RevWalk rw = new RevWalk(getRepository())) { 540 return delete(rw); 541 } 542 } 543 544 /** 545 * Delete the ref. 546 * 547 * @param walk 548 * a RevWalk instance this delete command can borrow to perform 549 * the merge test. The walk will be reset to perform the test. 550 * @return the result status of the delete. 551 * @throws IOException 552 */ 553 public Result delete(final RevWalk walk) throws IOException { 554 final String myName = detachingSymbolicRef 555 ? getRef().getName() 556 : getRef().getLeaf().getName(); 557 if (myName.startsWith(Constants.R_HEADS) && !getRepository().isBare()) { 558 // Don't allow the currently checked out branch to be deleted. 559 Ref head = getRefDatabase().getRef(Constants.HEAD); 560 while (head != null && head.isSymbolic()) { 561 head = head.getTarget(); 562 if (myName.equals(head.getName())) 563 return result = Result.REJECTED_CURRENT_BRANCH; 564 } 565 } 566 567 try { 568 return result = updateImpl(walk, new Store() { 569 @Override 570 Result execute(Result status) throws IOException { 571 return doDelete(status); 572 } 573 }); 574 } catch (IOException x) { 575 result = Result.IO_FAILURE; 576 throw x; 577 } 578 } 579 580 /** 581 * Replace this reference with a symbolic reference to another reference. 582 * <p> 583 * This exact reference (not its traversed leaf) is replaced with a symbolic 584 * reference to the requested name. 585 * 586 * @param target 587 * name of the new target for this reference. The new target name 588 * must be absolute, so it must begin with {@code refs/}. 589 * @return {@link Result#NEW} or {@link Result#FORCED} on success. 590 * @throws IOException 591 */ 592 public Result link(String target) throws IOException { 593 if (!target.startsWith(Constants.R_REFS)) 594 throw new IllegalArgumentException(MessageFormat.format(JGitText.get().illegalArgumentNotA, Constants.R_REFS)); 595 if (checkConflicting && getRefDatabase().isNameConflicting(getName())) 596 return Result.LOCK_FAILURE; 597 try { 598 if (!tryLock(false)) 599 return Result.LOCK_FAILURE; 600 601 final Ref old = getRefDatabase().getRef(getName()); 602 if (old != null && old.isSymbolic()) { 603 final Ref dst = old.getTarget(); 604 if (target.equals(dst.getName())) 605 return result = Result.NO_CHANGE; 606 } 607 608 if (old != null && old.getObjectId() != null) 609 setOldObjectId(old.getObjectId()); 610 611 final Ref dst = getRefDatabase().getRef(target); 612 if (dst != null && dst.getObjectId() != null) 613 setNewObjectId(dst.getObjectId()); 614 615 return result = doLink(target); 616 } catch (IOException x) { 617 result = Result.IO_FAILURE; 618 throw x; 619 } finally { 620 unlock(); 621 } 622 } 623 624 private Result updateImpl(final RevWalk walk, final Store store) 625 throws IOException { 626 RevObject newObj; 627 RevObject oldObj; 628 629 // don't make expensive conflict check if this is an existing Ref 630 if (oldValue == null && checkConflicting && getRefDatabase().isNameConflicting(getName())) 631 return Result.LOCK_FAILURE; 632 try { 633 // If we're detaching a symbolic reference, we should update the reference 634 // itself. Otherwise, we will update the leaf reference, which should be 635 // an ObjectIdRef. 636 if (!tryLock(!detachingSymbolicRef)) 637 return Result.LOCK_FAILURE; 638 if (expValue != null) { 639 final ObjectId o; 640 o = oldValue != null ? oldValue : ObjectId.zeroId(); 641 if (!AnyObjectId.equals(expValue, o)) 642 return Result.LOCK_FAILURE; 643 } 644 if (oldValue == null) 645 return store.execute(Result.NEW); 646 647 newObj = safeParse(walk, newValue); 648 oldObj = safeParse(walk, oldValue); 649 if (newObj == oldObj && !detachingSymbolicRef) 650 return store.execute(Result.NO_CHANGE); 651 652 if (isForceUpdate()) 653 return store.execute(Result.FORCED); 654 655 if (newObj instanceof RevCommit && oldObj instanceof RevCommit) { 656 if (walk.isMergedInto((RevCommit) oldObj, (RevCommit) newObj)) 657 return store.execute(Result.FAST_FORWARD); 658 } 659 660 return Result.REJECTED; 661 } finally { 662 unlock(); 663 } 664 } 665 666 /** 667 * Enable/disable the check for conflicting ref names. By default conflicts 668 * are checked explicitly. 669 * 670 * @param check 671 * @since 3.0 672 */ 673 public void setCheckConflicting(boolean check) { 674 checkConflicting = check; 675 } 676 677 private static RevObject safeParse(final RevWalk rw, final AnyObjectId id) 678 throws IOException { 679 try { 680 return id != null ? rw.parseAny(id) : null; 681 } catch (MissingObjectException e) { 682 // We can expect some objects to be missing, like if we are 683 // trying to force a deletion of a branch and the object it 684 // points to has been pruned from the database due to freak 685 // corruption accidents (it happens with 'git new-work-dir'). 686 // 687 return null; 688 } 689 } 690 691 /** 692 * Handle the abstraction of storing a ref update. This is because both 693 * updating and deleting of a ref have merge testing in common. 694 */ 695 private abstract class Store { 696 abstract Result execute(Result status) throws IOException; 697 } 698 }