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 /** 62 * Status of an update request. 63 * <p> 64 * New values may be added to this enum in the future. Callers may assume that 65 * unknown values are failures, and may generally treat them the same as 66 * {@link #REJECTED_OTHER_REASON}. 67 */ 68 public static enum Result { 69 /** The ref update/delete has not been attempted by the caller. */ 70 NOT_ATTEMPTED, 71 72 /** 73 * The ref could not be locked for update/delete. 74 * <p> 75 * This is generally a transient failure and is usually caused by 76 * another process trying to access the ref at the same time as this 77 * process was trying to update it. It is possible a future operation 78 * will be successful. 79 */ 80 LOCK_FAILURE, 81 82 /** 83 * Same value already stored. 84 * <p> 85 * Both the old value and the new value are identical. No change was 86 * necessary for an update. For delete the branch is removed. 87 */ 88 NO_CHANGE, 89 90 /** 91 * The ref was created locally for an update, but ignored for delete. 92 * <p> 93 * The ref did not exist when the update started, but it was created 94 * successfully with the new value. 95 */ 96 NEW, 97 98 /** 99 * The ref had to be forcefully updated/deleted. 100 * <p> 101 * The ref already existed but its old value was not fully merged into 102 * the new value. The configuration permitted a forced update to take 103 * place, so ref now contains the new value. History associated with the 104 * objects not merged may no longer be reachable. 105 */ 106 FORCED, 107 108 /** 109 * The ref was updated/deleted in a fast-forward way. 110 * <p> 111 * The tracking ref already existed and its old value was fully merged 112 * into the new value. No history was made unreachable. 113 */ 114 FAST_FORWARD, 115 116 /** 117 * Not a fast-forward and not stored. 118 * <p> 119 * The tracking ref already existed but its old value was not fully 120 * merged into the new value. The configuration did not allow a forced 121 * update/delete to take place, so ref still contains the old value. No 122 * previous history was lost. 123 * <p> 124 * <em>Note:</em> Despite the general name, this result only refers to the 125 * non-fast-forward case. For more general errors, see {@link 126 * #REJECTED_OTHER_REASON}. 127 */ 128 REJECTED, 129 130 /** 131 * Rejected because trying to delete the current branch. 132 * <p> 133 * Has no meaning for update. 134 */ 135 REJECTED_CURRENT_BRANCH, 136 137 /** 138 * The ref was probably not updated/deleted because of I/O error. 139 * <p> 140 * Unexpected I/O error occurred when writing new ref. Such error may 141 * result in uncertain state, but most probably ref was not updated. 142 * <p> 143 * This kind of error doesn't include {@link #LOCK_FAILURE}, which is a 144 * different case. 145 */ 146 IO_FAILURE, 147 148 /** 149 * The ref was renamed from another name 150 * <p> 151 */ 152 RENAMED, 153 154 /** 155 * One or more objects aren't in the repository. 156 * <p> 157 * This is severe indication of either repository corruption on the 158 * server side, or a bug in the client wherein the client did not supply 159 * all required objects during the pack transfer. 160 * 161 * @since 4.9 162 */ 163 REJECTED_MISSING_OBJECT, 164 165 /** 166 * Rejected for some other reason not covered by another enum value. 167 * 168 * @since 4.9 169 */ 170 REJECTED_OTHER_REASON; 171 } 172 173 /** New value the caller wants this ref to have. */ 174 private ObjectId newValue; 175 176 /** Does this specification ask for forced updated (rewind/reset)? */ 177 private boolean force; 178 179 /** Identity to record action as within the reflog. */ 180 private PersonIdent refLogIdent; 181 182 /** Message the caller wants included in the reflog. */ 183 private String refLogMessage; 184 185 /** Should the Result value be appended to {@link #refLogMessage}. */ 186 private boolean refLogIncludeResult; 187 188 /** 189 * Should reflogs be written even if the configured default for this ref is 190 * not to write it. 191 */ 192 private boolean forceRefLog; 193 194 /** Old value of the ref, obtained after we lock it. */ 195 private ObjectId oldValue; 196 197 /** If non-null, the value {@link #oldValue} must have to continue. */ 198 private ObjectId expValue; 199 200 /** Result of the update operation. */ 201 private Result result = Result.NOT_ATTEMPTED; 202 203 /** Push certificate associated with this update. */ 204 private PushCertificate pushCert; 205 206 private final Ref ref; 207 208 /** 209 * Is this RefUpdate detaching a symbolic ref? 210 * 211 * We need this info since this.ref will normally be peeled of in case of 212 * detaching a symbolic ref (HEAD for example). 213 * 214 * Without this flag we cannot decide whether the ref has to be updated or 215 * not in case when it was a symbolic ref and the newValue == oldValue. 216 */ 217 private boolean detachingSymbolicRef; 218 219 private boolean checkConflicting = true; 220 221 /** 222 * Construct a new update operation for the reference. 223 * <p> 224 * {@code ref.getObjectId()} will be used to seed {@link #getOldObjectId()}, 225 * which callers can use as part of their own update logic. 226 * 227 * @param ref 228 * the reference that will be updated by this operation. 229 */ 230 protected RefUpdate(final Ref ref) { 231 this.ref = ref; 232 oldValue = ref.getObjectId(); 233 refLogMessage = ""; //$NON-NLS-1$ 234 } 235 236 /** @return the reference database this update modifies. */ 237 protected abstract RefDatabase getRefDatabase(); 238 239 /** @return the repository storing the database's objects. */ 240 protected abstract Repository getRepository(); 241 242 /** 243 * Try to acquire the lock on the reference. 244 * <p> 245 * If the locking was successful the implementor must set the current 246 * identity value by calling {@link #setOldObjectId(ObjectId)}. 247 * 248 * @param deref 249 * true if the lock should be taken against the leaf level 250 * reference; false if it should be taken exactly against the 251 * current reference. 252 * @return true if the lock was acquired and the reference is likely 253 * protected from concurrent modification; false if it failed. 254 * @throws IOException 255 * the lock couldn't be taken due to an unexpected storage 256 * failure, and not because of a concurrent update. 257 */ 258 protected abstract boolean tryLock(boolean deref) throws IOException; 259 260 /** Releases the lock taken by {@link #tryLock} if it succeeded. */ 261 protected abstract void unlock(); 262 263 /** 264 * @param desiredResult 265 * @return {@code result} 266 * @throws IOException 267 */ 268 protected abstract Result doUpdate(Result desiredResult) throws IOException; 269 270 /** 271 * @param desiredResult 272 * @return {@code result} 273 * @throws IOException 274 */ 275 protected abstract Result doDelete(Result desiredResult) throws IOException; 276 277 /** 278 * @param target 279 * @return {@link Result#NEW} on success. 280 * @throws IOException 281 */ 282 protected abstract Result doLink(String target) throws IOException; 283 284 /** 285 * Get the name of the ref this update will operate on. 286 * 287 * @return name of underlying ref. 288 */ 289 public String getName() { 290 return getRef().getName(); 291 } 292 293 /** @return the reference this update will create or modify. */ 294 public Ref getRef() { 295 return ref; 296 } 297 298 /** 299 * Get the new value the ref will be (or was) updated to. 300 * 301 * @return new value. Null if the caller has not configured it. 302 */ 303 public ObjectId getNewObjectId() { 304 return newValue; 305 } 306 307 /** 308 * Tells this RefUpdate that it is actually detaching a symbolic ref. 309 */ 310 public void setDetachingSymbolicRef() { 311 detachingSymbolicRef = true; 312 } 313 314 /** 315 * Return whether this update is actually detaching a symbolic ref. 316 * 317 * @return true if detaching a symref. 318 * @since 4.9 319 */ 320 public boolean isDetachingSymbolicRef() { 321 return detachingSymbolicRef; 322 } 323 324 /** 325 * Set the new value the ref will update to. 326 * 327 * @param id 328 * the new value. 329 */ 330 public void setNewObjectId(final AnyObjectId id) { 331 newValue = id.copy(); 332 } 333 334 /** 335 * @return the expected value of the ref after the lock is taken, but before 336 * update occurs. Null to avoid the compare and swap test. Use 337 * {@link ObjectId#zeroId()} to indicate expectation of a 338 * non-existant ref. 339 */ 340 public ObjectId getExpectedOldObjectId() { 341 return expValue; 342 } 343 344 /** 345 * @param id 346 * the expected value of the ref after the lock is taken, but 347 * before update occurs. Null to avoid the compare and swap test. 348 * Use {@link ObjectId#zeroId()} to indicate expectation of a 349 * non-existant ref. 350 */ 351 public void setExpectedOldObjectId(final AnyObjectId id) { 352 expValue = id != null ? id.toObjectId() : null; 353 } 354 355 /** 356 * Check if this update wants to forcefully change the ref. 357 * 358 * @return true if this update should ignore merge tests. 359 */ 360 public boolean isForceUpdate() { 361 return force; 362 } 363 364 /** 365 * Set if this update wants to forcefully change the ref. 366 * 367 * @param b 368 * true if this update should ignore merge tests. 369 */ 370 public void setForceUpdate(final boolean b) { 371 force = b; 372 } 373 374 /** @return identity of the user making the change in the reflog. */ 375 public PersonIdent getRefLogIdent() { 376 return refLogIdent; 377 } 378 379 /** 380 * Set the identity of the user appearing in the reflog. 381 * <p> 382 * The timestamp portion of the identity is ignored. A new identity with the 383 * current timestamp will be created automatically when the update occurs 384 * and the log record is written. 385 * 386 * @param pi 387 * identity of the user. If null the identity will be 388 * automatically determined based on the repository 389 * configuration. 390 */ 391 public void setRefLogIdent(final PersonIdent pi) { 392 refLogIdent = pi; 393 } 394 395 /** 396 * Get the message to include in the reflog. 397 * 398 * @return message the caller wants to include in the reflog; null if the 399 * update should not be logged. 400 */ 401 public String getRefLogMessage() { 402 return refLogMessage; 403 } 404 405 /** @return {@code true} if the ref log message should show the result. */ 406 protected boolean isRefLogIncludingResult() { 407 return refLogIncludeResult; 408 } 409 410 /** 411 * Set the message to include in the reflog. 412 * <p> 413 * Repository implementations may limit which reflogs are written by default, 414 * based on the project configuration. If a repo is not configured to write 415 * logs for this ref by default, setting the message alone may have no effect. 416 * To indicate that the repo should write logs for this update in spite of 417 * configured defaults, use {@link #setForceRefLog(boolean)}. 418 * 419 * @param msg 420 * the message to describe this change. It may be null if 421 * appendStatus is null in order not to append to the reflog 422 * @param appendStatus 423 * true if the status of the ref change (fast-forward or 424 * forced-update) should be appended to the user supplied 425 * message. 426 */ 427 public void setRefLogMessage(final String msg, final boolean appendStatus) { 428 if (msg == null && !appendStatus) 429 disableRefLog(); 430 else if (msg == null && appendStatus) { 431 refLogMessage = ""; //$NON-NLS-1$ 432 refLogIncludeResult = true; 433 } else { 434 refLogMessage = msg; 435 refLogIncludeResult = appendStatus; 436 } 437 } 438 439 /** Don't record this update in the ref's associated reflog. */ 440 public void disableRefLog() { 441 refLogMessage = null; 442 refLogIncludeResult = false; 443 } 444 445 /** 446 * Force writing a reflog for the updated ref. 447 * 448 * @param force whether to force. 449 * @since 4.9 450 */ 451 public void setForceRefLog(boolean force) { 452 forceRefLog = force; 453 } 454 455 /** 456 * Check whether the reflog should be written regardless of repo defaults. 457 * 458 * @return whether force writing is enabled. 459 * @since 4.9 460 */ 461 protected boolean isForceRefLog() { 462 return forceRefLog; 463 } 464 465 /** 466 * The old value of the ref, prior to the update being attempted. 467 * <p> 468 * This value may differ before and after the update method. Initially it is 469 * populated with the value of the ref before the lock is taken, but the old 470 * value may change if someone else modified the ref between the time we 471 * last read it and when the ref was locked for update. 472 * 473 * @return the value of the ref prior to the update being attempted; null if 474 * the updated has not been attempted yet. 475 */ 476 public ObjectId getOldObjectId() { 477 return oldValue; 478 } 479 480 /** 481 * Set the old value of the ref. 482 * 483 * @param old 484 * the old value. 485 */ 486 protected void setOldObjectId(ObjectId old) { 487 oldValue = old; 488 } 489 490 /** 491 * Set a push certificate associated with this update. 492 * <p> 493 * This usually includes a command to update this ref, but is not required to. 494 * 495 * @param cert 496 * push certificate, may be null. 497 * @since 4.1 498 */ 499 public void setPushCertificate(PushCertificate cert) { 500 pushCert = cert; 501 } 502 503 /** 504 * Set the push certificate associated with this update. 505 * <p> 506 * This usually includes a command to update this ref, but is not required to. 507 * 508 * @return push certificate, may be null. 509 * @since 4.1 510 */ 511 protected PushCertificate getPushCertificate() { 512 return pushCert; 513 } 514 515 /** 516 * Get the status of this update. 517 * <p> 518 * The same value that was previously returned from an update method. 519 * 520 * @return the status of the update. 521 */ 522 public Result getResult() { 523 return result; 524 } 525 526 private void requireCanDoUpdate() { 527 if (newValue == null) 528 throw new IllegalStateException(JGitText.get().aNewObjectIdIsRequired); 529 } 530 531 /** 532 * Force the ref to take the new value. 533 * <p> 534 * This is just a convenient helper for setting the force flag, and as such 535 * the merge test is performed. 536 * 537 * @return the result status of the update. 538 * @throws IOException 539 * an unexpected IO error occurred while writing changes. 540 */ 541 public Result forceUpdate() throws IOException { 542 force = true; 543 return update(); 544 } 545 546 /** 547 * Gracefully update the ref to the new value. 548 * <p> 549 * Merge test will be performed according to {@link #isForceUpdate()}. 550 * <p> 551 * This is the same as: 552 * 553 * <pre> 554 * return update(new RevWalk(getRepository())); 555 * </pre> 556 * 557 * @return the result status of the update. 558 * @throws IOException 559 * an unexpected IO error occurred while writing changes. 560 */ 561 public Result update() throws IOException { 562 try (RevWalk rw = new RevWalk(getRepository())) { 563 return update(rw); 564 } 565 } 566 567 /** 568 * Gracefully update the ref to the new value. 569 * <p> 570 * Merge test will be performed according to {@link #isForceUpdate()}. 571 * 572 * @param walk 573 * a RevWalk instance this update command can borrow to perform 574 * the merge test. The walk will be reset to perform the test. 575 * @return the result status of the update. 576 * @throws IOException 577 * an unexpected IO error occurred while writing changes. 578 */ 579 public Result update(final RevWalk walk) throws IOException { 580 requireCanDoUpdate(); 581 try { 582 return result = updateImpl(walk, new Store() { 583 @Override 584 Result execute(Result status) throws IOException { 585 if (status == Result.NO_CHANGE) 586 return status; 587 return doUpdate(status); 588 } 589 }); 590 } catch (IOException x) { 591 result = Result.IO_FAILURE; 592 throw x; 593 } 594 } 595 596 /** 597 * Delete the ref. 598 * <p> 599 * This is the same as: 600 * 601 * <pre> 602 * return delete(new RevWalk(getRepository())); 603 * </pre> 604 * 605 * @return the result status of the delete. 606 * @throws IOException 607 */ 608 public Result delete() throws IOException { 609 try (RevWalk rw = new RevWalk(getRepository())) { 610 return delete(rw); 611 } 612 } 613 614 /** 615 * Delete the ref. 616 * 617 * @param walk 618 * a RevWalk instance this delete command can borrow to perform 619 * the merge test. The walk will be reset to perform the test. 620 * @return the result status of the delete. 621 * @throws IOException 622 */ 623 public Result delete(final RevWalk walk) throws IOException { 624 final String myName = detachingSymbolicRef 625 ? getRef().getName() 626 : getRef().getLeaf().getName(); 627 if (myName.startsWith(Constants.R_HEADS) && !getRepository().isBare()) { 628 // Don't allow the currently checked out branch to be deleted. 629 Ref head = getRefDatabase().getRef(Constants.HEAD); 630 while (head != null && head.isSymbolic()) { 631 head = head.getTarget(); 632 if (myName.equals(head.getName())) 633 return result = Result.REJECTED_CURRENT_BRANCH; 634 } 635 } 636 637 try { 638 return result = updateImpl(walk, new Store() { 639 @Override 640 Result execute(Result status) throws IOException { 641 return doDelete(status); 642 } 643 }); 644 } catch (IOException x) { 645 result = Result.IO_FAILURE; 646 throw x; 647 } 648 } 649 650 /** 651 * Replace this reference with a symbolic reference to another reference. 652 * <p> 653 * This exact reference (not its traversed leaf) is replaced with a symbolic 654 * reference to the requested name. 655 * 656 * @param target 657 * name of the new target for this reference. The new target name 658 * must be absolute, so it must begin with {@code refs/}. 659 * @return {@link Result#NEW} or {@link Result#FORCED} on success. 660 * @throws IOException 661 */ 662 public Result link(String target) throws IOException { 663 if (!target.startsWith(Constants.R_REFS)) 664 throw new IllegalArgumentException(MessageFormat.format(JGitText.get().illegalArgumentNotA, Constants.R_REFS)); 665 if (checkConflicting && getRefDatabase().isNameConflicting(getName())) 666 return Result.LOCK_FAILURE; 667 try { 668 if (!tryLock(false)) 669 return Result.LOCK_FAILURE; 670 671 final Ref old = getRefDatabase().getRef(getName()); 672 if (old != null && old.isSymbolic()) { 673 final Ref dst = old.getTarget(); 674 if (target.equals(dst.getName())) 675 return result = Result.NO_CHANGE; 676 } 677 678 if (old != null && old.getObjectId() != null) 679 setOldObjectId(old.getObjectId()); 680 681 final Ref dst = getRefDatabase().getRef(target); 682 if (dst != null && dst.getObjectId() != null) 683 setNewObjectId(dst.getObjectId()); 684 685 return result = doLink(target); 686 } catch (IOException x) { 687 result = Result.IO_FAILURE; 688 throw x; 689 } finally { 690 unlock(); 691 } 692 } 693 694 private Result updateImpl(final RevWalk walk, final Store store) 695 throws IOException { 696 RevObject newObj; 697 RevObject oldObj; 698 699 // don't make expensive conflict check if this is an existing Ref 700 if (oldValue == null && checkConflicting 701 && getRefDatabase().isNameConflicting(getName())) { 702 return Result.LOCK_FAILURE; 703 } 704 try { 705 // If we're detaching a symbolic reference, we should update the reference 706 // itself. Otherwise, we will update the leaf reference, which should be 707 // an ObjectIdRef. 708 if (!tryLock(!detachingSymbolicRef)) { 709 return Result.LOCK_FAILURE; 710 } 711 if (expValue != null) { 712 final ObjectId o; 713 o = oldValue != null ? oldValue : ObjectId.zeroId(); 714 if (!AnyObjectId.equals(expValue, o)) { 715 return Result.LOCK_FAILURE; 716 } 717 } 718 try { 719 newObj = safeParseNew(walk, newValue); 720 } catch (MissingObjectException e) { 721 return Result.REJECTED_MISSING_OBJECT; 722 } 723 724 if (oldValue == null) { 725 return store.execute(Result.NEW); 726 } 727 728 oldObj = safeParseOld(walk, oldValue); 729 if (newObj == oldObj && !detachingSymbolicRef) { 730 return store.execute(Result.NO_CHANGE); 731 } 732 733 if (isForceUpdate()) { 734 return store.execute(Result.FORCED); 735 } 736 737 if (newObj instanceof RevCommit && oldObj instanceof RevCommit) { 738 if (walk.isMergedInto((RevCommit) oldObj, (RevCommit) newObj)) { 739 return store.execute(Result.FAST_FORWARD); 740 } 741 } 742 743 return Result.REJECTED; 744 } finally { 745 unlock(); 746 } 747 } 748 749 /** 750 * Enable/disable the check for conflicting ref names. By default conflicts 751 * are checked explicitly. 752 * 753 * @param check 754 * @since 3.0 755 */ 756 public void setCheckConflicting(boolean check) { 757 checkConflicting = check; 758 } 759 760 private static RevObject safeParseNew(RevWalk rw, AnyObjectId newId) 761 throws IOException { 762 if (newId == null || ObjectId.zeroId().equals(newId)) { 763 return null; 764 } 765 return rw.parseAny(newId); 766 } 767 768 private static RevObject safeParseOld(RevWalk rw, AnyObjectId oldId) 769 throws IOException { 770 try { 771 return oldId != null ? rw.parseAny(oldId) : null; 772 } catch (MissingObjectException e) { 773 // We can expect some old objects to be missing, like if we are trying to 774 // force a deletion of a branch and the object it points to has been 775 // pruned from the database due to freak corruption accidents (it happens 776 // with 'git new-work-dir'). 777 return null; 778 } 779 } 780 781 /** 782 * Handle the abstraction of storing a ref update. This is because both 783 * updating and deleting of a ref have merge testing in common. 784 */ 785 private abstract class Store { 786 abstract Result execute(Result status) throws IOException; 787 } 788 }