ReceiveCommand.java

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

  10. package org.eclipse.jgit.transport;

  11. import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
  12. import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;

  13. import java.io.IOException;
  14. import java.text.MessageFormat;
  15. import java.util.ArrayList;
  16. import java.util.Collection;
  17. import java.util.List;

  18. import org.eclipse.jgit.annotations.NonNull;
  19. import org.eclipse.jgit.annotations.Nullable;
  20. import org.eclipse.jgit.internal.JGitText;
  21. import org.eclipse.jgit.lib.AnyObjectId;
  22. import org.eclipse.jgit.lib.ObjectId;
  23. import org.eclipse.jgit.lib.Ref;
  24. import org.eclipse.jgit.lib.RefUpdate;
  25. import org.eclipse.jgit.revwalk.RevCommit;
  26. import org.eclipse.jgit.revwalk.RevObject;
  27. import org.eclipse.jgit.revwalk.RevWalk;

  28. /**
  29.  * A command being processed by
  30.  * {@link org.eclipse.jgit.transport.ReceivePack}.
  31.  * <p>
  32.  * This command instance roughly translates to the server side representation of
  33.  * the {@link org.eclipse.jgit.transport.RemoteRefUpdate} created by the client.
  34.  */
  35. public class ReceiveCommand {
  36.     /** Type of operation requested. */
  37.     public enum Type {
  38.         /** Create a new ref; the ref must not already exist. */
  39.         CREATE,

  40.         /**
  41.          * Update an existing ref with a fast-forward update.
  42.          * <p>
  43.          * During a fast-forward update no changes will be lost; only new
  44.          * commits are inserted into the ref.
  45.          */
  46.         UPDATE,

  47.         /**
  48.          * Update an existing ref by potentially discarding objects.
  49.          * <p>
  50.          * The current value of the ref is not fully reachable from the new
  51.          * value of the ref, so a successful command may result in one or more
  52.          * objects becoming unreachable.
  53.          */
  54.         UPDATE_NONFASTFORWARD,

  55.         /** Delete an existing ref; the ref should already exist. */
  56.         DELETE;
  57.     }

  58.     /** Result of the update command. */
  59.     public enum Result {
  60.         /** The command has not yet been attempted by the server. */
  61.         NOT_ATTEMPTED,

  62.         /** The server is configured to deny creation of this ref. */
  63.         REJECTED_NOCREATE,

  64.         /** The server is configured to deny deletion of this ref. */
  65.         REJECTED_NODELETE,

  66.         /** The update is a non-fast-forward update and isn't permitted. */
  67.         REJECTED_NONFASTFORWARD,

  68.         /** The update affects <code>HEAD</code> and cannot be permitted. */
  69.         REJECTED_CURRENT_BRANCH,

  70.         /**
  71.          * One or more objects aren't in the repository.
  72.          * <p>
  73.          * This is severe indication of either repository corruption on the
  74.          * server side, or a bug in the client wherein the client did not supply
  75.          * all required objects during the pack transfer.
  76.          */
  77.         REJECTED_MISSING_OBJECT,

  78.         /** Other failure; see {@link ReceiveCommand#getMessage()}. */
  79.         REJECTED_OTHER_REASON,

  80.         /** The ref could not be locked and updated atomically; try again. */
  81.         LOCK_FAILURE,

  82.         /** The change was completed successfully. */
  83.         OK;
  84.     }

  85.     /**
  86.      * Filter a collection of commands according to result.
  87.      *
  88.      * @param in
  89.      *            commands to filter.
  90.      * @param want
  91.      *            desired status to filter by.
  92.      * @return a copy of the command list containing only those commands with
  93.      *         the desired status.
  94.      * @since 4.2
  95.      */
  96.     public static List<ReceiveCommand> filter(Iterable<ReceiveCommand> in,
  97.             Result want) {
  98.         List<ReceiveCommand> r;
  99.         if (in instanceof Collection)
  100.             r = new ArrayList<>(((Collection<?>) in).size());
  101.         else
  102.             r = new ArrayList<>();
  103.         for (ReceiveCommand cmd : in) {
  104.             if (cmd.getResult() == want)
  105.                 r.add(cmd);
  106.         }
  107.         return r;
  108.     }

  109.     /**
  110.      * Filter a list of commands according to result.
  111.      *
  112.      * @param commands
  113.      *            commands to filter.
  114.      * @param want
  115.      *            desired status to filter by.
  116.      * @return a copy of the command list containing only those commands with
  117.      *         the desired status.
  118.      * @since 2.0
  119.      */
  120.     public static List<ReceiveCommand> filter(List<ReceiveCommand> commands,
  121.             Result want) {
  122.         return filter((Iterable<ReceiveCommand>) commands, want);
  123.     }

  124.     /**
  125.      * Set unprocessed commands as failed due to transaction aborted.
  126.      * <p>
  127.      * If a command is still
  128.      * {@link org.eclipse.jgit.transport.ReceiveCommand.Result#NOT_ATTEMPTED} it
  129.      * will be set to
  130.      * {@link org.eclipse.jgit.transport.ReceiveCommand.Result#REJECTED_OTHER_REASON}.
  131.      *
  132.      * @param commands
  133.      *            commands to mark as failed.
  134.      * @since 4.2
  135.      */
  136.     public static void abort(Iterable<ReceiveCommand> commands) {
  137.         for (ReceiveCommand c : commands) {
  138.             if (c.getResult() == NOT_ATTEMPTED) {
  139.                 c.setResult(REJECTED_OTHER_REASON,
  140.                         JGitText.get().transactionAborted);
  141.             }
  142.         }
  143.     }

  144.     /**
  145.      * Check whether a command failed due to transaction aborted.
  146.      *
  147.      * @param cmd
  148.      *            command.
  149.      * @return whether the command failed due to transaction aborted, as in
  150.      *         {@link #abort(Iterable)}.
  151.      * @since 4.9
  152.      */
  153.     public static boolean isTransactionAborted(ReceiveCommand cmd) {
  154.         return cmd.getResult() == REJECTED_OTHER_REASON
  155.                 && cmd.getMessage().equals(JGitText.get().transactionAborted);
  156.     }

  157.     /**
  158.      * Create a command to switch a reference from object to symbolic.
  159.      *
  160.      * @param oldId
  161.      *            expected oldId. May be {@code zeroId} to create.
  162.      * @param newTarget
  163.      *            new target; must begin with {@code "refs/"}.
  164.      * @param name
  165.      *            name of the reference to make symbolic.
  166.      * @return command instance.
  167.      * @since 4.10
  168.      */
  169.     public static ReceiveCommand link(@NonNull ObjectId oldId,
  170.             @NonNull String newTarget, @NonNull String name) {
  171.         return new ReceiveCommand(oldId, newTarget, name);
  172.     }

  173.     /**
  174.      * Create a command to switch a symbolic reference's target.
  175.      *
  176.      * @param oldTarget
  177.      *            expected old target. May be null to create.
  178.      * @param newTarget
  179.      *            new target; must begin with {@code "refs/"}.
  180.      * @param name
  181.      *            name of the reference to make symbolic.
  182.      * @return command instance.
  183.      * @since 4.10
  184.      */
  185.     public static ReceiveCommand link(@Nullable String oldTarget,
  186.             @NonNull String newTarget, @NonNull String name) {
  187.         return new ReceiveCommand(oldTarget, newTarget, name);
  188.     }

  189.     /**
  190.      * Create a command to switch a reference from symbolic to object.
  191.      *
  192.      * @param oldTarget
  193.      *            expected old target.
  194.      * @param newId
  195.      *            new object identifier. May be {@code zeroId()} to delete.
  196.      * @param name
  197.      *            name of the reference to convert from symbolic.
  198.      * @return command instance.
  199.      * @since 4.10
  200.      */
  201.     public static ReceiveCommand unlink(@NonNull String oldTarget,
  202.             @NonNull ObjectId newId, @NonNull String name) {
  203.         return new ReceiveCommand(oldTarget, newId, name);
  204.     }

  205.     private final ObjectId oldId;

  206.     private final String oldSymref;

  207.     private final ObjectId newId;

  208.     private final String newSymref;

  209.     private final String name;

  210.     private Type type;

  211.     private boolean typeIsCorrect;

  212.     private Ref ref;

  213.     private Result status = Result.NOT_ATTEMPTED;

  214.     private String message;

  215.     private boolean customRefLog;

  216.     private String refLogMessage;

  217.     private boolean refLogIncludeResult;

  218.     private Boolean forceRefLog;

  219.     /**
  220.      * Create a new command for
  221.      * {@link org.eclipse.jgit.transport.ReceivePack}.
  222.      *
  223.      * @param oldId
  224.      *            the expected old object id; must not be null. Use
  225.      *            {@link org.eclipse.jgit.lib.ObjectId#zeroId()} to indicate a
  226.      *            ref creation.
  227.      * @param newId
  228.      *            the new object id; must not be null. Use
  229.      *            {@link org.eclipse.jgit.lib.ObjectId#zeroId()} to indicate a
  230.      *            ref deletion.
  231.      * @param name
  232.      *            name of the ref being affected.
  233.      */
  234.     public ReceiveCommand(final ObjectId oldId, final ObjectId newId,
  235.             final String name) {
  236.         if (oldId == null) {
  237.             throw new IllegalArgumentException(
  238.                     JGitText.get().oldIdMustNotBeNull);
  239.         }
  240.         if (newId == null) {
  241.             throw new IllegalArgumentException(
  242.                     JGitText.get().newIdMustNotBeNull);
  243.         }
  244.         if (name == null || name.isEmpty()) {
  245.             throw new IllegalArgumentException(
  246.                     JGitText.get().nameMustNotBeNullOrEmpty);
  247.         }
  248.         this.oldId = oldId;
  249.         this.oldSymref = null;
  250.         this.newId = newId;
  251.         this.newSymref = null;
  252.         this.name = name;

  253.         type = Type.UPDATE;
  254.         if (ObjectId.zeroId().equals(oldId)) {
  255.             type = Type.CREATE;
  256.         }
  257.         if (ObjectId.zeroId().equals(newId)) {
  258.             type = Type.DELETE;
  259.         }
  260.     }

  261.     /**
  262.      * Create a new command for
  263.      * {@link org.eclipse.jgit.transport.ReceivePack}.
  264.      *
  265.      * @param oldId
  266.      *            the old object id; must not be null. Use
  267.      *            {@link org.eclipse.jgit.lib.ObjectId#zeroId()} to indicate a
  268.      *            ref creation.
  269.      * @param newId
  270.      *            the new object id; must not be null. Use
  271.      *            {@link org.eclipse.jgit.lib.ObjectId#zeroId()} to indicate a
  272.      *            ref deletion.
  273.      * @param name
  274.      *            name of the ref being affected.
  275.      * @param type
  276.      *            type of the command. Must be
  277.      *            {@link org.eclipse.jgit.transport.ReceiveCommand.Type#CREATE}
  278.      *            if {@code
  279.      *            oldId} is zero, or
  280.      *            {@link org.eclipse.jgit.transport.ReceiveCommand.Type#DELETE}
  281.      *            if {@code newId} is zero.
  282.      * @since 2.0
  283.      */
  284.     public ReceiveCommand(final ObjectId oldId, final ObjectId newId,
  285.             final String name, final Type type) {
  286.         if (oldId == null) {
  287.             throw new IllegalArgumentException(
  288.                     JGitText.get().oldIdMustNotBeNull);
  289.         }
  290.         if (newId == null) {
  291.             throw new IllegalArgumentException(
  292.                     JGitText.get().newIdMustNotBeNull);
  293.         }
  294.         if (name == null || name.isEmpty()) {
  295.             throw new IllegalArgumentException(
  296.                     JGitText.get().nameMustNotBeNullOrEmpty);
  297.         }
  298.         this.oldId = oldId;
  299.         this.oldSymref = null;
  300.         this.newId = newId;
  301.         this.newSymref = null;
  302.         this.name = name;
  303.         switch (type) {
  304.         case CREATE:
  305.             if (!ObjectId.zeroId().equals(oldId)) {
  306.                 throw new IllegalArgumentException(
  307.                         JGitText.get().createRequiresZeroOldId);
  308.             }
  309.             break;
  310.         case DELETE:
  311.             if (!ObjectId.zeroId().equals(newId)) {
  312.                 throw new IllegalArgumentException(
  313.                         JGitText.get().deleteRequiresZeroNewId);
  314.             }
  315.             break;
  316.         case UPDATE:
  317.         case UPDATE_NONFASTFORWARD:
  318.             if (ObjectId.zeroId().equals(newId)
  319.                     || ObjectId.zeroId().equals(oldId)) {
  320.                 throw new IllegalArgumentException(
  321.                         JGitText.get().updateRequiresOldIdAndNewId);
  322.             }
  323.             break;
  324.         default:
  325.             throw new IllegalStateException(
  326.                     JGitText.get().enumValueNotSupported0);
  327.         }
  328.         this.type = type;
  329.     }

  330.     /**
  331.      * Create a command to switch a reference from object to symbolic.
  332.      *
  333.      * @param oldId
  334.      *            the old object id; must not be null. Use
  335.      *            {@link ObjectId#zeroId()} to indicate a ref creation.
  336.      * @param newSymref
  337.      *            new target, must begin with {@code "refs/"}. Use {@code null}
  338.      *            to indicate a ref deletion.
  339.      * @param name
  340.      *            name of the reference to make symbolic.
  341.      * @since 4.10
  342.      */
  343.     private ReceiveCommand(ObjectId oldId, String newSymref, String name) {
  344.         if (oldId == null) {
  345.             throw new IllegalArgumentException(
  346.                     JGitText.get().oldIdMustNotBeNull);
  347.         }
  348.         if (name == null || name.isEmpty()) {
  349.             throw new IllegalArgumentException(
  350.                     JGitText.get().nameMustNotBeNullOrEmpty);
  351.         }
  352.         this.oldId = oldId;
  353.         this.oldSymref = null;
  354.         this.newId = ObjectId.zeroId();
  355.         this.newSymref = newSymref;
  356.         this.name = name;
  357.         if (AnyObjectId.isEqual(ObjectId.zeroId(), oldId)) {
  358.             type = Type.CREATE;
  359.         } else if (newSymref != null) {
  360.             type = Type.UPDATE;
  361.         } else {
  362.             type = Type.DELETE;
  363.         }
  364.         typeIsCorrect = true;
  365.     }

  366.     /**
  367.      * Create a command to switch a reference from symbolic to object.
  368.      *
  369.      * @param oldSymref
  370.      *            expected old target. Use {@code null} to indicate a ref
  371.      *            creation.
  372.      * @param newId
  373.      *            the new object id; must not be null. Use
  374.      *            {@link ObjectId#zeroId()} to indicate a ref deletion.
  375.      * @param name
  376.      *            name of the reference to convert from symbolic.
  377.      * @since 4.10
  378.      */
  379.     private ReceiveCommand(String oldSymref, ObjectId newId, String name) {
  380.         if (newId == null) {
  381.             throw new IllegalArgumentException(
  382.                     JGitText.get().newIdMustNotBeNull);
  383.         }
  384.         if (name == null || name.isEmpty()) {
  385.             throw new IllegalArgumentException(
  386.                     JGitText.get().nameMustNotBeNullOrEmpty);
  387.         }
  388.         this.oldId = ObjectId.zeroId();
  389.         this.oldSymref = oldSymref;
  390.         this.newId = newId;
  391.         this.newSymref = null;
  392.         this.name = name;
  393.         if (oldSymref == null) {
  394.             type = Type.CREATE;
  395.         } else if (!AnyObjectId.isEqual(ObjectId.zeroId(), newId)) {
  396.             type = Type.UPDATE;
  397.         } else {
  398.             type = Type.DELETE;
  399.         }
  400.         typeIsCorrect = true;
  401.     }

  402.     /**
  403.      * Create a command to switch a symbolic reference's target.
  404.      *
  405.      * @param oldTarget
  406.      *            expected old target. Use {@code null} to indicate a ref
  407.      *            creation.
  408.      * @param newTarget
  409.      *            new target. Use {@code null} to indicate a ref deletion.
  410.      * @param name
  411.      *            name of the reference to make symbolic.
  412.      * @since 4.10
  413.      */
  414.     private ReceiveCommand(@Nullable String oldTarget, String newTarget, String name) {
  415.         if (name == null || name.isEmpty()) {
  416.             throw new IllegalArgumentException(
  417.                     JGitText.get().nameMustNotBeNullOrEmpty);
  418.         }
  419.         this.oldId = ObjectId.zeroId();
  420.         this.oldSymref = oldTarget;
  421.         this.newId = ObjectId.zeroId();
  422.         this.newSymref = newTarget;
  423.         this.name = name;
  424.         if (oldTarget == null) {
  425.             if (newTarget == null) {
  426.                 throw new IllegalArgumentException(
  427.                         JGitText.get().bothRefTargetsMustNotBeNull);
  428.             }
  429.             type = Type.CREATE;
  430.         } else if (newTarget != null) {
  431.             type = Type.UPDATE;
  432.         } else {
  433.             type = Type.DELETE;
  434.         }
  435.         typeIsCorrect = true;
  436.     }

  437.     /**
  438.      * Get the old value the client thinks the ref has.
  439.      *
  440.      * @return the old value the client thinks the ref has.
  441.      */
  442.     public ObjectId getOldId() {
  443.         return oldId;
  444.     }

  445.     /**
  446.      * Get expected old target for a symbolic reference.
  447.      *
  448.      * @return expected old target for a symbolic reference.
  449.      * @since 4.10
  450.      */
  451.     @Nullable
  452.     public String getOldSymref() {
  453.         return oldSymref;
  454.     }

  455.     /**
  456.      * Get the requested new value for this ref.
  457.      *
  458.      * @return the requested new value for this ref.
  459.      */
  460.     public ObjectId getNewId() {
  461.         return newId;
  462.     }

  463.     /**
  464.      * Get requested new target for a symbolic reference.
  465.      *
  466.      * @return requested new target for a symbolic reference.
  467.      * @since 4.10
  468.      */
  469.     @Nullable
  470.     public String getNewSymref() {
  471.         return newSymref;
  472.     }

  473.     /**
  474.      * Get the name of the ref being updated.
  475.      *
  476.      * @return the name of the ref being updated.
  477.      */
  478.     public String getRefName() {
  479.         return name;
  480.     }

  481.     /**
  482.      * Get the type of this command; see {@link Type}.
  483.      *
  484.      * @return the type of this command; see {@link Type}.
  485.      */
  486.     public Type getType() {
  487.         return type;
  488.     }

  489.     /**
  490.      * Get the ref, if this was advertised by the connection.
  491.      *
  492.      * @return the ref, if this was advertised by the connection.
  493.      */
  494.     public Ref getRef() {
  495.         return ref;
  496.     }

  497.     /**
  498.      * Get the current status code of this command.
  499.      *
  500.      * @return the current status code of this command.
  501.      */
  502.     public Result getResult() {
  503.         return status;
  504.     }

  505.     /**
  506.      * Get the message associated with a failure status.
  507.      *
  508.      * @return the message associated with a failure status.
  509.      */
  510.     public String getMessage() {
  511.         return message;
  512.     }

  513.     /**
  514.      * Set the message to include in the reflog.
  515.      * <p>
  516.      * Overrides the default set by {@code setRefLogMessage} on any containing
  517.      * {@link org.eclipse.jgit.lib.BatchRefUpdate}.
  518.      *
  519.      * @param msg
  520.      *            the message to describe this change. If null and appendStatus is
  521.      *            false, the reflog will not be updated.
  522.      * @param appendStatus
  523.      *            true if the status of the ref change (fast-forward or
  524.      *            forced-update) should be appended to the user supplied message.
  525.      * @since 4.9
  526.      */
  527.     public void setRefLogMessage(String msg, boolean appendStatus) {
  528.         customRefLog = true;
  529.         if (msg == null && !appendStatus) {
  530.             disableRefLog();
  531.         } else if (msg == null && appendStatus) {
  532.             refLogMessage = ""; //$NON-NLS-1$
  533.             refLogIncludeResult = true;
  534.         } else {
  535.             refLogMessage = msg;
  536.             refLogIncludeResult = appendStatus;
  537.         }
  538.     }

  539.     /**
  540.      * Don't record this update in the ref's associated reflog.
  541.      * <p>
  542.      * Equivalent to {@code setRefLogMessage(null, false)}.
  543.      *
  544.      * @since 4.9
  545.      */
  546.     public void disableRefLog() {
  547.         customRefLog = true;
  548.         refLogMessage = null;
  549.         refLogIncludeResult = false;
  550.     }

  551.     /**
  552.      * Force writing a reflog for the updated ref.
  553.      *
  554.      * @param force whether to force.
  555.      * @since 4.9
  556.      */
  557.     public void setForceRefLog(boolean force) {
  558.         forceRefLog = Boolean.valueOf(force);
  559.     }

  560.     /**
  561.      * Check whether this command has a custom reflog message setting that should
  562.      * override defaults in any containing
  563.      * {@link org.eclipse.jgit.lib.BatchRefUpdate}.
  564.      * <p>
  565.      * Does not take into account whether {@code #setForceRefLog(boolean)} has
  566.      * been called.
  567.      *
  568.      * @return whether a custom reflog is set.
  569.      * @since 4.9
  570.      */
  571.     public boolean hasCustomRefLog() {
  572.         return customRefLog;
  573.     }

  574.     /**
  575.      * Check whether log has been disabled by {@link #disableRefLog()}.
  576.      *
  577.      * @return true if disabled.
  578.      * @since 4.9
  579.      */
  580.     public boolean isRefLogDisabled() {
  581.         return refLogMessage == null;
  582.     }

  583.     /**
  584.      * Get the message to include in the reflog.
  585.      *
  586.      * @return message the caller wants to include in the reflog; null if the
  587.      *         update should not be logged.
  588.      * @since 4.9
  589.      */
  590.     @Nullable
  591.     public String getRefLogMessage() {
  592.         return refLogMessage;
  593.     }

  594.     /**
  595.      * Check whether the reflog message should include the result of the update,
  596.      * such as fast-forward or force-update.
  597.      *
  598.      * @return true if the message should include the result.
  599.      * @since 4.9
  600.      */
  601.     public boolean isRefLogIncludingResult() {
  602.         return refLogIncludeResult;
  603.     }

  604.     /**
  605.      * Check whether the reflog should be written regardless of repo defaults.
  606.      *
  607.      * @return whether force writing is enabled; {@code null} if
  608.      *         {@code #setForceRefLog(boolean)} was never called.
  609.      * @since 4.9
  610.      */
  611.     @Nullable
  612.     public Boolean isForceRefLog() {
  613.         return forceRefLog;
  614.     }

  615.     /**
  616.      * Set the status of this command.
  617.      *
  618.      * @param s
  619.      *            the new status code for this command.
  620.      */
  621.     public void setResult(Result s) {
  622.         setResult(s, null);
  623.     }

  624.     /**
  625.      * Set the status of this command.
  626.      *
  627.      * @param s
  628.      *            new status code for this command.
  629.      * @param m
  630.      *            optional message explaining the new status.
  631.      */
  632.     public void setResult(Result s, String m) {
  633.         status = s;
  634.         message = m;
  635.     }

  636.     /**
  637.      * Update the type of this command by checking for fast-forward.
  638.      * <p>
  639.      * If the command's current type is UPDATE, a merge test will be performed
  640.      * using the supplied RevWalk to determine if {@link #getOldId()} is fully
  641.      * merged into {@link #getNewId()}. If some commits are not merged the
  642.      * update type is changed to
  643.      * {@link org.eclipse.jgit.transport.ReceiveCommand.Type#UPDATE_NONFASTFORWARD}.
  644.      *
  645.      * @param walk
  646.      *            an instance to perform the merge test with. The caller must
  647.      *            allocate and release this object.
  648.      * @throws java.io.IOException
  649.      *             either oldId or newId is not accessible in the repository
  650.      *             used by the RevWalk. This usually indicates data corruption,
  651.      *             and the command cannot be processed.
  652.      */
  653.     public void updateType(RevWalk walk) throws IOException {
  654.         if (typeIsCorrect)
  655.             return;
  656.         if (type == Type.UPDATE && !AnyObjectId.isEqual(oldId, newId)) {
  657.             RevObject o = walk.parseAny(oldId);
  658.             RevObject n = walk.parseAny(newId);
  659.             if (!(o instanceof RevCommit)
  660.                     || !(n instanceof RevCommit)
  661.                     || !walk.isMergedInto((RevCommit) o, (RevCommit) n))
  662.                 setType(Type.UPDATE_NONFASTFORWARD);
  663.         }
  664.         typeIsCorrect = true;
  665.     }

  666.     /**
  667.      * Execute this command during a receive-pack session.
  668.      * <p>
  669.      * Sets the status of the command as a side effect.
  670.      *
  671.      * @param rp
  672.      *            receive-pack session.
  673.      * @since 5.6
  674.      */
  675.     public void execute(ReceivePack rp) {
  676.         try {
  677.             String expTarget = getOldSymref();
  678.             boolean detach = getNewSymref() != null
  679.                     || (type == Type.DELETE && expTarget != null);
  680.             RefUpdate ru = rp.getRepository().updateRef(getRefName(), detach);
  681.             if (expTarget != null) {
  682.                 if (!ru.getRef().isSymbolic() || !ru.getRef().getTarget()
  683.                         .getName().equals(expTarget)) {
  684.                     setResult(Result.LOCK_FAILURE);
  685.                     return;
  686.                 }
  687.             }

  688.             ru.setRefLogIdent(rp.getRefLogIdent());
  689.             ru.setRefLogMessage(refLogMessage, refLogIncludeResult);
  690.             switch (getType()) {
  691.             case DELETE:
  692.                 if (!ObjectId.zeroId().equals(getOldId())) {
  693.                     // We can only do a CAS style delete if the client
  694.                     // didn't bork its delete request by sending the
  695.                     // wrong zero id rather than the advertised one.
  696.                     //
  697.                     ru.setExpectedOldObjectId(getOldId());
  698.                 }
  699.                 ru.setForceUpdate(true);
  700.                 setResult(ru.delete(rp.getRevWalk()));
  701.                 break;

  702.             case CREATE:
  703.             case UPDATE:
  704.             case UPDATE_NONFASTFORWARD:
  705.                 ru.setForceUpdate(rp.isAllowNonFastForwards());
  706.                 ru.setExpectedOldObjectId(getOldId());
  707.                 ru.setRefLogMessage("push", true); //$NON-NLS-1$
  708.                 if (getNewSymref() != null) {
  709.                     setResult(ru.link(getNewSymref()));
  710.                 } else {
  711.                     ru.setNewObjectId(getNewId());
  712.                     setResult(ru.update(rp.getRevWalk()));
  713.                 }
  714.                 break;
  715.             }
  716.         } catch (IOException err) {
  717.             reject(err);
  718.         }
  719.     }

  720.     void setRef(Ref r) {
  721.         ref = r;
  722.     }

  723.     void setType(Type t) {
  724.         type = t;
  725.     }

  726.     void setTypeFastForwardUpdate() {
  727.         type = Type.UPDATE;
  728.         typeIsCorrect = true;
  729.     }

  730.     /**
  731.      * Set the result of this command.
  732.      *
  733.      * @param r
  734.      *            the new result code for this command.
  735.      */
  736.     public void setResult(RefUpdate.Result r) {
  737.         switch (r) {
  738.         case NOT_ATTEMPTED:
  739.             setResult(Result.NOT_ATTEMPTED);
  740.             break;

  741.         case LOCK_FAILURE:
  742.         case IO_FAILURE:
  743.             setResult(Result.LOCK_FAILURE);
  744.             break;

  745.         case NO_CHANGE:
  746.         case NEW:
  747.         case FORCED:
  748.         case FAST_FORWARD:
  749.             setResult(Result.OK);
  750.             break;

  751.         case REJECTED:
  752.             setResult(Result.REJECTED_NONFASTFORWARD);
  753.             break;

  754.         case REJECTED_CURRENT_BRANCH:
  755.             setResult(Result.REJECTED_CURRENT_BRANCH);
  756.             break;

  757.         case REJECTED_MISSING_OBJECT:
  758.             setResult(Result.REJECTED_MISSING_OBJECT);
  759.             break;

  760.         case REJECTED_OTHER_REASON:
  761.             setResult(Result.REJECTED_OTHER_REASON);
  762.             break;

  763.         default:
  764.             setResult(Result.REJECTED_OTHER_REASON, r.name());
  765.             break;
  766.         }
  767.     }

  768.     void reject(IOException err) {
  769.         setResult(Result.REJECTED_OTHER_REASON, MessageFormat.format(
  770.                 JGitText.get().lockError, err.getMessage()));
  771.     }

  772.     /** {@inheritDoc} */
  773.     @SuppressWarnings("nls")
  774.     @Override
  775.     public String toString() {
  776.         return getType().name() + ": " + getOldId().name() + " "
  777.                 + getNewId().name() + " " + getRefName();
  778.     }
  779. }