This class perform analogous operations as FetchProcess. It processes refs advertised by connection, updates RemoteRefUpdates and local tracking branches - TrackingRefUpdates. Signed-off-by: Marek Zawirski <marek.zawirski@xxxxxxxxx> --- .../org/spearce/jgit/transport/PushProcess.java | 224 ++++++++++++++++++++ 1 files changed, 224 insertions(+), 0 deletions(-) create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/PushProcess.java diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/PushProcess.java b/org.spearce.jgit/src/org/spearce/jgit/transport/PushProcess.java new file mode 100644 index 0000000..f742949 --- /dev/null +++ b/org.spearce.jgit/src/org/spearce/jgit/transport/PushProcess.java @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2008, Marek Zawirski <marek.zawirski@xxxxxxxxx> + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Git Development Community nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.spearce.jgit.transport; + +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.spearce.jgit.errors.MissingObjectException; +import org.spearce.jgit.errors.NotSupportedException; +import org.spearce.jgit.errors.TransportException; +import org.spearce.jgit.lib.ObjectId; +import org.spearce.jgit.lib.ProgressMonitor; +import org.spearce.jgit.lib.Ref; +import org.spearce.jgit.revwalk.RevCommit; +import org.spearce.jgit.revwalk.RevObject; +import org.spearce.jgit.revwalk.RevWalk; +import org.spearce.jgit.transport.RemoteRefUpdate.Status; + +/** + * Class performing push operation on remote repository. + * + * @see Transport#push(ProgressMonitor, Collection) + */ +class PushProcess { + /** Task name for {@link ProgressMonitor} used during opening connection. */ + static final String PROGRESS_OPENING_CONNECTION = "Opening connection"; + + /** Transport used to perform this operation. */ + private final Transport transport; + + /** Push operation connection created to perform this operation */ + private PushConnection connection; + + /** Refs to update on remote side. */ + private final Map<String, RemoteRefUpdate> toPush; + + /** Revision walker for checking some updates properties. */ + private final RevWalk walker; + + /** + * Create process for specified transport and refs updates specification. + * + * @param transport + * transport between remote and local repository, used to create + * connection. + * @param toPush + * specification of refs updates (and local tracking branches). + * @throws TransportException + */ + PushProcess(final Transport transport, + final Collection<RemoteRefUpdate> toPush) throws TransportException { + this.walker = new RevWalk(transport.local); + this.transport = transport; + this.toPush = new HashMap<String, RemoteRefUpdate>(); + for (final RemoteRefUpdate rru : toPush) { + if (this.toPush.put(rru.getRemoteName(), rru) != null) + throw new TransportException( + "Duplicate remote ref update is illegal. Affected remote name: " + + rru.getRemoteName()); + } + } + + /** + * Perform push operation between local and remote repository - set remote + * refs appropriately, send needed objects and update local tracking refs. + * + * @param monitor + * progress monitor used for feedback about operation. + * @return result of push operation with complete status description. + * @throws NotSupportedException + * when push operation is not supported by provided transport. + * @throws TransportException + * when some error occurred during operation, like I/O, protocol + * error, or local database consistency error. + */ + PushResult execute(final ProgressMonitor monitor) + throws NotSupportedException, TransportException { + monitor.beginTask(PROGRESS_OPENING_CONNECTION, ProgressMonitor.UNKNOWN); + connection = transport.openPush(); + try { + monitor.endTask(); + + final Map<String, RemoteRefUpdate> preprocessed = prepareRemoteUpdates(); + if (!preprocessed.isEmpty()) + connection.push(monitor, preprocessed); + } finally { + connection.close(); + } + updateTrackingRefs(); + return prepareOperationResult(); + } + + private Map<String, RemoteRefUpdate> prepareRemoteUpdates() + throws TransportException { + final Map<String, RemoteRefUpdate> result = new HashMap<String, RemoteRefUpdate>(); + for (final RemoteRefUpdate rru : toPush.values()) { + final Ref advertisedRef = connection.getRef(rru.getRemoteName()); + final ObjectId advertisedOld = (advertisedRef == null ? ObjectId + .zeroId() : advertisedRef.getObjectId()); + + if (rru.getNewObjectId().equals(advertisedOld)) { + if (rru.isDelete()) { + // ref does exist neither locally nor remotely + rru.setStatus(Status.NON_EXISTING); + } else { + // same object - nothing to do + rru.setStatus(Status.UP_TO_DATE); + } + continue; + } + + // caller has explicitly specified expected old object id, while it + // has been changed in the mean time - reject + if (rru.isExpectingOldObjectId() + && !rru.getExpectedOldObjectId().equals(advertisedOld)) { + rru.setStatus(Status.REJECTED_REMOTE_CHANGED); + continue; + } + + // create ref (hasn't existed on remote side) and delete ref + // are always fast-forward commands, feasible at this level + if (advertisedOld.equals(ObjectId.zeroId()) || rru.isDelete()) { + rru.setFastForward(true); + result.put(rru.getRemoteName(), rru); + continue; + } + + // check for fast-forward: + // - both old and new ref must point to commits, AND + // - both of them must be known for us, exist in repository, AND + // - old commit must be ancestor of new commit + boolean fastForward = true; + try { + RevObject oldRev = walker.parseAny(advertisedOld); + final RevObject newRev = walker.parseAny(rru.getNewObjectId()); + if (!(oldRev instanceof RevCommit) + || !(newRev instanceof RevCommit) + || !walker.isMergedInto((RevCommit) oldRev, + (RevCommit) newRev)) + fastForward = false; + } catch (MissingObjectException x) { + fastForward = false; + } catch (Exception x) { + throw new TransportException(transport.getURI() + + ": reading objects from local repository failed: " + + x.getMessage(), x); + } + rru.setFastForward(fastForward); + if (!fastForward && !rru.isForceUpdate()) + rru.setStatus(Status.REJECTED_NONFASTFORWARD); + else + result.put(rru.getRemoteName(), rru); + } + return result; + } + + private void updateTrackingRefs() { + for (final RemoteRefUpdate rru : toPush.values()) { + final Status status = rru.getStatus(); + if (rru.hasTrackingRefUpdate() + && (status == Status.UP_TO_DATE || status == Status.OK)) { + // update local tracking branch only when there is a chance that + // it has changed; this is possible for: + // -updated (OK) status, + // -up to date (UP_TO_DATE) status + try { + rru.updateTrackingRef(walker); + } catch (IOException e) { + // ignore as RefUpdate has stored I/O error status + } + } + } + } + + private PushResult prepareOperationResult() { + final PushResult result = new PushResult(); + result.setAdvertisedRefs(connection.getRefsMap()); + result.setRemoteUpdates(toPush); + + for (final RemoteRefUpdate rru : toPush.values()) { + final TrackingRefUpdate tru = rru.getTrackingRefUpdate(); + if (tru != null) + result.add(tru); + } + return result; + } +} -- 1.5.5.3 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html