RemoteRefUpdate.java

  1. /*
  2.  * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com> 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 java.io.IOException;
  12. import java.text.MessageFormat;

  13. import org.eclipse.jgit.internal.JGitText;
  14. import org.eclipse.jgit.lib.ObjectId;
  15. import org.eclipse.jgit.lib.Ref;
  16. import org.eclipse.jgit.lib.RefUpdate;
  17. import org.eclipse.jgit.lib.Repository;
  18. import org.eclipse.jgit.revwalk.RevWalk;

  19. /**
  20.  * Represent request and status of a remote ref update. Specification is
  21.  * provided by client, while status is handled by
  22.  * {@link org.eclipse.jgit.transport.PushProcess} class, being read-only for
  23.  * client.
  24.  * <p>
  25.  * Client can create instances of this class directly, basing on user
  26.  * specification and advertised refs
  27.  * ({@link org.eclipse.jgit.transport.Connection} or through
  28.  * {@link org.eclipse.jgit.transport.Transport} helper methods. Apply this
  29.  * specification on remote repository using
  30.  * {@link org.eclipse.jgit.transport.Transport#push(org.eclipse.jgit.lib.ProgressMonitor, java.util.Collection)}
  31.  * method.
  32.  * </p>
  33.  */
  34. public class RemoteRefUpdate {
  35.     /**
  36.      * Represent current status of a remote ref update.
  37.      */
  38.     public enum Status {
  39.         /**
  40.          * Push process hasn't yet attempted to update this ref. This is the
  41.          * default status, prior to push process execution.
  42.          */
  43.         NOT_ATTEMPTED,

  44.         /**
  45.          * Remote ref was up to date, there was no need to update anything.
  46.          */
  47.         UP_TO_DATE,

  48.         /**
  49.          * Remote ref update was rejected, as it would cause non fast-forward
  50.          * update.
  51.          */
  52.         REJECTED_NONFASTFORWARD,

  53.         /**
  54.          * Remote ref update was rejected, because remote side doesn't
  55.          * support/allow deleting refs.
  56.          */
  57.         REJECTED_NODELETE,

  58.         /**
  59.          * Remote ref update was rejected, because old object id on remote
  60.          * repository wasn't the same as defined expected old object.
  61.          */
  62.         REJECTED_REMOTE_CHANGED,

  63.         /**
  64.          * Remote ref update was rejected for other reason, possibly described
  65.          * in {@link RemoteRefUpdate#getMessage()}.
  66.          */
  67.         REJECTED_OTHER_REASON,

  68.         /**
  69.          * Remote ref didn't exist. Can occur on delete request of a non
  70.          * existing ref.
  71.          */
  72.         NON_EXISTING,

  73.         /**
  74.          * Push process is awaiting update report from remote repository. This
  75.          * is a temporary state or state after critical error in push process.
  76.          */
  77.         AWAITING_REPORT,

  78.         /**
  79.          * Remote ref was successfully updated.
  80.          */
  81.         OK;
  82.     }

  83.     private ObjectId expectedOldObjectId;

  84.     private final ObjectId newObjectId;

  85.     private final String remoteName;

  86.     private final TrackingRefUpdate trackingRefUpdate;

  87.     private final String srcRef;

  88.     private final boolean forceUpdate;

  89.     private Status status;

  90.     private boolean fastForward;

  91.     private String message;

  92.     private final Repository localDb;

  93.     private RefUpdate localUpdate;

  94.     /**
  95.      * Construct remote ref update request by providing an update specification.
  96.      * Object is created with default
  97.      * {@link org.eclipse.jgit.transport.RemoteRefUpdate.Status#NOT_ATTEMPTED}
  98.      * status and no message.
  99.      *
  100.      * @param localDb
  101.      *            local repository to push from.
  102.      * @param srcRef
  103.      *            source revision - any string resolvable by
  104.      *            {@link org.eclipse.jgit.lib.Repository#resolve(String)}. This
  105.      *            resolves to the new object that the caller want remote ref to
  106.      *            be after update. Use null or
  107.      *            {@link org.eclipse.jgit.lib.ObjectId#zeroId()} string for
  108.      *            delete request.
  109.      * @param remoteName
  110.      *            full name of a remote ref to update, e.g. "refs/heads/master"
  111.      *            (no wildcard, no short name).
  112.      * @param forceUpdate
  113.      *            true when caller want remote ref to be updated regardless
  114.      *            whether it is fast-forward update (old object is ancestor of
  115.      *            new object).
  116.      * @param localName
  117.      *            optional full name of a local stored tracking branch, to
  118.      *            update after push, e.g. "refs/remotes/zawir/dirty" (no
  119.      *            wildcard, no short name); null if no local tracking branch
  120.      *            should be updated.
  121.      * @param expectedOldObjectId
  122.      *            optional object id that caller is expecting, requiring to be
  123.      *            advertised by remote side before update; update will take
  124.      *            place ONLY if remote side advertise exactly this expected id;
  125.      *            null if caller doesn't care what object id remote side
  126.      *            advertise. Use {@link org.eclipse.jgit.lib.ObjectId#zeroId()}
  127.      *            when expecting no remote ref with this name.
  128.      * @throws java.io.IOException
  129.      *             when I/O error occurred during creating
  130.      *             {@link org.eclipse.jgit.transport.TrackingRefUpdate} for
  131.      *             local tracking branch or srcRef can't be resolved to any
  132.      *             object.
  133.      * @throws java.lang.IllegalArgumentException
  134.      *             if some required parameter was null
  135.      */
  136.     public RemoteRefUpdate(final Repository localDb, final String srcRef,
  137.             final String remoteName, final boolean forceUpdate,
  138.             final String localName, final ObjectId expectedOldObjectId)
  139.             throws IOException {
  140.         this(localDb, srcRef, srcRef != null ? localDb.resolve(srcRef)
  141.                 : ObjectId.zeroId(), remoteName, forceUpdate, localName,
  142.                 expectedOldObjectId);
  143.     }

  144.     /**
  145.      * Construct remote ref update request by providing an update specification.
  146.      * Object is created with default
  147.      * {@link org.eclipse.jgit.transport.RemoteRefUpdate.Status#NOT_ATTEMPTED}
  148.      * status and no message.
  149.      *
  150.      * @param localDb
  151.      *            local repository to push from.
  152.      * @param srcRef
  153.      *            source revision. Use null to delete.
  154.      * @param remoteName
  155.      *            full name of a remote ref to update, e.g. "refs/heads/master"
  156.      *            (no wildcard, no short name).
  157.      * @param forceUpdate
  158.      *            true when caller want remote ref to be updated regardless
  159.      *            whether it is fast-forward update (old object is ancestor of
  160.      *            new object).
  161.      * @param localName
  162.      *            optional full name of a local stored tracking branch, to
  163.      *            update after push, e.g. "refs/remotes/zawir/dirty" (no
  164.      *            wildcard, no short name); null if no local tracking branch
  165.      *            should be updated.
  166.      * @param expectedOldObjectId
  167.      *            optional object id that caller is expecting, requiring to be
  168.      *            advertised by remote side before update; update will take
  169.      *            place ONLY if remote side advertise exactly this expected id;
  170.      *            null if caller doesn't care what object id remote side
  171.      *            advertise. Use {@link org.eclipse.jgit.lib.ObjectId#zeroId()}
  172.      *            when expecting no remote ref with this name.
  173.      * @throws java.io.IOException
  174.      *             when I/O error occurred during creating
  175.      *             {@link org.eclipse.jgit.transport.TrackingRefUpdate} for
  176.      *             local tracking branch or srcRef can't be resolved to any
  177.      *             object.
  178.      * @throws java.lang.IllegalArgumentException
  179.      *             if some required parameter was null
  180.      */
  181.     public RemoteRefUpdate(final Repository localDb, final Ref srcRef,
  182.             final String remoteName, final boolean forceUpdate,
  183.             final String localName, final ObjectId expectedOldObjectId)
  184.             throws IOException {
  185.         this(localDb, srcRef != null ? srcRef.getName() : null,
  186.                 srcRef != null ? srcRef.getObjectId() : null, remoteName,
  187.                 forceUpdate, localName, expectedOldObjectId);
  188.     }

  189.     /**
  190.      * Construct remote ref update request by providing an update specification.
  191.      * Object is created with default
  192.      * {@link org.eclipse.jgit.transport.RemoteRefUpdate.Status#NOT_ATTEMPTED}
  193.      * status and no message.
  194.      *
  195.      * @param localDb
  196.      *            local repository to push from.
  197.      * @param srcRef
  198.      *            source revision to label srcId with. If null srcId.name() will
  199.      *            be used instead.
  200.      * @param srcId
  201.      *            The new object that the caller wants remote ref to be after
  202.      *            update. Use null or
  203.      *            {@link org.eclipse.jgit.lib.ObjectId#zeroId()} for delete
  204.      *            request.
  205.      * @param remoteName
  206.      *            full name of a remote ref to update, e.g. "refs/heads/master"
  207.      *            (no wildcard, no short name).
  208.      * @param forceUpdate
  209.      *            true when caller want remote ref to be updated regardless
  210.      *            whether it is fast-forward update (old object is ancestor of
  211.      *            new object).
  212.      * @param localName
  213.      *            optional full name of a local stored tracking branch, to
  214.      *            update after push, e.g. "refs/remotes/zawir/dirty" (no
  215.      *            wildcard, no short name); null if no local tracking branch
  216.      *            should be updated.
  217.      * @param expectedOldObjectId
  218.      *            optional object id that caller is expecting, requiring to be
  219.      *            advertised by remote side before update; update will take
  220.      *            place ONLY if remote side advertise exactly this expected id;
  221.      *            null if caller doesn't care what object id remote side
  222.      *            advertise. Use {@link org.eclipse.jgit.lib.ObjectId#zeroId()}
  223.      *            when expecting no remote ref with this name.
  224.      * @throws java.io.IOException
  225.      *             when I/O error occurred during creating
  226.      *             {@link org.eclipse.jgit.transport.TrackingRefUpdate} for
  227.      *             local tracking branch or srcRef can't be resolved to any
  228.      *             object.
  229.      * @throws java.lang.IllegalArgumentException
  230.      *             if some required parameter was null
  231.      */
  232.     public RemoteRefUpdate(final Repository localDb, final String srcRef,
  233.             final ObjectId srcId, final String remoteName,
  234.             final boolean forceUpdate, final String localName,
  235.             final ObjectId expectedOldObjectId) throws IOException {
  236.         if (remoteName == null)
  237.             throw new IllegalArgumentException(JGitText.get().remoteNameCannotBeNull);
  238.         if (srcId == null && srcRef != null)
  239.             throw new IOException(MessageFormat.format(
  240.                     JGitText.get().sourceRefDoesntResolveToAnyObject, srcRef));

  241.         if (srcRef != null)
  242.             this.srcRef = srcRef;
  243.         else if (srcId != null && !srcId.equals(ObjectId.zeroId()))
  244.             this.srcRef = srcId.name();
  245.         else
  246.             this.srcRef = null;

  247.         if (srcId != null)
  248.             this.newObjectId = srcId;
  249.         else
  250.             this.newObjectId = ObjectId.zeroId();

  251.         this.remoteName = remoteName;
  252.         this.forceUpdate = forceUpdate;
  253.         if (localName != null && localDb != null) {
  254.             localUpdate = localDb.updateRef(localName);
  255.             localUpdate.setForceUpdate(true);
  256.             localUpdate.setRefLogMessage("push", true); //$NON-NLS-1$
  257.             localUpdate.setNewObjectId(newObjectId);
  258.             trackingRefUpdate = new TrackingRefUpdate(
  259.                     true,
  260.                     remoteName,
  261.                     localName,
  262.                     localUpdate.getOldObjectId() != null
  263.                         ? localUpdate.getOldObjectId()
  264.                         : ObjectId.zeroId(),
  265.                     newObjectId);
  266.         } else
  267.             trackingRefUpdate = null;
  268.         this.localDb = localDb;
  269.         this.expectedOldObjectId = expectedOldObjectId;
  270.         this.status = Status.NOT_ATTEMPTED;
  271.     }

  272.     /**
  273.      * Create a new instance of this object basing on existing instance for
  274.      * configuration. State (like {@link #getMessage()}, {@link #getStatus()})
  275.      * of base object is not shared. Expected old object id is set up from
  276.      * scratch, as this constructor may be used for 2-stage push: first one
  277.      * being dry run, second one being actual push.
  278.      *
  279.      * @param base
  280.      *            configuration base.
  281.      * @param newExpectedOldObjectId
  282.      *            new expected object id value.
  283.      * @throws java.io.IOException
  284.      *             when I/O error occurred during creating
  285.      *             {@link org.eclipse.jgit.transport.TrackingRefUpdate} for
  286.      *             local tracking branch or srcRef of base object no longer can
  287.      *             be resolved to any object.
  288.      */
  289.     public RemoteRefUpdate(final RemoteRefUpdate base,
  290.             final ObjectId newExpectedOldObjectId) throws IOException {
  291.         this(base.localDb, base.srcRef, base.remoteName, base.forceUpdate,
  292.                 (base.trackingRefUpdate == null ? null : base.trackingRefUpdate
  293.                         .getLocalName()), newExpectedOldObjectId);
  294.     }

  295.     /**
  296.      * Get expected old object id
  297.      *
  298.      * @return expectedOldObjectId required to be advertised by remote side, as
  299.      *         set in constructor; may be null.
  300.      */
  301.     public ObjectId getExpectedOldObjectId() {
  302.         return expectedOldObjectId;
  303.     }

  304.     /**
  305.      * Whether some object is required to be advertised by remote side, as set
  306.      * in constructor
  307.      *
  308.      * @return true if some object is required to be advertised by remote side,
  309.      *         as set in constructor; false otherwise.
  310.      */
  311.     public boolean isExpectingOldObjectId() {
  312.         return expectedOldObjectId != null;
  313.     }

  314.     /**
  315.      * Get new object id
  316.      *
  317.      * @return newObjectId for remote ref, as set in constructor.
  318.      */
  319.     public ObjectId getNewObjectId() {
  320.         return newObjectId;
  321.     }

  322.     /**
  323.      * Whether this update is a deleting update
  324.      *
  325.      * @return true if this update is deleting update; false otherwise.
  326.      */
  327.     public boolean isDelete() {
  328.         return ObjectId.zeroId().equals(newObjectId);
  329.     }

  330.     /**
  331.      * Get name of remote ref to update
  332.      *
  333.      * @return name of remote ref to update, as set in constructor.
  334.      */
  335.     public String getRemoteName() {
  336.         return remoteName;
  337.     }

  338.     /**
  339.      * Get tracking branch update if localName was set in constructor.
  340.      *
  341.      * @return local tracking branch update if localName was set in constructor.
  342.      */
  343.     public TrackingRefUpdate getTrackingRefUpdate() {
  344.         return trackingRefUpdate;
  345.     }

  346.     /**
  347.      * Get source revision as specified by user (in constructor)
  348.      *
  349.      * @return source revision as specified by user (in constructor), could be
  350.      *         any string parseable by
  351.      *         {@link org.eclipse.jgit.lib.Repository#resolve(String)}; can be
  352.      *         null if specified that way in constructor - this stands for
  353.      *         delete request.
  354.      */
  355.     public String getSrcRef() {
  356.         return srcRef;
  357.     }

  358.     /**
  359.      * Whether user specified a local tracking branch for remote update
  360.      *
  361.      * @return true if user specified a local tracking branch for remote update;
  362.      *         false otherwise.
  363.      */
  364.     public boolean hasTrackingRefUpdate() {
  365.         return trackingRefUpdate != null;
  366.     }

  367.     /**
  368.      * Whether this update is forced regardless of old remote ref object
  369.      *
  370.      * @return true if this update is forced regardless of old remote ref
  371.      *         object; false otherwise.
  372.      */
  373.     public boolean isForceUpdate() {
  374.         return forceUpdate;
  375.     }

  376.     /**
  377.      * Get status of remote ref update operation.
  378.      *
  379.      * @return status of remote ref update operation.
  380.      */
  381.     public Status getStatus() {
  382.         return status;
  383.     }

  384.     /**
  385.      * Check whether update was fast-forward. Note that this result is
  386.      * meaningful only after successful update (when status is
  387.      * {@link org.eclipse.jgit.transport.RemoteRefUpdate.Status#OK}).
  388.      *
  389.      * @return true if update was fast-forward; false otherwise.
  390.      */
  391.     public boolean isFastForward() {
  392.         return fastForward;
  393.     }

  394.     /**
  395.      * Get message describing reasons of status when needed/possible; may be
  396.      * null.
  397.      *
  398.      * @return message describing reasons of status when needed/possible; may be
  399.      *         null.
  400.      */
  401.     public String getMessage() {
  402.         return message;
  403.     }

  404.     void setExpectedOldObjectId(ObjectId id) {
  405.         expectedOldObjectId = id;
  406.     }

  407.     void setStatus(Status status) {
  408.         this.status = status;
  409.     }

  410.     void setFastForward(boolean fastForward) {
  411.         this.fastForward = fastForward;
  412.     }

  413.     void setMessage(String message) {
  414.         this.message = message;
  415.     }

  416.     /**
  417.      * Update locally stored tracking branch with the new object.
  418.      *
  419.      * @param walk
  420.      *            walker used for checking update properties.
  421.      * @throws java.io.IOException
  422.      *             when I/O error occurred during update
  423.      */
  424.     protected void updateTrackingRef(RevWalk walk) throws IOException {
  425.         if (isDelete())
  426.             trackingRefUpdate.setResult(localUpdate.delete(walk));
  427.         else
  428.             trackingRefUpdate.setResult(localUpdate.update(walk));
  429.     }

  430.     /** {@inheritDoc} */
  431.     @SuppressWarnings("nls")
  432.     @Override
  433.     public String toString() {
  434.         return "RemoteRefUpdate[remoteName="
  435.                 + remoteName
  436.                 + ", "
  437.                 + status
  438.                 + ", "
  439.                 + (expectedOldObjectId != null ? expectedOldObjectId.name()
  440.                         : "(null)") + "..."
  441.                 + (newObjectId != null ? newObjectId.name() : "(null)")
  442.                 + (fastForward ? ", fastForward" : "")
  443.                 + ", srcRef=" + srcRef
  444.                 + (forceUpdate ? ", forceUpdate" : "") + ", message="
  445.                 + (message != null ? "\"" + message + "\"" : "null") + "]";
  446.     }
  447. }