| /* |
| * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com> and others |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Distribution License v. 1.0 which is available at |
| * https://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| package org.eclipse.jgit.transport; |
| |
| import java.io.IOException; |
| import java.text.MessageFormat; |
| |
| import org.eclipse.jgit.internal.JGitText; |
| import org.eclipse.jgit.lib.ObjectId; |
| import org.eclipse.jgit.lib.Ref; |
| import org.eclipse.jgit.lib.RefUpdate; |
| import org.eclipse.jgit.lib.Repository; |
| import org.eclipse.jgit.revwalk.RevWalk; |
| |
| /** |
| * Represent request and status of a remote ref update. Specification is |
| * provided by client, while status is handled by |
| * {@link org.eclipse.jgit.transport.PushProcess} class, being read-only for |
| * client. |
| * <p> |
| * Client can create instances of this class directly, basing on user |
| * specification and advertised refs |
| * ({@link org.eclipse.jgit.transport.Connection} or through |
| * {@link org.eclipse.jgit.transport.Transport} helper methods. Apply this |
| * specification on remote repository using |
| * {@link org.eclipse.jgit.transport.Transport#push(org.eclipse.jgit.lib.ProgressMonitor, java.util.Collection)} |
| * method. |
| * </p> |
| */ |
| public class RemoteRefUpdate { |
| /** |
| * Represent current status of a remote ref update. |
| */ |
| public enum Status { |
| /** |
| * Push process hasn't yet attempted to update this ref. This is the |
| * default status, prior to push process execution. |
| */ |
| NOT_ATTEMPTED, |
| |
| /** |
| * Remote ref was up to date, there was no need to update anything. |
| */ |
| UP_TO_DATE, |
| |
| /** |
| * Remote ref update was rejected, as it would cause non fast-forward |
| * update. |
| */ |
| REJECTED_NONFASTFORWARD, |
| |
| /** |
| * Remote ref update was rejected, because remote side doesn't |
| * support/allow deleting refs. |
| */ |
| REJECTED_NODELETE, |
| |
| /** |
| * Remote ref update was rejected, because old object id on remote |
| * repository wasn't the same as defined expected old object. |
| */ |
| REJECTED_REMOTE_CHANGED, |
| |
| /** |
| * Remote ref update was rejected for other reason, possibly described |
| * in {@link RemoteRefUpdate#getMessage()}. |
| */ |
| REJECTED_OTHER_REASON, |
| |
| /** |
| * Remote ref didn't exist. Can occur on delete request of a non |
| * existing ref. |
| */ |
| NON_EXISTING, |
| |
| /** |
| * Push process is awaiting update report from remote repository. This |
| * is a temporary state or state after critical error in push process. |
| */ |
| AWAITING_REPORT, |
| |
| /** |
| * Remote ref was successfully updated. |
| */ |
| OK; |
| } |
| |
| private ObjectId expectedOldObjectId; |
| |
| private final ObjectId newObjectId; |
| |
| private final String remoteName; |
| |
| private final TrackingRefUpdate trackingRefUpdate; |
| |
| private final String srcRef; |
| |
| private final boolean forceUpdate; |
| |
| private Status status; |
| |
| private boolean fastForward; |
| |
| private String message; |
| |
| private final Repository localDb; |
| |
| private RefUpdate localUpdate; |
| |
| /** |
| * Construct remote ref update request by providing an update specification. |
| * Object is created with default |
| * {@link org.eclipse.jgit.transport.RemoteRefUpdate.Status#NOT_ATTEMPTED} |
| * status and no message. |
| * |
| * @param localDb |
| * local repository to push from. |
| * @param srcRef |
| * source revision - any string resolvable by |
| * {@link org.eclipse.jgit.lib.Repository#resolve(String)}. This |
| * resolves to the new object that the caller want remote ref to |
| * be after update. Use null or |
| * {@link org.eclipse.jgit.lib.ObjectId#zeroId()} string for |
| * delete request. |
| * @param remoteName |
| * full name of a remote ref to update, e.g. "refs/heads/master" |
| * (no wildcard, no short name). |
| * @param forceUpdate |
| * true when caller want remote ref to be updated regardless |
| * whether it is fast-forward update (old object is ancestor of |
| * new object). |
| * @param localName |
| * optional full name of a local stored tracking branch, to |
| * update after push, e.g. "refs/remotes/zawir/dirty" (no |
| * wildcard, no short name); null if no local tracking branch |
| * should be updated. |
| * @param expectedOldObjectId |
| * optional object id that caller is expecting, requiring to be |
| * advertised by remote side before update; update will take |
| * place ONLY if remote side advertise exactly this expected id; |
| * null if caller doesn't care what object id remote side |
| * advertise. Use {@link org.eclipse.jgit.lib.ObjectId#zeroId()} |
| * when expecting no remote ref with this name. |
| * @throws java.io.IOException |
| * when I/O error occurred during creating |
| * {@link org.eclipse.jgit.transport.TrackingRefUpdate} for |
| * local tracking branch or srcRef can't be resolved to any |
| * object. |
| * @throws java.lang.IllegalArgumentException |
| * if some required parameter was null |
| */ |
| public RemoteRefUpdate(final Repository localDb, final String srcRef, |
| final String remoteName, final boolean forceUpdate, |
| final String localName, final ObjectId expectedOldObjectId) |
| throws IOException { |
| this(localDb, srcRef, srcRef != null ? localDb.resolve(srcRef) |
| : ObjectId.zeroId(), remoteName, forceUpdate, localName, |
| expectedOldObjectId); |
| } |
| |
| /** |
| * Construct remote ref update request by providing an update specification. |
| * Object is created with default |
| * {@link org.eclipse.jgit.transport.RemoteRefUpdate.Status#NOT_ATTEMPTED} |
| * status and no message. |
| * |
| * @param localDb |
| * local repository to push from. |
| * @param srcRef |
| * source revision. Use null to delete. |
| * @param remoteName |
| * full name of a remote ref to update, e.g. "refs/heads/master" |
| * (no wildcard, no short name). |
| * @param forceUpdate |
| * true when caller want remote ref to be updated regardless |
| * whether it is fast-forward update (old object is ancestor of |
| * new object). |
| * @param localName |
| * optional full name of a local stored tracking branch, to |
| * update after push, e.g. "refs/remotes/zawir/dirty" (no |
| * wildcard, no short name); null if no local tracking branch |
| * should be updated. |
| * @param expectedOldObjectId |
| * optional object id that caller is expecting, requiring to be |
| * advertised by remote side before update; update will take |
| * place ONLY if remote side advertise exactly this expected id; |
| * null if caller doesn't care what object id remote side |
| * advertise. Use {@link org.eclipse.jgit.lib.ObjectId#zeroId()} |
| * when expecting no remote ref with this name. |
| * @throws java.io.IOException |
| * when I/O error occurred during creating |
| * {@link org.eclipse.jgit.transport.TrackingRefUpdate} for |
| * local tracking branch or srcRef can't be resolved to any |
| * object. |
| * @throws java.lang.IllegalArgumentException |
| * if some required parameter was null |
| */ |
| public RemoteRefUpdate(final Repository localDb, final Ref srcRef, |
| final String remoteName, final boolean forceUpdate, |
| final String localName, final ObjectId expectedOldObjectId) |
| throws IOException { |
| this(localDb, srcRef != null ? srcRef.getName() : null, |
| srcRef != null ? srcRef.getObjectId() : null, remoteName, |
| forceUpdate, localName, expectedOldObjectId); |
| } |
| |
| /** |
| * Construct remote ref update request by providing an update specification. |
| * Object is created with default |
| * {@link org.eclipse.jgit.transport.RemoteRefUpdate.Status#NOT_ATTEMPTED} |
| * status and no message. |
| * |
| * @param localDb |
| * local repository to push from. |
| * @param srcRef |
| * source revision to label srcId with. If null srcId.name() will |
| * be used instead. |
| * @param srcId |
| * The new object that the caller wants remote ref to be after |
| * update. Use null or |
| * {@link org.eclipse.jgit.lib.ObjectId#zeroId()} for delete |
| * request. |
| * @param remoteName |
| * full name of a remote ref to update, e.g. "refs/heads/master" |
| * (no wildcard, no short name). |
| * @param forceUpdate |
| * true when caller want remote ref to be updated regardless |
| * whether it is fast-forward update (old object is ancestor of |
| * new object). |
| * @param localName |
| * optional full name of a local stored tracking branch, to |
| * update after push, e.g. "refs/remotes/zawir/dirty" (no |
| * wildcard, no short name); null if no local tracking branch |
| * should be updated. |
| * @param expectedOldObjectId |
| * optional object id that caller is expecting, requiring to be |
| * advertised by remote side before update; update will take |
| * place ONLY if remote side advertise exactly this expected id; |
| * null if caller doesn't care what object id remote side |
| * advertise. Use {@link org.eclipse.jgit.lib.ObjectId#zeroId()} |
| * when expecting no remote ref with this name. |
| * @throws java.io.IOException |
| * when I/O error occurred during creating |
| * {@link org.eclipse.jgit.transport.TrackingRefUpdate} for |
| * local tracking branch or srcRef can't be resolved to any |
| * object. |
| * @throws java.lang.IllegalArgumentException |
| * if some required parameter was null |
| */ |
| public RemoteRefUpdate(final Repository localDb, final String srcRef, |
| final ObjectId srcId, final String remoteName, |
| final boolean forceUpdate, final String localName, |
| final ObjectId expectedOldObjectId) throws IOException { |
| if (remoteName == null) |
| throw new IllegalArgumentException(JGitText.get().remoteNameCannotBeNull); |
| if (srcId == null && srcRef != null) |
| throw new IOException(MessageFormat.format( |
| JGitText.get().sourceRefDoesntResolveToAnyObject, srcRef)); |
| |
| if (srcRef != null) |
| this.srcRef = srcRef; |
| else if (srcId != null && !srcId.equals(ObjectId.zeroId())) |
| this.srcRef = srcId.name(); |
| else |
| this.srcRef = null; |
| |
| if (srcId != null) |
| this.newObjectId = srcId; |
| else |
| this.newObjectId = ObjectId.zeroId(); |
| |
| this.remoteName = remoteName; |
| this.forceUpdate = forceUpdate; |
| if (localName != null && localDb != null) { |
| localUpdate = localDb.updateRef(localName); |
| localUpdate.setForceUpdate(true); |
| localUpdate.setRefLogMessage("push", true); //$NON-NLS-1$ |
| localUpdate.setNewObjectId(newObjectId); |
| trackingRefUpdate = new TrackingRefUpdate( |
| true, |
| remoteName, |
| localName, |
| localUpdate.getOldObjectId() != null |
| ? localUpdate.getOldObjectId() |
| : ObjectId.zeroId(), |
| newObjectId); |
| } else |
| trackingRefUpdate = null; |
| this.localDb = localDb; |
| this.expectedOldObjectId = expectedOldObjectId; |
| this.status = Status.NOT_ATTEMPTED; |
| } |
| |
| /** |
| * Create a new instance of this object basing on existing instance for |
| * configuration. State (like {@link #getMessage()}, {@link #getStatus()}) |
| * of base object is not shared. Expected old object id is set up from |
| * scratch, as this constructor may be used for 2-stage push: first one |
| * being dry run, second one being actual push. |
| * |
| * @param base |
| * configuration base. |
| * @param newExpectedOldObjectId |
| * new expected object id value. |
| * @throws java.io.IOException |
| * when I/O error occurred during creating |
| * {@link org.eclipse.jgit.transport.TrackingRefUpdate} for |
| * local tracking branch or srcRef of base object no longer can |
| * be resolved to any object. |
| */ |
| public RemoteRefUpdate(final RemoteRefUpdate base, |
| final ObjectId newExpectedOldObjectId) throws IOException { |
| this(base.localDb, base.srcRef, base.remoteName, base.forceUpdate, |
| (base.trackingRefUpdate == null ? null : base.trackingRefUpdate |
| .getLocalName()), newExpectedOldObjectId); |
| } |
| |
| /** |
| * Get expected old object id |
| * |
| * @return expectedOldObjectId required to be advertised by remote side, as |
| * set in constructor; may be null. |
| */ |
| public ObjectId getExpectedOldObjectId() { |
| return expectedOldObjectId; |
| } |
| |
| /** |
| * Whether some object is required to be advertised by remote side, as set |
| * in constructor |
| * |
| * @return true if some object is required to be advertised by remote side, |
| * as set in constructor; false otherwise. |
| */ |
| public boolean isExpectingOldObjectId() { |
| return expectedOldObjectId != null; |
| } |
| |
| /** |
| * Get new object id |
| * |
| * @return newObjectId for remote ref, as set in constructor. |
| */ |
| public ObjectId getNewObjectId() { |
| return newObjectId; |
| } |
| |
| /** |
| * Whether this update is a deleting update |
| * |
| * @return true if this update is deleting update; false otherwise. |
| */ |
| public boolean isDelete() { |
| return ObjectId.zeroId().equals(newObjectId); |
| } |
| |
| /** |
| * Get name of remote ref to update |
| * |
| * @return name of remote ref to update, as set in constructor. |
| */ |
| public String getRemoteName() { |
| return remoteName; |
| } |
| |
| /** |
| * Get tracking branch update if localName was set in constructor. |
| * |
| * @return local tracking branch update if localName was set in constructor. |
| */ |
| public TrackingRefUpdate getTrackingRefUpdate() { |
| return trackingRefUpdate; |
| } |
| |
| /** |
| * Get source revision as specified by user (in constructor) |
| * |
| * @return source revision as specified by user (in constructor), could be |
| * any string parseable by |
| * {@link org.eclipse.jgit.lib.Repository#resolve(String)}; can be |
| * null if specified that way in constructor - this stands for |
| * delete request. |
| */ |
| public String getSrcRef() { |
| return srcRef; |
| } |
| |
| /** |
| * Whether user specified a local tracking branch for remote update |
| * |
| * @return true if user specified a local tracking branch for remote update; |
| * false otherwise. |
| */ |
| public boolean hasTrackingRefUpdate() { |
| return trackingRefUpdate != null; |
| } |
| |
| /** |
| * Whether this update is forced regardless of old remote ref object |
| * |
| * @return true if this update is forced regardless of old remote ref |
| * object; false otherwise. |
| */ |
| public boolean isForceUpdate() { |
| return forceUpdate; |
| } |
| |
| /** |
| * Get status of remote ref update operation. |
| * |
| * @return status of remote ref update operation. |
| */ |
| public Status getStatus() { |
| return status; |
| } |
| |
| /** |
| * Check whether update was fast-forward. Note that this result is |
| * meaningful only after successful update (when status is |
| * {@link org.eclipse.jgit.transport.RemoteRefUpdate.Status#OK}). |
| * |
| * @return true if update was fast-forward; false otherwise. |
| */ |
| public boolean isFastForward() { |
| return fastForward; |
| } |
| |
| /** |
| * Get message describing reasons of status when needed/possible; may be |
| * null. |
| * |
| * @return message describing reasons of status when needed/possible; may be |
| * null. |
| */ |
| public String getMessage() { |
| return message; |
| } |
| |
| void setExpectedOldObjectId(ObjectId id) { |
| expectedOldObjectId = id; |
| } |
| |
| void setStatus(Status status) { |
| this.status = status; |
| } |
| |
| void setFastForward(boolean fastForward) { |
| this.fastForward = fastForward; |
| } |
| |
| void setMessage(String message) { |
| this.message = message; |
| } |
| |
| /** |
| * Update locally stored tracking branch with the new object. |
| * |
| * @param walk |
| * walker used for checking update properties. |
| * @throws java.io.IOException |
| * when I/O error occurred during update |
| */ |
| protected void updateTrackingRef(RevWalk walk) throws IOException { |
| if (isDelete()) |
| trackingRefUpdate.setResult(localUpdate.delete(walk)); |
| else |
| trackingRefUpdate.setResult(localUpdate.update(walk)); |
| } |
| |
| /** {@inheritDoc} */ |
| @SuppressWarnings("nls") |
| @Override |
| public String toString() { |
| return "RemoteRefUpdate[remoteName=" |
| + remoteName |
| + ", " |
| + status |
| + ", " |
| + (expectedOldObjectId != null ? expectedOldObjectId.name() |
| : "(null)") + "..." |
| + (newObjectId != null ? newObjectId.name() : "(null)") |
| + (fastForward ? ", fastForward" : "") |
| + ", srcRef=" + srcRef |
| + (forceUpdate ? ", forceUpdate" : "") + ", message=" |
| + (message != null ? "\"" + message + "\"" : "null") + "]"; |
| } |
| } |