diff options
4 files changed, 195 insertions, 97 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java index 07ef39dc79..9899d14d8d 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java @@ -56,6 +56,7 @@ import java.util.Collections; import java.util.List; import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.SampleDataRepositoryTestCase; import org.junit.After; import org.junit.Before; @@ -209,7 +210,7 @@ public class TransportTest extends SampleDataRepositoryTestCase { assertEquals("refs/remotes/test/a", tru.getLocalName()); assertEquals("refs/heads/a", tru.getRemoteName()); assertEquals(db.resolve("refs/heads/a"), tru.getNewObjectId()); - assertNull(tru.getOldObjectId()); + assertEquals(ObjectId.zeroId(), tru.getOldObjectId()); } @Test diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java index 27abf789dd..66d0c62dd9 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java @@ -44,6 +44,11 @@ package org.eclipse.jgit.transport; +import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED; +import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK; +import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_NONFASTFORWARD; +import static org.eclipse.jgit.transport.ReceiveCommand.Type.UPDATE_NONFASTFORWARD; + import java.io.File; import java.io.IOException; import java.io.OutputStreamWriter; @@ -63,12 +68,13 @@ import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.NotSupportedException; import org.eclipse.jgit.errors.TransportException; import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.lib.BatchRefUpdate; import org.eclipse.jgit.lib.BatchingProgressMonitor; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.Ref; -import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.RefDatabase; import org.eclipse.jgit.revwalk.ObjectWalk; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.storage.file.LockFile; @@ -97,6 +103,8 @@ class FetchProcess { private FetchConnection conn; + private Map<String, Ref> localRefs; + FetchProcess(final Transport t, final Collection<RefSpec> f) { transport = t; toFetch = f; @@ -108,6 +116,7 @@ class FetchProcess { localUpdates.clear(); fetchHeadUpdates.clear(); packLocks.clear(); + localRefs = null; try { executeImp(monitor, result); @@ -183,27 +192,40 @@ class FetchProcess { closeConnection(result); } + BatchRefUpdate batch = transport.local.getRefDatabase() + .newBatchUpdate() + .setAllowNonFastForwards(true) + .setRefLogMessage("fetch", true); final RevWalk walk = new RevWalk(transport.local); try { if (monitor instanceof BatchingProgressMonitor) { ((BatchingProgressMonitor) monitor).setDelayStart( 250, TimeUnit.MILLISECONDS); } - monitor.beginTask(JGitText.get().updatingReferences, localUpdates.size()); if (transport.isRemoveDeletedRefs()) - deleteStaleTrackingRefs(result, walk); + deleteStaleTrackingRefs(result, batch); for (TrackingRefUpdate u : localUpdates) { - try { - monitor.update(1); - u.update(walk); - result.add(u); - } catch (IOException err) { - throw new TransportException(MessageFormat.format(JGitText - .get().failureUpdatingTrackingRef, - u.getLocalName(), err.getMessage()), err); - } + result.add(u); + batch.addCommand(u.asReceiveCommand()); } - monitor.endTask(); + for (ReceiveCommand cmd : batch.getCommands()) { + cmd.updateType(walk); + if (cmd.getType() == UPDATE_NONFASTFORWARD + && cmd instanceof TrackingRefUpdate.Command + && !((TrackingRefUpdate.Command) cmd).canForceUpdate()) + cmd.setResult(REJECTED_NONFASTFORWARD); + } + if (transport.isDryRun()) { + for (ReceiveCommand cmd : batch.getCommands()) { + if (cmd.getResult() == NOT_ATTEMPTED) + cmd.setResult(OK); + } + } else + batch.execute(walk, monitor); + } catch (IOException err) { + throw new TransportException(MessageFormat.format( + JGitText.get().failureUpdatingTrackingRef, + getFirstFailedRefName(batch), err.getMessage()), err); } finally { walk.release(); } @@ -320,7 +342,7 @@ class FetchProcess { try { for (final ObjectId want : askFor.keySet()) ow.markStart(ow.parseAny(want)); - for (final Ref ref : transport.local.getAllRefs().values()) + for (final Ref ref : localRefs().values()) ow.markUninteresting(ow.parseAny(ref.getObjectId())); ow.checkConnectivity(); } finally { @@ -354,7 +376,7 @@ class FetchProcess { private Collection<Ref> expandAutoFollowTags() throws TransportException { final Collection<Ref> additionalTags = new ArrayList<Ref>(); - final Map<String, Ref> haveRefs = transport.local.getAllRefs(); + final Map<String, Ref> haveRefs = localRefs(); for (final Ref r : conn.getRefs()) { if (!isTag(r)) continue; @@ -385,7 +407,7 @@ class FetchProcess { } private void expandFetchTags() throws TransportException { - final Map<String, Ref> haveRefs = transport.local.getAllRefs(); + final Map<String, Ref> haveRefs = localRefs(); for (final Ref r : conn.getRefs()) { if (!isTag(r)) continue; @@ -404,17 +426,10 @@ class FetchProcess { throws TransportException { final ObjectId newId = src.getObjectId(); if (spec.getDestination() != null) { - try { - final TrackingRefUpdate tru = createUpdate(spec, newId); - if (newId.equals(tru.getOldObjectId())) - return; - localUpdates.add(tru); - } catch (IOException err) { - // Bad symbolic ref? That is the most likely cause. - // - throw new TransportException( MessageFormat.format( - JGitText.get().cannotResolveLocalTrackingRefForUpdating, spec.getDestination()), err); - } + final TrackingRefUpdate tru = createUpdate(spec, newId); + if (newId.equals(tru.getOldObjectId())) + return; + localUpdates.add(tru); } askFor.put(newId, src); @@ -427,21 +442,41 @@ class FetchProcess { fetchHeadUpdates.add(fhr); } - private TrackingRefUpdate createUpdate(final RefSpec spec, - final ObjectId newId) throws IOException { - return new TrackingRefUpdate(transport.local, spec, newId, "fetch"); + private TrackingRefUpdate createUpdate(RefSpec spec, ObjectId newId) + throws TransportException { + Ref ref = localRefs().get(spec.getDestination()); + ObjectId oldId = ref != null && ref.getObjectId() != null + ? ref.getObjectId() + : ObjectId.zeroId(); + return new TrackingRefUpdate( + spec.isForceUpdate(), + spec.getSource(), + spec.getDestination(), + oldId, + newId); + } + + private Map<String, Ref> localRefs() throws TransportException { + if (localRefs == null) { + try { + localRefs = transport.local.getRefDatabase() + .getRefs(RefDatabase.ALL); + } catch (IOException err) { + throw new TransportException(JGitText.get().cannotListRefs, err); + } + } + return localRefs; } - private void deleteStaleTrackingRefs(final FetchResult result, - final RevWalk walk) throws TransportException { - final Repository db = transport.local; - for (final Ref ref : db.getAllRefs().values()) { + private void deleteStaleTrackingRefs(FetchResult result, + BatchRefUpdate batch) throws IOException { + for (final Ref ref : localRefs().values()) { final String refname = ref.getName(); for (final RefSpec spec : toFetch) { if (spec.matchDestination(refname)) { final RefSpec s = spec.expandFromDestination(refname); if (result.getAdvertisedRef(s.getSource()) == null) { - deleteTrackingRef(result, db, walk, s, ref); + deleteTrackingRef(result, batch, s, ref); } } } @@ -449,31 +484,17 @@ class FetchProcess { } private void deleteTrackingRef(final FetchResult result, - final Repository db, final RevWalk walk, final RefSpec spec, - final Ref localRef) throws TransportException { - final String name = localRef.getName(); - try { - final TrackingRefUpdate u = new TrackingRefUpdate(db, name, spec - .getSource(), true, ObjectId.zeroId(), "deleted"); - result.add(u); - if (transport.isDryRun()){ - return; - } - u.delete(walk); - switch (u.getResult()) { - case NEW: - case NO_CHANGE: - case FAST_FORWARD: - case FORCED: - break; - default: - throw new TransportException(transport.getURI(), MessageFormat.format( - JGitText.get().cannotDeleteStaleTrackingRef2, name, u.getResult().name())); - } - } catch (IOException e) { - throw new TransportException(transport.getURI(), MessageFormat.format( - JGitText.get().cannotDeleteStaleTrackingRef, name), e); - } + final BatchRefUpdate batch, final RefSpec spec, final Ref localRef) { + if (localRef.getObjectId() == null) + return; + TrackingRefUpdate update = new TrackingRefUpdate( + true, + spec.getSource(), + localRef.getName(), + localRef.getObjectId(), + ObjectId.zeroId()); + result.add(update); + batch.addCommand(update.asReceiveCommand()); } private static boolean isTag(final Ref r) { @@ -483,4 +504,12 @@ class FetchProcess { private static boolean isTag(final String name) { return name.startsWith(Constants.R_TAGS); } + + private static String getFirstFailedRefName(BatchRefUpdate batch) { + for (ReceiveCommand cmd : batch.getCommands()) { + if (cmd.getResult() != ReceiveCommand.Result.OK) + return cmd.getRefName(); + } + return ""; + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java index 3aaf7bb4ef..421830202e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java @@ -49,6 +49,7 @@ 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; @@ -144,6 +145,8 @@ public class RemoteRefUpdate { private final Repository localDb; + private RefUpdate localUpdate; + /** * Construct remote ref update request by providing an update specification. * Object is created with default {@link Status#NOT_ATTEMPTED} status and no @@ -299,10 +302,20 @@ public class RemoteRefUpdate { this.remoteName = remoteName; this.forceUpdate = forceUpdate; - if (localName != null && localDb != null) - trackingRefUpdate = new TrackingRefUpdate(localDb, localName, - remoteName, true, newObjectId, "push"); - else + if (localName != null && localDb != null) { + localUpdate = localDb.updateRef(localName); + localUpdate.setForceUpdate(true); + localUpdate.setRefLogMessage("push", true); + 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; @@ -449,9 +462,9 @@ public class RemoteRefUpdate { */ protected void updateTrackingRef(final RevWalk walk) throws IOException { if (isDelete()) - trackingRefUpdate.delete(walk); + trackingRefUpdate.setResult(localUpdate.delete(walk)); else - trackingRefUpdate.update(walk); + trackingRefUpdate.setResult(localUpdate.update(walk)); } @Override diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TrackingRefUpdate.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TrackingRefUpdate.java index 03ecd81e0c..3344c3f6a5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TrackingRefUpdate.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TrackingRefUpdate.java @@ -45,35 +45,31 @@ package org.eclipse.jgit.transport; -import java.io.IOException; - import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.RefUpdate; -import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.lib.RefUpdate.Result; -import org.eclipse.jgit.revwalk.RevWalk; /** Update of a locally stored tracking branch. */ public class TrackingRefUpdate { private final String remoteName; + private final String localName; + private boolean forceUpdate; + private ObjectId oldObjectId; + private ObjectId newObjectId; - private final RefUpdate update; - - TrackingRefUpdate(final Repository db, final RefSpec spec, - final AnyObjectId nv, final String msg) throws IOException { - this(db, spec.getDestination(), spec.getSource(), spec.isForceUpdate(), - nv, msg); - } + private RefUpdate.Result result; - TrackingRefUpdate(final Repository db, final String localName, - final String remoteName, final boolean forceUpdate, - final AnyObjectId nv, final String msg) throws IOException { + TrackingRefUpdate( + boolean canForceUpdate, + String remoteName, + String localName, + AnyObjectId oldValue, + AnyObjectId newValue) { this.remoteName = remoteName; - update = db.updateRef(localName); - update.setForceUpdate(forceUpdate); - update.setNewObjectId(nv); - update.setRefLogMessage(msg, true); + this.localName = localName; + this.forceUpdate = canForceUpdate; + this.oldObjectId = oldValue.copy(); + this.newObjectId = newValue.copy(); } /** @@ -95,7 +91,7 @@ public class TrackingRefUpdate { * @return the name used within this local repository. */ public String getLocalName() { - return update.getName(); + return localName; } /** @@ -104,7 +100,7 @@ public class TrackingRefUpdate { * @return new value. Null if the caller has not configured it. */ public ObjectId getNewObjectId() { - return update.getNewObjectId(); + return newObjectId; } /** @@ -115,11 +111,10 @@ public class TrackingRefUpdate { * value may change if someone else modified the ref between the time we * last read it and when the ref was locked for update. * - * @return the value of the ref prior to the update being attempted; null if - * the updated has not been attempted yet. + * @return the value of the ref prior to the update being attempted. */ public ObjectId getOldObjectId() { - return update.getOldObjectId(); + return oldObjectId; } /** @@ -127,15 +122,75 @@ public class TrackingRefUpdate { * * @return the status of the update. */ - public Result getResult() { - return update.getResult(); + public RefUpdate.Result getResult() { + return result; } - void update(final RevWalk walk) throws IOException { - update.update(walk); + void setResult(RefUpdate.Result result) { + this.result = result; } - void delete(final RevWalk walk) throws IOException { - update.delete(walk); + ReceiveCommand asReceiveCommand() { + return new Command(); + } + + final class Command extends ReceiveCommand { + private Command() { + super(oldObjectId, newObjectId, localName); + } + + boolean canForceUpdate() { + return forceUpdate; + } + + @Override + public void setResult(RefUpdate.Result status) { + result = status; + super.setResult(status); + } + + @Override + public void setResult(ReceiveCommand.Result status) { + result = decode(status); + super.setResult(status); + } + + @Override + public void setResult(ReceiveCommand.Result status, String msg) { + result = decode(status); + super.setResult(status, msg); + } + + private RefUpdate.Result decode(ReceiveCommand.Result status) { + switch (status) { + case OK: + if (AnyObjectId.equals(oldObjectId, newObjectId)) + return RefUpdate.Result.NO_CHANGE; + switch (getType()) { + case CREATE: + return RefUpdate.Result.NEW; + case UPDATE: + return RefUpdate.Result.FAST_FORWARD; + case DELETE: + case UPDATE_NONFASTFORWARD: + default: + return RefUpdate.Result.FORCED; + } + + case REJECTED_NOCREATE: + case REJECTED_NODELETE: + case REJECTED_NONFASTFORWARD: + return RefUpdate.Result.REJECTED; + case REJECTED_CURRENT_BRANCH: + return RefUpdate.Result.REJECTED_CURRENT_BRANCH; + case REJECTED_MISSING_OBJECT: + return RefUpdate.Result.IO_FAILURE; + case LOCK_FAILURE: + case NOT_ATTEMPTED: + case REJECTED_OTHER_REASON: + default: + return RefUpdate.Result.LOCK_FAILURE; + } + } } } |