Browse Source

Merge changes Iee9af8d5,I8e1674f0,If5a6fcc5,I3bb28e4d

* changes:
  Use BatchRefUpdate for tracking refs in FetchProcess
  Batch reference updates together for storage
  Expose ReceiveCommand.updateType to check for UPDATE_NONFASTFORWARD
  Reject non-fast-forwards earlier in BaseReceivePack
stable-2.1
Shawn O. Pearce 13 years ago committed by Gerrit Code Review @ Eclipse.org
parent
commit
73cdb08af6
  1. 3
      org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/TransportTest.java
  2. 317
      org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchRefUpdate.java
  3. 11
      org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
  4. 41
      org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java
  5. 149
      org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java
  6. 57
      org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java
  7. 25
      org.eclipse.jgit/src/org/eclipse/jgit/transport/RemoteRefUpdate.java
  8. 115
      org.eclipse.jgit/src/org/eclipse/jgit/transport/TrackingRefUpdate.java

3
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

317
org.eclipse.jgit/src/org/eclipse/jgit/lib/BatchRefUpdate.java

@ -0,0 +1,317 @@
/*
* Copyright (C) 2008-2012, Google Inc.
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
* accompanies this distribution, is reproduced below, and is
* available at http://www.eclipse.org/org/documents/edl-v10.php
*
* 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 Eclipse Foundation, Inc. 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.eclipse.jgit.lib;
import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.ReceiveCommand;
/**
* Batch of reference updates to be applied to a repository.
* <p>
* The batch update is primarily useful in the transport code, where a client or
* server is making changes to more than one reference at a time.
*/
public class BatchRefUpdate {
private final RefDatabase refdb;
/** Commands to apply during this batch. */
private final List<ReceiveCommand> commands;
/** Does the caller permit a forced update on a reference? */
private boolean allowNonFastForwards;
/** Identity to record action as within the reflog. */
private PersonIdent refLogIdent;
/** Message the caller wants included in the reflog. */
private String refLogMessage;
/** Should the result value be appended to {@link #refLogMessage}. */
private boolean refLogIncludeResult;
/**
* Initialize a new batch update.
*
* @param refdb
* the reference database of the repository to be updated.
*/
protected BatchRefUpdate(RefDatabase refdb) {
this.refdb = refdb;
this.commands = new ArrayList<ReceiveCommand>();
}
/**
* @return true if the batch update will permit a non-fast-forward update to
* an existing reference.
*/
public boolean isAllowNonFastForwards() {
return allowNonFastForwards;
}
/**
* Set if this update wants to permit a forced update.
*
* @param allow
* true if this update batch should ignore merge tests.
* @return {@code this}.
*/
public BatchRefUpdate setAllowNonFastForwards(boolean allow) {
allowNonFastForwards = allow;
return this;
}
/** @return identity of the user making the change in the reflog. */
public PersonIdent getRefLogIdent() {
return refLogIdent;
}
/**
* Set the identity of the user appearing in the reflog.
* <p>
* The timestamp portion of the identity is ignored. A new identity with the
* current timestamp will be created automatically when the update occurs
* and the log record is written.
*
* @param pi
* identity of the user. If null the identity will be
* automatically determined based on the repository
* configuration.
* @return {@code this}.
*/
public BatchRefUpdate setRefLogIdent(final PersonIdent pi) {
refLogIdent = pi;
return this;
}
/**
* Get the message to include in the reflog.
*
* @return message the caller wants to include in the reflog; null if the
* update should not be logged.
*/
public String getRefLogMessage() {
return refLogMessage;
}
/** @return {@code true} if the ref log message should show the result. */
public boolean isRefLogIncludingResult() {
return refLogIncludeResult;
}
/**
* Set the message to include in the reflog.
*
* @param msg
* the message to describe this change. It may be null if
* appendStatus is null in order not to append to the reflog
* @param appendStatus
* true if the status of the ref change (fast-forward or
* forced-update) should be appended to the user supplied
* message.
* @return {@code this}.
*/
public BatchRefUpdate setRefLogMessage(String msg, boolean appendStatus) {
if (msg == null && !appendStatus)
disableRefLog();
else if (msg == null && appendStatus) {
refLogMessage = "";
refLogIncludeResult = true;
} else {
refLogMessage = msg;
refLogIncludeResult = appendStatus;
}
return this;
}
/**
* Don't record this update in the ref's associated reflog.
*
* @return {@code this}.
*/
public BatchRefUpdate disableRefLog() {
refLogMessage = null;
refLogIncludeResult = false;
return this;
}
/** @return true if log has been disabled by {@link #disableRefLog()}. */
public boolean isRefLogDisabled() {
return refLogMessage == null;
}
/** @return commands this update will process. */
public List<ReceiveCommand> getCommands() {
return Collections.unmodifiableList(commands);
}
/**
* Add a single command to this batch update.
*
* @param cmd
* the command to add, must not be null.
* @return {@code this}.
*/
public BatchRefUpdate addCommand(ReceiveCommand cmd) {
commands.add(cmd);
return this;
}
/**
* Add commands to this batch update.
*
* @param cmd
* the commands to add, must not be null.
* @return {@code this}.
*/
public BatchRefUpdate addCommand(ReceiveCommand... cmd) {
return addCommand(Arrays.asList(cmd));
}
/**
* Add commands to this batch update.
*
* @param cmd
* the commands to add, must not be null.
* @return {@code this}.
*/
public BatchRefUpdate addCommand(Collection<ReceiveCommand> cmd) {
commands.addAll(cmd);
return this;
}
/**
* Execute this batch update.
* <p>
* The default implementation of this method performs a sequential reference
* update over each reference.
*
* @param walk
* a RevWalk to parse tags in case the storage system wants to
* store them pre-peeled, a common performance optimization.
* @param update
* progress monitor to receive update status on.
* @throws IOException
* the database is unable to accept the update. Individual
* command status must be tested to determine if there is a
* partial failure, or a total failure.
*/
public void execute(RevWalk walk, ProgressMonitor update)
throws IOException {
update.beginTask(JGitText.get().updatingReferences, commands.size());
for (ReceiveCommand cmd : commands) {
try {
update.update(1);
if (cmd.getResult() == NOT_ATTEMPTED) {
cmd.updateType(walk);
RefUpdate ru = newUpdate(cmd);
switch (cmd.getType()) {
case DELETE:
cmd.setResult(ru.delete(walk));
continue;
case CREATE:
case UPDATE:
case UPDATE_NONFASTFORWARD:
cmd.setResult(ru.update(walk));
continue;
}
}
} catch (IOException err) {
cmd.setResult(REJECTED_OTHER_REASON, MessageFormat.format(
JGitText.get().lockError, err.getMessage()));
}
}
update.endTask();
}
/**
* Create a new RefUpdate copying the batch settings.
*
* @param cmd
* specific command the update should be created to copy.
* @return a single reference update command.
* @throws IOException
* the reference database cannot make a new update object for
* the given reference.
*/
protected RefUpdate newUpdate(ReceiveCommand cmd) throws IOException {
RefUpdate ru = refdb.newUpdate(cmd.getRefName(), false);
if (isRefLogDisabled())
ru.disableRefLog();
else {
ru.setRefLogIdent(refLogIdent);
ru.setRefLogMessage(refLogMessage, refLogIncludeResult);
}
switch (cmd.getType()) {
case DELETE:
if (!ObjectId.zeroId().equals(cmd.getOldId()))
ru.setExpectedOldObjectId(cmd.getOldId());
ru.setForceUpdate(true);
return ru;
case CREATE:
case UPDATE:
case UPDATE_NONFASTFORWARD:
default:
ru.setForceUpdate(isAllowNonFastForwards());
ru.setExpectedOldObjectId(cmd.getOldId());
ru.setNewObjectId(cmd.getNewId());
return ru;
}
}
}

11
org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java

@ -146,6 +146,17 @@ public abstract class RefDatabase {
public abstract RefRename newRename(String fromName, String toName)
throws IOException;
/**
* Create a new batch update to attempt on this database.
* <p>
* The default implementation performs a sequential update of each command.
*
* @return a new batch update object.
*/
public BatchRefUpdate newBatchUpdate() {
return new BatchRefUpdate(this);
}
/**
* Read a single reference.
* <p>

41
org.eclipse.jgit/src/org/eclipse/jgit/transport/BaseReceivePack.java

@ -67,6 +67,7 @@ import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.PackProtocolException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Config.SectionParser;
import org.eclipse.jgit.lib.Constants;
@ -1086,11 +1087,11 @@ public abstract class BaseReceivePack {
if (oldObj instanceof RevCommit && newObj instanceof RevCommit) {
try {
if (!walk.isMergedInto((RevCommit) oldObj,
(RevCommit) newObj)) {
cmd
.setType(ReceiveCommand.Type.UPDATE_NONFASTFORWARD);
}
if (walk.isMergedInto((RevCommit) oldObj,
(RevCommit) newObj))
cmd.setTypeFastForwardUpdate();
else
cmd.setType(ReceiveCommand.Type.UPDATE_NONFASTFORWARD);
} catch (MissingObjectException e) {
cmd.setResult(Result.REJECTED_MISSING_OBJECT, e
.getMessage());
@ -1100,6 +1101,12 @@ public abstract class BaseReceivePack {
} else {
cmd.setType(ReceiveCommand.Type.UPDATE_NONFASTFORWARD);
}
if (cmd.getType() == ReceiveCommand.Type.UPDATE_NONFASTFORWARD
&& !isAllowNonFastForwards()) {
cmd.setResult(Result.REJECTED_NONFASTFORWARD);
continue;
}
}
if (!cmd.getRefName().startsWith(Constants.R_REFS)
@ -1123,20 +1130,30 @@ public abstract class BaseReceivePack {
/** Execute commands to update references. */
protected void executeCommands() {
List<ReceiveCommand> toApply = ReceiveCommand.filter(commands,
Result.NOT_ATTEMPTED);
List<ReceiveCommand> toApply = filterCommands(Result.NOT_ATTEMPTED);
if (toApply.isEmpty())
return;
ProgressMonitor updating = NullProgressMonitor.INSTANCE;
if (sideBand) {
SideBandProgressMonitor pm = new SideBandProgressMonitor(msgOut);
pm.setDelayStart(250, TimeUnit.MILLISECONDS);
updating = pm;
}
updating.beginTask(JGitText.get().updatingReferences, toApply.size());
for (ReceiveCommand cmd : toApply) {
updating.update(1);
cmd.execute(this);
BatchRefUpdate batch = db.getRefDatabase().newBatchUpdate();
batch.setAllowNonFastForwards(isAllowNonFastForwards());
batch.setRefLogIdent(getRefLogIdent());
batch.setRefLogMessage("push", true);
batch.addCommand(toApply);
try {
batch.execute(walk, updating);
} catch (IOException err) {
for (ReceiveCommand cmd : toApply) {
if (cmd.getResult() == Result.NOT_ATTEMPTED)
cmd.reject(err);
}
}
updating.endTask();
}
/**

149
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 "";
}
}

57
org.eclipse.jgit/src/org/eclipse/jgit/transport/ReceiveCommand.java

@ -49,9 +49,13 @@ import java.util.ArrayList;
import java.util.List;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk;
/**
* A command being processed by {@link BaseReceivePack}.
@ -157,6 +161,8 @@ public class ReceiveCommand {
private String message;
private boolean typeIsCorrect;
/**
* Create a new command for {@link BaseReceivePack}.
*
@ -264,6 +270,36 @@ public class ReceiveCommand {
message = m;
}
/**
* Update the type of this command by checking for fast-forward.
* <p>
* If the command's current type is UPDATE, a merge test will be performed
* using the supplied RevWalk to determine if {@link #getOldId()} is fully
* merged into {@link #getNewId()}. If some commits are not merged the
* update type is changed to {@link Type#UPDATE_NONFASTFORWARD}.
*
* @param walk
* an instance to perform the merge test with. The caller must
* allocate and release this object.
* @throws IOException
* either oldId or newId is not accessible in the repository
* used by the RevWalk. This usually indicates data corruption,
* and the command cannot be processed.
*/
public void updateType(RevWalk walk) throws IOException {
if (typeIsCorrect)
return;
if (type == Type.UPDATE && !AnyObjectId.equals(oldId, newId)) {
RevObject o = walk.parseAny(oldId);
RevObject n = walk.parseAny(newId);
if (!(o instanceof RevCommit)
|| !(n instanceof RevCommit)
|| !walk.isMergedInto((RevCommit) o, (RevCommit) n))
setType(Type.UPDATE_NONFASTFORWARD);
}
typeIsCorrect = true;
}
/**
* Execute this command during a receive-pack session.
* <p>
@ -301,8 +337,7 @@ public class ReceiveCommand {
break;
}
} catch (IOException err) {
setResult(Result.REJECTED_OTHER_REASON, MessageFormat.format(
JGitText.get().lockError, err.getMessage()));
reject(err);
}
}
@ -314,7 +349,18 @@ public class ReceiveCommand {
type = t;
}
private void setResult(final RefUpdate.Result r) {
void setTypeFastForwardUpdate() {
type = Type.UPDATE;
typeIsCorrect = true;
}
/**
* Set the result of this command.
*
* @param r
* the new result code for this command.
*/
public void setResult(RefUpdate.Result r) {
switch (r) {
case NOT_ATTEMPTED:
setResult(Result.NOT_ATTEMPTED);
@ -346,6 +392,11 @@ public class ReceiveCommand {
}
}
void reject(IOException err) {
setResult(Result.REJECTED_OTHER_REASON, MessageFormat.format(
JGitText.get().lockError, err.getMessage()));
}
@Override
public String toString() {
return getType().name() + ": " + getOldId().name() + " "

25
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

115
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;
}
}
}
}

Loading…
Cancel
Save