RefUpdate.java

  1. /*
  2.  * Copyright (C) 2008-2010, Google Inc.
  3.  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
  4.  *
  5.  * This program and the accompanying materials are made available under the
  6.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  7.  * https://www.eclipse.org/org/documents/edl-v10.php.
  8.  *
  9.  * SPDX-License-Identifier: BSD-3-Clause
  10.  */

  11. package org.eclipse.jgit.lib;

  12. import java.io.IOException;
  13. import java.text.MessageFormat;

  14. import org.eclipse.jgit.errors.MissingObjectException;
  15. import org.eclipse.jgit.internal.JGitText;
  16. import org.eclipse.jgit.revwalk.RevCommit;
  17. import org.eclipse.jgit.revwalk.RevObject;
  18. import org.eclipse.jgit.revwalk.RevWalk;
  19. import org.eclipse.jgit.transport.PushCertificate;
  20. import org.eclipse.jgit.util.References;

  21. /**
  22.  * Creates, updates or deletes any reference.
  23.  */
  24. public abstract class RefUpdate {
  25.     /**
  26.      * Status of an update request.
  27.      * <p>
  28.      * New values may be added to this enum in the future. Callers may assume that
  29.      * unknown values are failures, and may generally treat them the same as
  30.      * {@link #REJECTED_OTHER_REASON}.
  31.      */
  32.     public enum Result {
  33.         /** The ref update/delete has not been attempted by the caller. */
  34.         NOT_ATTEMPTED,

  35.         /**
  36.          * The ref could not be locked for update/delete.
  37.          * <p>
  38.          * This is generally a transient failure and is usually caused by
  39.          * another process trying to access the ref at the same time as this
  40.          * process was trying to update it. It is possible a future operation
  41.          * will be successful.
  42.          */
  43.         LOCK_FAILURE,

  44.         /**
  45.          * Same value already stored.
  46.          * <p>
  47.          * Both the old value and the new value are identical. No change was
  48.          * necessary for an update. For delete the branch is removed.
  49.          */
  50.         NO_CHANGE,

  51.         /**
  52.          * The ref was created locally for an update, but ignored for delete.
  53.          * <p>
  54.          * The ref did not exist when the update started, but it was created
  55.          * successfully with the new value.
  56.          */
  57.         NEW,

  58.         /**
  59.          * The ref had to be forcefully updated/deleted.
  60.          * <p>
  61.          * The ref already existed but its old value was not fully merged into
  62.          * the new value. The configuration permitted a forced update to take
  63.          * place, so ref now contains the new value. History associated with the
  64.          * objects not merged may no longer be reachable.
  65.          */
  66.         FORCED,

  67.         /**
  68.          * The ref was updated/deleted in a fast-forward way.
  69.          * <p>
  70.          * The tracking ref already existed and its old value was fully merged
  71.          * into the new value. No history was made unreachable.
  72.          */
  73.         FAST_FORWARD,

  74.         /**
  75.          * Not a fast-forward and not stored.
  76.          * <p>
  77.          * The tracking ref already existed but its old value was not fully
  78.          * merged into the new value. The configuration did not allow a forced
  79.          * update/delete to take place, so ref still contains the old value. No
  80.          * previous history was lost.
  81.          * <p>
  82.          * <em>Note:</em> Despite the general name, this result only refers to the
  83.          * non-fast-forward case. For more general errors, see {@link
  84.          * #REJECTED_OTHER_REASON}.
  85.          */
  86.         REJECTED,

  87.         /**
  88.          * Rejected because trying to delete the current branch.
  89.          * <p>
  90.          * Has no meaning for update.
  91.          */
  92.         REJECTED_CURRENT_BRANCH,

  93.         /**
  94.          * The ref was probably not updated/deleted because of I/O error.
  95.          * <p>
  96.          * Unexpected I/O error occurred when writing new ref. Such error may
  97.          * result in uncertain state, but most probably ref was not updated.
  98.          * <p>
  99.          * This kind of error doesn't include {@link #LOCK_FAILURE}, which is a
  100.          * different case.
  101.          */
  102.         IO_FAILURE,

  103.         /**
  104.          * The ref was renamed from another name
  105.          * <p>
  106.          */
  107.         RENAMED,

  108.         /**
  109.          * One or more objects aren't in the repository.
  110.          * <p>
  111.          * This is severe indication of either repository corruption on the
  112.          * server side, or a bug in the client wherein the client did not supply
  113.          * all required objects during the pack transfer.
  114.          *
  115.          * @since 4.9
  116.          */
  117.         REJECTED_MISSING_OBJECT,

  118.         /**
  119.          * Rejected for some other reason not covered by another enum value.
  120.          *
  121.          * @since 4.9
  122.          */
  123.         REJECTED_OTHER_REASON;
  124.     }

  125.     /** New value the caller wants this ref to have. */
  126.     private ObjectId newValue;

  127.     /** Does this specification ask for forced updated (rewind/reset)? */
  128.     private boolean force;

  129.     /** Identity to record action as within the reflog. */
  130.     private PersonIdent refLogIdent;

  131.     /** Message the caller wants included in the reflog. */
  132.     private String refLogMessage;

  133.     /** Should the Result value be appended to {@link #refLogMessage}. */
  134.     private boolean refLogIncludeResult;

  135.     /**
  136.      * Should reflogs be written even if the configured default for this ref is
  137.      * not to write it.
  138.      */
  139.     private boolean forceRefLog;

  140.     /** Old value of the ref, obtained after we lock it. */
  141.     private ObjectId oldValue;

  142.     /** If non-null, the value {@link #oldValue} must have to continue. */
  143.     private ObjectId expValue;

  144.     /** Result of the update operation. */
  145.     private Result result = Result.NOT_ATTEMPTED;

  146.     /** Push certificate associated with this update. */
  147.     private PushCertificate pushCert;

  148.     private final Ref ref;

  149.     /**
  150.      * Is this RefUpdate detaching a symbolic ref?
  151.      *
  152.      * We need this info since this.ref will normally be peeled of in case of
  153.      * detaching a symbolic ref (HEAD for example).
  154.      *
  155.      * Without this flag we cannot decide whether the ref has to be updated or
  156.      * not in case when it was a symbolic ref and the newValue == oldValue.
  157.      */
  158.     private boolean detachingSymbolicRef;

  159.     private boolean checkConflicting = true;

  160.     /**
  161.      * Construct a new update operation for the reference.
  162.      * <p>
  163.      * {@code ref.getObjectId()} will be used to seed {@link #getOldObjectId()},
  164.      * which callers can use as part of their own update logic.
  165.      *
  166.      * @param ref
  167.      *            the reference that will be updated by this operation.
  168.      */
  169.     protected RefUpdate(Ref ref) {
  170.         this.ref = ref;
  171.         oldValue = ref.getObjectId();
  172.         refLogMessage = ""; //$NON-NLS-1$
  173.     }

  174.     /**
  175.      * Get the reference database this update modifies.
  176.      *
  177.      * @return the reference database this update modifies.
  178.      */
  179.     protected abstract RefDatabase getRefDatabase();

  180.     /**
  181.      * Get the repository storing the database's objects.
  182.      *
  183.      * @return the repository storing the database's objects.
  184.      */
  185.     protected abstract Repository getRepository();

  186.     /**
  187.      * Try to acquire the lock on the reference.
  188.      * <p>
  189.      * If the locking was successful the implementor must set the current
  190.      * identity value by calling {@link #setOldObjectId(ObjectId)}.
  191.      *
  192.      * @param deref
  193.      *            true if the lock should be taken against the leaf level
  194.      *            reference; false if it should be taken exactly against the
  195.      *            current reference.
  196.      * @return true if the lock was acquired and the reference is likely
  197.      *         protected from concurrent modification; false if it failed.
  198.      * @throws java.io.IOException
  199.      *             the lock couldn't be taken due to an unexpected storage
  200.      *             failure, and not because of a concurrent update.
  201.      */
  202.     protected abstract boolean tryLock(boolean deref) throws IOException;

  203.     /**
  204.      * Releases the lock taken by {@link #tryLock} if it succeeded.
  205.      */
  206.     protected abstract void unlock();

  207.     /**
  208.      * Do update
  209.      *
  210.      * @param desiredResult
  211.      *            a {@link org.eclipse.jgit.lib.RefUpdate.Result} object.
  212.      * @return {@code result}
  213.      * @throws java.io.IOException
  214.      */
  215.     protected abstract Result doUpdate(Result desiredResult) throws IOException;

  216.     /**
  217.      * Do delete
  218.      *
  219.      * @param desiredResult
  220.      *            a {@link org.eclipse.jgit.lib.RefUpdate.Result} object.
  221.      * @return {@code result}
  222.      * @throws java.io.IOException
  223.      */
  224.     protected abstract Result doDelete(Result desiredResult) throws IOException;

  225.     /**
  226.      * Do link
  227.      *
  228.      * @param target
  229.      *            a {@link java.lang.String} object.
  230.      * @return {@link org.eclipse.jgit.lib.RefUpdate.Result#NEW} on success.
  231.      * @throws java.io.IOException
  232.      */
  233.     protected abstract Result doLink(String target) throws IOException;

  234.     /**
  235.      * Get the name of the ref this update will operate on.
  236.      *
  237.      * @return name of underlying ref.
  238.      */
  239.     public String getName() {
  240.         return getRef().getName();
  241.     }

  242.     /**
  243.      * Get the reference this update will create or modify.
  244.      *
  245.      * @return the reference this update will create or modify.
  246.      */
  247.     public Ref getRef() {
  248.         return ref;
  249.     }

  250.     /**
  251.      * Get the new value the ref will be (or was) updated to.
  252.      *
  253.      * @return new value. Null if the caller has not configured it.
  254.      */
  255.     public ObjectId getNewObjectId() {
  256.         return newValue;
  257.     }

  258.     /**
  259.      * Tells this RefUpdate that it is actually detaching a symbolic ref.
  260.      */
  261.     public void setDetachingSymbolicRef() {
  262.         detachingSymbolicRef = true;
  263.     }

  264.     /**
  265.      * Return whether this update is actually detaching a symbolic ref.
  266.      *
  267.      * @return true if detaching a symref.
  268.      * @since 4.9
  269.      */
  270.     public boolean isDetachingSymbolicRef() {
  271.         return detachingSymbolicRef;
  272.     }

  273.     /**
  274.      * Set the new value the ref will update to.
  275.      *
  276.      * @param id
  277.      *            the new value.
  278.      */
  279.     public void setNewObjectId(AnyObjectId id) {
  280.         newValue = id.copy();
  281.     }

  282.     /**
  283.      * Get the expected value of the ref after the lock is taken, but before
  284.      * update occurs.
  285.      *
  286.      * @return the expected value of the ref after the lock is taken, but before
  287.      *         update occurs. Null to avoid the compare and swap test. Use
  288.      *         {@link org.eclipse.jgit.lib.ObjectId#zeroId()} to indicate
  289.      *         expectation of a non-existant ref.
  290.      */
  291.     public ObjectId getExpectedOldObjectId() {
  292.         return expValue;
  293.     }

  294.     /**
  295.      * Set the expected value of the ref after the lock is taken, but before
  296.      * update occurs.
  297.      *
  298.      * @param id
  299.      *            the expected value of the ref after the lock is taken, but
  300.      *            before update occurs. Null to avoid the compare and swap test.
  301.      *            Use {@link org.eclipse.jgit.lib.ObjectId#zeroId()} to indicate
  302.      *            expectation of a non-existant ref.
  303.      */
  304.     public void setExpectedOldObjectId(AnyObjectId id) {
  305.         expValue = id != null ? id.toObjectId() : null;
  306.     }

  307.     /**
  308.      * Check if this update wants to forcefully change the ref.
  309.      *
  310.      * @return true if this update should ignore merge tests.
  311.      */
  312.     public boolean isForceUpdate() {
  313.         return force;
  314.     }

  315.     /**
  316.      * Set if this update wants to forcefully change the ref.
  317.      *
  318.      * @param b
  319.      *            true if this update should ignore merge tests.
  320.      */
  321.     public void setForceUpdate(boolean b) {
  322.         force = b;
  323.     }

  324.     /**
  325.      * Get identity of the user making the change in the reflog.
  326.      *
  327.      * @return identity of the user making the change in the reflog.
  328.      */
  329.     public PersonIdent getRefLogIdent() {
  330.         return refLogIdent;
  331.     }

  332.     /**
  333.      * Set the identity of the user appearing in the reflog.
  334.      * <p>
  335.      * The timestamp portion of the identity is ignored. A new identity with the
  336.      * current timestamp will be created automatically when the update occurs
  337.      * and the log record is written.
  338.      *
  339.      * @param pi
  340.      *            identity of the user. If null the identity will be
  341.      *            automatically determined based on the repository
  342.      *            configuration.
  343.      */
  344.     public void setRefLogIdent(PersonIdent pi) {
  345.         refLogIdent = pi;
  346.     }

  347.     /**
  348.      * Get the message to include in the reflog.
  349.      *
  350.      * @return message the caller wants to include in the reflog; null if the
  351.      *         update should not be logged.
  352.      */
  353.     public String getRefLogMessage() {
  354.         return refLogMessage;
  355.     }

  356.     /**
  357.      * Whether the ref log message should show the result.
  358.      *
  359.      * @return {@code true} if the ref log message should show the result.
  360.      */
  361.     protected boolean isRefLogIncludingResult() {
  362.         return refLogIncludeResult;
  363.     }

  364.     /**
  365.      * Set the message to include in the reflog.
  366.      * <p>
  367.      * Repository implementations may limit which reflogs are written by default,
  368.      * based on the project configuration. If a repo is not configured to write
  369.      * logs for this ref by default, setting the message alone may have no effect.
  370.      * To indicate that the repo should write logs for this update in spite of
  371.      * configured defaults, use {@link #setForceRefLog(boolean)}.
  372.      *
  373.      * @param msg
  374.      *            the message to describe this change. It may be null if
  375.      *            appendStatus is null in order not to append to the reflog
  376.      * @param appendStatus
  377.      *            true if the status of the ref change (fast-forward or
  378.      *            forced-update) should be appended to the user supplied
  379.      *            message.
  380.      */
  381.     public void setRefLogMessage(String msg, boolean appendStatus) {
  382.         if (msg == null && !appendStatus)
  383.             disableRefLog();
  384.         else if (msg == null && appendStatus) {
  385.             refLogMessage = ""; //$NON-NLS-1$
  386.             refLogIncludeResult = true;
  387.         } else {
  388.             refLogMessage = msg;
  389.             refLogIncludeResult = appendStatus;
  390.         }
  391.     }

  392.     /**
  393.      * Don't record this update in the ref's associated reflog.
  394.      */
  395.     public void disableRefLog() {
  396.         refLogMessage = null;
  397.         refLogIncludeResult = false;
  398.     }

  399.     /**
  400.      * Force writing a reflog for the updated ref.
  401.      *
  402.      * @param force whether to force.
  403.      * @since 4.9
  404.      */
  405.     public void setForceRefLog(boolean force) {
  406.         forceRefLog = force;
  407.     }

  408.     /**
  409.      * Check whether the reflog should be written regardless of repo defaults.
  410.      *
  411.      * @return whether force writing is enabled.
  412.      * @since 4.9
  413.      */
  414.     protected boolean isForceRefLog() {
  415.         return forceRefLog;
  416.     }

  417.     /**
  418.      * The old value of the ref, prior to the update being attempted.
  419.      * <p>
  420.      * This value may differ before and after the update method. Initially it is
  421.      * populated with the value of the ref before the lock is taken, but the old
  422.      * value may change if someone else modified the ref between the time we
  423.      * last read it and when the ref was locked for update.
  424.      *
  425.      * @return the value of the ref prior to the update being attempted; null if
  426.      *         the updated has not been attempted yet.
  427.      */
  428.     public ObjectId getOldObjectId() {
  429.         return oldValue;
  430.     }

  431.     /**
  432.      * Set the old value of the ref.
  433.      *
  434.      * @param old
  435.      *            the old value.
  436.      */
  437.     protected void setOldObjectId(ObjectId old) {
  438.         oldValue = old;
  439.     }

  440.     /**
  441.      * Set a push certificate associated with this update.
  442.      * <p>
  443.      * This usually includes a command to update this ref, but is not required to.
  444.      *
  445.      * @param cert
  446.      *            push certificate, may be null.
  447.      * @since 4.1
  448.      */
  449.     public void setPushCertificate(PushCertificate cert) {
  450.         pushCert = cert;
  451.     }

  452.     /**
  453.      * Set the push certificate associated with this update.
  454.      * <p>
  455.      * This usually includes a command to update this ref, but is not required to.
  456.      *
  457.      * @return push certificate, may be null.
  458.      * @since 4.1
  459.      */
  460.     protected PushCertificate getPushCertificate() {
  461.         return pushCert;
  462.     }

  463.     /**
  464.      * Get the status of this update.
  465.      * <p>
  466.      * The same value that was previously returned from an update method.
  467.      *
  468.      * @return the status of the update.
  469.      */
  470.     public Result getResult() {
  471.         return result;
  472.     }

  473.     private void requireCanDoUpdate() {
  474.         if (newValue == null)
  475.             throw new IllegalStateException(JGitText.get().aNewObjectIdIsRequired);
  476.     }

  477.     /**
  478.      * Force the ref to take the new value.
  479.      * <p>
  480.      * This is just a convenient helper for setting the force flag, and as such
  481.      * the merge test is performed.
  482.      *
  483.      * @return the result status of the update.
  484.      * @throws java.io.IOException
  485.      *             an unexpected IO error occurred while writing changes.
  486.      */
  487.     public Result forceUpdate() throws IOException {
  488.         force = true;
  489.         return update();
  490.     }

  491.     /**
  492.      * Gracefully update the ref to the new value.
  493.      * <p>
  494.      * Merge test will be performed according to {@link #isForceUpdate()}.
  495.      * <p>
  496.      * This is the same as:
  497.      *
  498.      * <pre>
  499.      * return update(new RevWalk(getRepository()));
  500.      * </pre>
  501.      *
  502.      * @return the result status of the update.
  503.      * @throws java.io.IOException
  504.      *             an unexpected IO error occurred while writing changes.
  505.      */
  506.     public Result update() throws IOException {
  507.         try (RevWalk rw = new RevWalk(getRepository())) {
  508.             rw.setRetainBody(false);
  509.             return update(rw);
  510.         }
  511.     }

  512.     /**
  513.      * Gracefully update the ref to the new value.
  514.      * <p>
  515.      * Merge test will be performed according to {@link #isForceUpdate()}.
  516.      *
  517.      * @param walk
  518.      *            a RevWalk instance this update command can borrow to perform
  519.      *            the merge test. The walk will be reset to perform the test.
  520.      * @return the result status of the update.
  521.      * @throws java.io.IOException
  522.      *             an unexpected IO error occurred while writing changes.
  523.      */
  524.     public Result update(RevWalk walk) throws IOException {
  525.         requireCanDoUpdate();
  526.         try {
  527.             return result = updateImpl(walk, new Store() {
  528.                 @Override
  529.                 Result execute(Result status) throws IOException {
  530.                     if (status == Result.NO_CHANGE)
  531.                         return status;
  532.                     return doUpdate(status);
  533.                 }
  534.             });
  535.         } catch (IOException x) {
  536.             result = Result.IO_FAILURE;
  537.             throw x;
  538.         }
  539.     }

  540.     /**
  541.      * Delete the ref.
  542.      * <p>
  543.      * This is the same as:
  544.      *
  545.      * <pre>
  546.      * return delete(new RevWalk(getRepository()));
  547.      * </pre>
  548.      *
  549.      * @return the result status of the delete.
  550.      * @throws java.io.IOException
  551.      */
  552.     public Result delete() throws IOException {
  553.         try (RevWalk rw = new RevWalk(getRepository())) {
  554.             rw.setRetainBody(false);
  555.             return delete(rw);
  556.         }
  557.     }

  558.     /**
  559.      * Delete the ref.
  560.      *
  561.      * @param walk
  562.      *            a RevWalk instance this delete command can borrow to perform
  563.      *            the merge test. The walk will be reset to perform the test.
  564.      * @return the result status of the delete.
  565.      * @throws java.io.IOException
  566.      */
  567.     public Result delete(RevWalk walk) throws IOException {
  568.         final String myName = detachingSymbolicRef
  569.                 ? getRef().getName()
  570.                 : getRef().getLeaf().getName();
  571.         if (myName.startsWith(Constants.R_HEADS) && !getRepository().isBare()) {
  572.             // Don't allow the currently checked out branch to be deleted.
  573.             Ref head = getRefDatabase().exactRef(Constants.HEAD);
  574.             while (head != null && head.isSymbolic()) {
  575.                 head = head.getTarget();
  576.                 if (myName.equals(head.getName()))
  577.                     return result = Result.REJECTED_CURRENT_BRANCH;
  578.             }
  579.         }

  580.         try {
  581.             return result = updateImpl(walk, new Store() {
  582.                 @Override
  583.                 Result execute(Result status) throws IOException {
  584.                     return doDelete(status);
  585.                 }
  586.             });
  587.         } catch (IOException x) {
  588.             result = Result.IO_FAILURE;
  589.             throw x;
  590.         }
  591.     }

  592.     /**
  593.      * Replace this reference with a symbolic reference to another reference.
  594.      * <p>
  595.      * This exact reference (not its traversed leaf) is replaced with a symbolic
  596.      * reference to the requested name.
  597.      *
  598.      * @param target
  599.      *            name of the new target for this reference. The new target name
  600.      *            must be absolute, so it must begin with {@code refs/}.
  601.      * @return {@link org.eclipse.jgit.lib.RefUpdate.Result#NEW} or
  602.      *         {@link org.eclipse.jgit.lib.RefUpdate.Result#FORCED} on success.
  603.      * @throws java.io.IOException
  604.      */
  605.     public Result link(String target) throws IOException {
  606.         if (!target.startsWith(Constants.R_REFS))
  607.             throw new IllegalArgumentException(MessageFormat.format(JGitText.get().illegalArgumentNotA, Constants.R_REFS));
  608.         if (checkConflicting && getRefDatabase().isNameConflicting(getName()))
  609.             return Result.LOCK_FAILURE;
  610.         try {
  611.             if (!tryLock(false))
  612.                 return Result.LOCK_FAILURE;

  613.             final Ref old = getRefDatabase().exactRef(getName());
  614.             if (old != null && old.isSymbolic()) {
  615.                 final Ref dst = old.getTarget();
  616.                 if (target.equals(dst.getName()))
  617.                     return result = Result.NO_CHANGE;
  618.             }

  619.             if (old != null && old.getObjectId() != null)
  620.                 setOldObjectId(old.getObjectId());

  621.             final Ref dst = getRefDatabase().exactRef(target);
  622.             if (dst != null && dst.getObjectId() != null)
  623.                 setNewObjectId(dst.getObjectId());

  624.             return result = doLink(target);
  625.         } catch (IOException x) {
  626.             result = Result.IO_FAILURE;
  627.             throw x;
  628.         } finally {
  629.             unlock();
  630.         }
  631.     }

  632.     private Result updateImpl(RevWalk walk, Store store)
  633.             throws IOException {
  634.         RevObject newObj;
  635.         RevObject oldObj;

  636.         // don't make expensive conflict check if this is an existing Ref
  637.         if (oldValue == null && checkConflicting
  638.                 && getRefDatabase().isNameConflicting(getName())) {
  639.             return Result.LOCK_FAILURE;
  640.         }
  641.         try {
  642.             // If we're detaching a symbolic reference, we should update the reference
  643.             // itself. Otherwise, we will update the leaf reference, which should be
  644.             // an ObjectIdRef.
  645.             if (!tryLock(!detachingSymbolicRef)) {
  646.                 return Result.LOCK_FAILURE;
  647.             }
  648.             if (expValue != null) {
  649.                 final ObjectId o;
  650.                 o = oldValue != null ? oldValue : ObjectId.zeroId();
  651.                 if (!AnyObjectId.isEqual(expValue, o)) {
  652.                     return Result.LOCK_FAILURE;
  653.                 }
  654.             }
  655.             try {
  656.                 newObj = safeParseNew(walk, newValue);
  657.             } catch (MissingObjectException e) {
  658.                 return Result.REJECTED_MISSING_OBJECT;
  659.             }

  660.             if (oldValue == null) {
  661.                 return store.execute(Result.NEW);
  662.             }

  663.             oldObj = safeParseOld(walk, oldValue);
  664.             if (References.isSameObject(newObj, oldObj)
  665.                     && !detachingSymbolicRef) {
  666.                 return store.execute(Result.NO_CHANGE);
  667.             }

  668.             if (isForceUpdate()) {
  669.                 return store.execute(Result.FORCED);
  670.             }

  671.             if (newObj instanceof RevCommit && oldObj instanceof RevCommit) {
  672.                 if (walk.isMergedInto((RevCommit) oldObj, (RevCommit) newObj)) {
  673.                     return store.execute(Result.FAST_FORWARD);
  674.                 }
  675.             }

  676.             return Result.REJECTED;
  677.         } finally {
  678.             unlock();
  679.         }
  680.     }

  681.     /**
  682.      * Enable/disable the check for conflicting ref names. By default conflicts
  683.      * are checked explicitly.
  684.      *
  685.      * @param check
  686.      *            whether to enable the check for conflicting ref names.
  687.      * @since 3.0
  688.      */
  689.     public void setCheckConflicting(boolean check) {
  690.         checkConflicting = check;
  691.     }

  692.     private static RevObject safeParseNew(RevWalk rw, AnyObjectId newId)
  693.             throws IOException {
  694.         if (newId == null || ObjectId.zeroId().equals(newId)) {
  695.             return null;
  696.         }
  697.         return rw.parseAny(newId);
  698.     }

  699.     private static RevObject safeParseOld(RevWalk rw, AnyObjectId oldId)
  700.             throws IOException {
  701.         try {
  702.             return oldId != null ? rw.parseAny(oldId) : null;
  703.         } catch (MissingObjectException e) {
  704.             // We can expect some old objects to be missing, like if we are trying to
  705.             // force a deletion of a branch and the object it points to has been
  706.             // pruned from the database due to freak corruption accidents (it happens
  707.             // with 'git new-work-dir').
  708.             return null;
  709.         }
  710.     }

  711.     /**
  712.      * Handle the abstraction of storing a ref update. This is because both
  713.      * updating and deleting of a ref have merge testing in common.
  714.      */
  715.     private abstract static class Store {
  716.         abstract Result execute(Result status) throws IOException;
  717.     }
  718. }