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(Ref ref) { 231 this.ref = ref; 232 oldValue = ref.getObjectId(); 233 refLogMessage = ""; //$NON-NLS-1$ 234 } 235 236 /** 237 * Get the reference database this update modifies. 238 * 239 * @return the reference database this update modifies. 240 */ 241 protected abstract RefDatabase getRefDatabase(); 242 243 /** 244 * Get the repository storing the database's objects. 245 * 246 * @return the repository storing the database's objects. 247 */ 248 protected abstract Repository getRepository(); 249 250 /** 251 * Try to acquire the lock on the reference. 252 * <p> 253 * If the locking was successful the implementor must set the current 254 * identity value by calling {@link #setOldObjectId(ObjectId)}. 255 * 256 * @param deref 257 * true if the lock should be taken against the leaf level 258 * reference; false if it should be taken exactly against the 259 * current reference. 260 * @return true if the lock was acquired and the reference is likely 261 * protected from concurrent modification; false if it failed. 262 * @throws java.io.IOException 263 * the lock couldn't be taken due to an unexpected storage 264 * failure, and not because of a concurrent update. 265 */ 266 protected abstract boolean tryLock(boolean deref) throws IOException; 267 268 /** 269 * Releases the lock taken by {@link #tryLock} if it succeeded. 270 */ 271 protected abstract void unlock(); 272 273 /** 274 * Do update 275 * 276 * @param desiredResult 277 * a {@link org.eclipse.jgit.lib.RefUpdate.Result} object. 278 * @return {@code result} 279 * @throws java.io.IOException 280 */ 281 protected abstract Result doUpdate(Result desiredResult) throws IOException; 282 283 /** 284 * Do delete 285 * 286 * @param desiredResult 287 * a {@link org.eclipse.jgit.lib.RefUpdate.Result} object. 288 * @return {@code result} 289 * @throws java.io.IOException 290 */ 291 protected abstract Result doDelete(Result desiredResult) throws IOException; 292 293 /** 294 * Do link 295 * 296 * @param target 297 * a {@link java.lang.String} object. 298 * @return {@link org.eclipse.jgit.lib.RefUpdate.Result#NEW} on success. 299 * @throws java.io.IOException 300 */ 301 protected abstract Result doLink(String target) throws IOException; 302 303 /** 304 * Get the name of the ref this update will operate on. 305 * 306 * @return name of underlying ref. 307 */ 308 public String getName() { 309 return getRef().getName(); 310 } 311 312 /** 313 * Get the reference this update will create or modify. 314 * 315 * @return the reference this update will create or modify. 316 */ 317 public Ref getRef() { 318 return ref; 319 } 320 321 /** 322 * Get the new value the ref will be (or was) updated to. 323 * 324 * @return new value. Null if the caller has not configured it. 325 */ 326 public ObjectId getNewObjectId() { 327 return newValue; 328 } 329 330 /** 331 * Tells this RefUpdate that it is actually detaching a symbolic ref. 332 */ 333 public void setDetachingSymbolicRef() { 334 detachingSymbolicRef = true; 335 } 336 337 /** 338 * Return whether this update is actually detaching a symbolic ref. 339 * 340 * @return true if detaching a symref. 341 * @since 4.9 342 */ 343 public boolean isDetachingSymbolicRef() { 344 return detachingSymbolicRef; 345 } 346 347 /** 348 * Set the new value the ref will update to. 349 * 350 * @param id 351 * the new value. 352 */ 353 public void setNewObjectId(AnyObjectId id) { 354 newValue = id.copy(); 355 } 356 357 /** 358 * Get the expected value of the ref after the lock is taken, but before 359 * update occurs. 360 * 361 * @return the expected value of the ref after the lock is taken, but before 362 * update occurs. Null to avoid the compare and swap test. Use 363 * {@link org.eclipse.jgit.lib.ObjectId#zeroId()} to indicate 364 * expectation of a non-existant ref. 365 */ 366 public ObjectId getExpectedOldObjectId() { 367 return expValue; 368 } 369 370 /** 371 * Set the expected value of the ref after the lock is taken, but before 372 * update occurs. 373 * 374 * @param id 375 * the expected value of the ref after the lock is taken, but 376 * before update occurs. Null to avoid the compare and swap test. 377 * Use {@link org.eclipse.jgit.lib.ObjectId#zeroId()} to indicate 378 * expectation of a non-existant ref. 379 */ 380 public void setExpectedOldObjectId(AnyObjectId id) { 381 expValue = id != null ? id.toObjectId() : null; 382 } 383 384 /** 385 * Check if this update wants to forcefully change the ref. 386 * 387 * @return true if this update should ignore merge tests. 388 */ 389 public boolean isForceUpdate() { 390 return force; 391 } 392 393 /** 394 * Set if this update wants to forcefully change the ref. 395 * 396 * @param b 397 * true if this update should ignore merge tests. 398 */ 399 public void setForceUpdate(boolean b) { 400 force = b; 401 } 402 403 /** 404 * Get identity of the user making the change in the reflog. 405 * 406 * @return identity of the user making the change in the reflog. 407 */ 408 public PersonIdent getRefLogIdent() { 409 return refLogIdent; 410 } 411 412 /** 413 * Set the identity of the user appearing in the reflog. 414 * <p> 415 * The timestamp portion of the identity is ignored. A new identity with the 416 * current timestamp will be created automatically when the update occurs 417 * and the log record is written. 418 * 419 * @param pi 420 * identity of the user. If null the identity will be 421 * automatically determined based on the repository 422 * configuration. 423 */ 424 public void setRefLogIdent(PersonIdent pi) { 425 refLogIdent = pi; 426 } 427 428 /** 429 * Get the message to include in the reflog. 430 * 431 * @return message the caller wants to include in the reflog; null if the 432 * update should not be logged. 433 */ 434 public String getRefLogMessage() { 435 return refLogMessage; 436 } 437 438 /** 439 * Whether the ref log message should show the result. 440 * 441 * @return {@code true} if the ref log message should show the result. 442 */ 443 protected boolean isRefLogIncludingResult() { 444 return refLogIncludeResult; 445 } 446 447 /** 448 * Set the message to include in the reflog. 449 * <p> 450 * Repository implementations may limit which reflogs are written by default, 451 * based on the project configuration. If a repo is not configured to write 452 * logs for this ref by default, setting the message alone may have no effect. 453 * To indicate that the repo should write logs for this update in spite of 454 * configured defaults, use {@link #setForceRefLog(boolean)}. 455 * 456 * @param msg 457 * the message to describe this change. It may be null if 458 * appendStatus is null in order not to append to the reflog 459 * @param appendStatus 460 * true if the status of the ref change (fast-forward or 461 * forced-update) should be appended to the user supplied 462 * message. 463 */ 464 public void setRefLogMessage(String msg, boolean appendStatus) { 465 if (msg == null && !appendStatus) 466 disableRefLog(); 467 else if (msg == null && appendStatus) { 468 refLogMessage = ""; //$NON-NLS-1$ 469 refLogIncludeResult = true; 470 } else { 471 refLogMessage = msg; 472 refLogIncludeResult = appendStatus; 473 } 474 } 475 476 /** 477 * Don't record this update in the ref's associated reflog. 478 */ 479 public void disableRefLog() { 480 refLogMessage = null; 481 refLogIncludeResult = false; 482 } 483 484 /** 485 * Force writing a reflog for the updated ref. 486 * 487 * @param force whether to force. 488 * @since 4.9 489 */ 490 public void setForceRefLog(boolean force) { 491 forceRefLog = force; 492 } 493 494 /** 495 * Check whether the reflog should be written regardless of repo defaults. 496 * 497 * @return whether force writing is enabled. 498 * @since 4.9 499 */ 500 protected boolean isForceRefLog() { 501 return forceRefLog; 502 } 503 504 /** 505 * The old value of the ref, prior to the update being attempted. 506 * <p> 507 * This value may differ before and after the update method. Initially it is 508 * populated with the value of the ref before the lock is taken, but the old 509 * value may change if someone else modified the ref between the time we 510 * last read it and when the ref was locked for update. 511 * 512 * @return the value of the ref prior to the update being attempted; null if 513 * the updated has not been attempted yet. 514 */ 515 public ObjectId getOldObjectId() { 516 return oldValue; 517 } 518 519 /** 520 * Set the old value of the ref. 521 * 522 * @param old 523 * the old value. 524 */ 525 protected void setOldObjectId(ObjectId old) { 526 oldValue = old; 527 } 528 529 /** 530 * Set a push certificate associated with this update. 531 * <p> 532 * This usually includes a command to update this ref, but is not required to. 533 * 534 * @param cert 535 * push certificate, may be null. 536 * @since 4.1 537 */ 538 public void setPushCertificate(PushCertificate cert) { 539 pushCert = cert; 540 } 541 542 /** 543 * Set the push certificate associated with this update. 544 * <p> 545 * This usually includes a command to update this ref, but is not required to. 546 * 547 * @return push certificate, may be null. 548 * @since 4.1 549 */ 550 protected PushCertificate getPushCertificate() { 551 return pushCert; 552 } 553 554 /** 555 * Get the status of this update. 556 * <p> 557 * The same value that was previously returned from an update method. 558 * 559 * @return the status of the update. 560 */ 561 public Result getResult() { 562 return result; 563 } 564 565 private void requireCanDoUpdate() { 566 if (newValue == null) 567 throw new IllegalStateException(JGitText.get().aNewObjectIdIsRequired); 568 } 569 570 /** 571 * Force the ref to take the new value. 572 * <p> 573 * This is just a convenient helper for setting the force flag, and as such 574 * the merge test is performed. 575 * 576 * @return the result status of the update. 577 * @throws java.io.IOException 578 * an unexpected IO error occurred while writing changes. 579 */ 580 public Result forceUpdate() throws IOException { 581 force = true; 582 return update(); 583 } 584 585 /** 586 * Gracefully update the ref to the new value. 587 * <p> 588 * Merge test will be performed according to {@link #isForceUpdate()}. 589 * <p> 590 * This is the same as: 591 * 592 * <pre> 593 * return update(new RevWalk(getRepository())); 594 * </pre> 595 * 596 * @return the result status of the update. 597 * @throws java.io.IOException 598 * an unexpected IO error occurred while writing changes. 599 */ 600 public Result update() throws IOException { 601 try (RevWalk rw = new RevWalk(getRepository())) { 602 return update(rw); 603 } 604 } 605 606 /** 607 * Gracefully update the ref to the new value. 608 * <p> 609 * Merge test will be performed according to {@link #isForceUpdate()}. 610 * 611 * @param walk 612 * a RevWalk instance this update command can borrow to perform 613 * the merge test. The walk will be reset to perform the test. 614 * @return the result status of the update. 615 * @throws java.io.IOException 616 * an unexpected IO error occurred while writing changes. 617 */ 618 public Result update(RevWalk walk) throws IOException { 619 requireCanDoUpdate(); 620 try { 621 return result = updateImpl(walk, new Store() { 622 @Override 623 Result execute(Result status) throws IOException { 624 if (status == Result.NO_CHANGE) 625 return status; 626 return doUpdate(status); 627 } 628 }); 629 } catch (IOException x) { 630 result = Result.IO_FAILURE; 631 throw x; 632 } 633 } 634 635 /** 636 * Delete the ref. 637 * <p> 638 * This is the same as: 639 * 640 * <pre> 641 * return delete(new RevWalk(getRepository())); 642 * </pre> 643 * 644 * @return the result status of the delete. 645 * @throws java.io.IOException 646 */ 647 public Result delete() throws IOException { 648 try (RevWalk rw = new RevWalk(getRepository())) { 649 return delete(rw); 650 } 651 } 652 653 /** 654 * Delete the ref. 655 * 656 * @param walk 657 * a RevWalk instance this delete command can borrow to perform 658 * the merge test. The walk will be reset to perform the test. 659 * @return the result status of the delete. 660 * @throws java.io.IOException 661 */ 662 public Result delete(RevWalk walk) throws IOException { 663 final String myName = detachingSymbolicRef 664 ? getRef().getName() 665 : getRef().getLeaf().getName(); 666 if (myName.startsWith(Constants.R_HEADS) && !getRepository().isBare()) { 667 // Don't allow the currently checked out branch to be deleted. 668 Ref head = getRefDatabase().getRef(Constants.HEAD); 669 while (head != null && head.isSymbolic()) { 670 head = head.getTarget(); 671 if (myName.equals(head.getName())) 672 return result = Result.REJECTED_CURRENT_BRANCH; 673 } 674 } 675 676 try { 677 return result = updateImpl(walk, new Store() { 678 @Override 679 Result execute(Result status) throws IOException { 680 return doDelete(status); 681 } 682 }); 683 } catch (IOException x) { 684 result = Result.IO_FAILURE; 685 throw x; 686 } 687 } 688 689 /** 690 * Replace this reference with a symbolic reference to another reference. 691 * <p> 692 * This exact reference (not its traversed leaf) is replaced with a symbolic 693 * reference to the requested name. 694 * 695 * @param target 696 * name of the new target for this reference. The new target name 697 * must be absolute, so it must begin with {@code refs/}. 698 * @return {@link org.eclipse.jgit.lib.RefUpdate.Result#NEW} or 699 * {@link org.eclipse.jgit.lib.RefUpdate.Result#FORCED} on success. 700 * @throws java.io.IOException 701 */ 702 public Result link(String target) throws IOException { 703 if (!target.startsWith(Constants.R_REFS)) 704 throw new IllegalArgumentException(MessageFormat.format(JGitText.get().illegalArgumentNotA, Constants.R_REFS)); 705 if (checkConflicting && getRefDatabase().isNameConflicting(getName())) 706 return Result.LOCK_FAILURE; 707 try { 708 if (!tryLock(false)) 709 return Result.LOCK_FAILURE; 710 711 final Ref old = getRefDatabase().getRef(getName()); 712 if (old != null && old.isSymbolic()) { 713 final Ref dst = old.getTarget(); 714 if (target.equals(dst.getName())) 715 return result = Result.NO_CHANGE; 716 } 717 718 if (old != null && old.getObjectId() != null) 719 setOldObjectId(old.getObjectId()); 720 721 final Ref dst = getRefDatabase().getRef(target); 722 if (dst != null && dst.getObjectId() != null) 723 setNewObjectId(dst.getObjectId()); 724 725 return result = doLink(target); 726 } catch (IOException x) { 727 result = Result.IO_FAILURE; 728 throw x; 729 } finally { 730 unlock(); 731 } 732 } 733 734 private Result updateImpl(RevWalk walk, Store store) 735 throws IOException { 736 RevObject newObj; 737 RevObject oldObj; 738 739 // don't make expensive conflict check if this is an existing Ref 740 if (oldValue == null && checkConflicting 741 && getRefDatabase().isNameConflicting(getName())) { 742 return Result.LOCK_FAILURE; 743 } 744 try { 745 // If we're detaching a symbolic reference, we should update the reference 746 // itself. Otherwise, we will update the leaf reference, which should be 747 // an ObjectIdRef. 748 if (!tryLock(!detachingSymbolicRef)) { 749 return Result.LOCK_FAILURE; 750 } 751 if (expValue != null) { 752 final ObjectId o; 753 o = oldValue != null ? oldValue : ObjectId.zeroId(); 754 if (!AnyObjectId.equals(expValue, o)) { 755 return Result.LOCK_FAILURE; 756 } 757 } 758 try { 759 newObj = safeParseNew(walk, newValue); 760 } catch (MissingObjectException e) { 761 return Result.REJECTED_MISSING_OBJECT; 762 } 763 764 if (oldValue == null) { 765 return store.execute(Result.NEW); 766 } 767 768 oldObj = safeParseOld(walk, oldValue); 769 if (newObj == oldObj && !detachingSymbolicRef) { 770 return store.execute(Result.NO_CHANGE); 771 } 772 773 if (isForceUpdate()) { 774 return store.execute(Result.FORCED); 775 } 776 777 if (newObj instanceof RevCommit && oldObj instanceof RevCommit) { 778 if (walk.isMergedInto((RevCommit) oldObj, (RevCommit) newObj)) { 779 return store.execute(Result.FAST_FORWARD); 780 } 781 } 782 783 return Result.REJECTED; 784 } finally { 785 unlock(); 786 } 787 } 788 789 /** 790 * Enable/disable the check for conflicting ref names. By default conflicts 791 * are checked explicitly. 792 * 793 * @param check 794 * whether to enable the check for conflicting ref names. 795 * @since 3.0 796 */ 797 public void setCheckConflicting(boolean check) { 798 checkConflicting = check; 799 } 800 801 private static RevObject safeParseNew(RevWalk rw, AnyObjectId newId) 802 throws IOException { 803 if (newId == null || ObjectId.zeroId().equals(newId)) { 804 return null; 805 } 806 return rw.parseAny(newId); 807 } 808 809 private static RevObject safeParseOld(RevWalk rw, AnyObjectId oldId) 810 throws IOException { 811 try { 812 return oldId != null ? rw.parseAny(oldId) : null; 813 } catch (MissingObjectException e) { 814 // We can expect some old objects to be missing, like if we are trying to 815 // force a deletion of a branch and the object it points to has been 816 // pruned from the database due to freak corruption accidents (it happens 817 // with 'git new-work-dir'). 818 return null; 819 } 820 } 821 822 /** 823 * Handle the abstraction of storing a ref update. This is because both 824 * updating and deleting of a ref have merge testing in common. 825 */ 826 private static abstract class Store { 827 abstract Result execute(Result status) throws IOException; 828 } 829 }