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