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;
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;
private FetchConnection conn;
+ private Map<String, Ref> localRefs;
+
FetchProcess(final Transport t, final Collection<RefSpec> f) {
transport = t;
toFetch = f;
localUpdates.clear();
fetchHeadUpdates.clear();
packLocks.clear();
+ localRefs = null;
try {
executeImp(monitor, result);
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();
}
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 {
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;
}
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;
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);
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);
}
}
}
}
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) {
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 "";
+ }
}
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();
}
/**
* @return the name used within this local repository.
*/
public String getLocalName() {
- return update.getName();
+ return localName;
}
/**
* @return new value. Null if the caller has not configured it.
*/
public ObjectId getNewObjectId() {
- return update.getNewObjectId();
+ return newObjectId;
}
/**
* 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;
}
/**
*
* @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;
+ }
+ }
}
}