aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit/src/org/eclipse/jgit/transport
diff options
context:
space:
mode:
authorShawn O. Pearce <spearce@spearce.org>2012-05-22 20:13:11 -0700
committerShawn O. Pearce <spearce@spearce.org>2012-05-22 20:39:59 -0700
commit3da13473f7a8baaac6ef1cf965062249c1de141f (patch)
tree15252a0bb4edc62c049417b61596afd767f2987d /org.eclipse.jgit/src/org/eclipse/jgit/transport
parent17be66acdbe662b42a263be77c435945902df968 (diff)
downloadjgit-3da13473f7a8baaac6ef1cf965062249c1de141f.tar.gz
jgit-3da13473f7a8baaac6ef1cf965062249c1de141f.zip
Use BatchRefUpdate for tracking refs in FetchProcess
If there are a lot of references to modify, using BatchRefUpdate can save time if the underlying storage is able to combine these updates together. This should speed up initial clone or fetch into an empty repository, as some projects can have hundreds of release tags, or hundreds of branch heads. Change-Id: Iee9af8d5fa19080077d88357c18853540936e940
Diffstat (limited to 'org.eclipse.jgit/src/org/eclipse/jgit/transport')
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java149
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java25
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/TrackingRefUpdate.java115
3 files changed, 193 insertions, 96 deletions
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;
+ }
+ }
}
}