[push] Call the pre-push hook later in the push process
Call the pre-push hook only after having received the remote advertisement and having determined rejections, like C git does. Also similar to C git, don't pass rejected or up-to-date updates to the pre-push hook. Bug: 578852 Change-Id: I51d379ea7bd8234ec815f8f4a9fa325816f476cf Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
This commit is contained in:
parent
72ae234e79
commit
90df7c123e
|
@ -14,14 +14,19 @@
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.api.errors.AbortedByHookException;
|
||||||
import org.eclipse.jgit.errors.NotSupportedException;
|
import org.eclipse.jgit.errors.NotSupportedException;
|
||||||
import org.eclipse.jgit.errors.TransportException;
|
import org.eclipse.jgit.errors.TransportException;
|
||||||
|
import org.eclipse.jgit.hooks.PrePushHook;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.ObjectIdRef;
|
import org.eclipse.jgit.lib.ObjectIdRef;
|
||||||
import org.eclipse.jgit.lib.ProgressMonitor;
|
import org.eclipse.jgit.lib.ProgressMonitor;
|
||||||
|
@ -31,6 +36,7 @@
|
||||||
import org.eclipse.jgit.lib.TextProgressMonitor;
|
import org.eclipse.jgit.lib.TextProgressMonitor;
|
||||||
import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
|
import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
|
||||||
import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
|
import org.eclipse.jgit.transport.RemoteRefUpdate.Status;
|
||||||
|
import org.eclipse.jgit.util.io.NullOutputStream;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -220,7 +226,17 @@ public void testUpdateUnexpectedRemoteVsForce() throws IOException {
|
||||||
.fromString("0000000000000000000000000000000000000001"));
|
.fromString("0000000000000000000000000000000000000001"));
|
||||||
final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
|
final Ref ref = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, "refs/heads/master",
|
||||||
ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
|
ObjectId.fromString("ac7e7e44c1885efb472ad54a78327d66bfc4ecef"));
|
||||||
testOneUpdateStatus(rru, ref, Status.REJECTED_REMOTE_CHANGED, null);
|
try (ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
|
PrintStream out = new PrintStream(bytes, true,
|
||||||
|
StandardCharsets.UTF_8);
|
||||||
|
PrintStream err = new PrintStream(NullOutputStream.INSTANCE)) {
|
||||||
|
MockPrePushHook hook = new MockPrePushHook(db, out, err);
|
||||||
|
testOneUpdateStatus(rru, ref, Status.REJECTED_REMOTE_CHANGED, null,
|
||||||
|
hook);
|
||||||
|
out.flush();
|
||||||
|
String result = new String(bytes.toString(StandardCharsets.UTF_8));
|
||||||
|
assertEquals("", result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -256,10 +272,22 @@ public void testUpdateMixedCases() throws IOException {
|
||||||
refUpdates.add(rruOk);
|
refUpdates.add(rruOk);
|
||||||
refUpdates.add(rruReject);
|
refUpdates.add(rruReject);
|
||||||
advertisedRefs.add(refToChange);
|
advertisedRefs.add(refToChange);
|
||||||
executePush();
|
try (ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
|
PrintStream out = new PrintStream(bytes, true,
|
||||||
|
StandardCharsets.UTF_8);
|
||||||
|
PrintStream err = new PrintStream(NullOutputStream.INSTANCE)) {
|
||||||
|
MockPrePushHook hook = new MockPrePushHook(db, out, err);
|
||||||
|
executePush(hook);
|
||||||
assertEquals(Status.OK, rruOk.getStatus());
|
assertEquals(Status.OK, rruOk.getStatus());
|
||||||
assertTrue(rruOk.isFastForward());
|
assertTrue(rruOk.isFastForward());
|
||||||
assertEquals(Status.NON_EXISTING, rruReject.getStatus());
|
assertEquals(Status.NON_EXISTING, rruReject.getStatus());
|
||||||
|
out.flush();
|
||||||
|
String result = new String(bytes.toString(StandardCharsets.UTF_8));
|
||||||
|
assertEquals(
|
||||||
|
"null 0000000000000000000000000000000000000000 "
|
||||||
|
+ "refs/heads/master 2c349335b7f797072cf729c4f3bb0914ecb6dec9\n",
|
||||||
|
result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -346,10 +374,18 @@ private PushResult testOneUpdateStatus(final RemoteRefUpdate rru,
|
||||||
final Ref advertisedRef, final Status expectedStatus,
|
final Ref advertisedRef, final Status expectedStatus,
|
||||||
Boolean fastForward) throws NotSupportedException,
|
Boolean fastForward) throws NotSupportedException,
|
||||||
TransportException {
|
TransportException {
|
||||||
|
return testOneUpdateStatus(rru, advertisedRef, expectedStatus,
|
||||||
|
fastForward, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PushResult testOneUpdateStatus(final RemoteRefUpdate rru,
|
||||||
|
final Ref advertisedRef, final Status expectedStatus,
|
||||||
|
Boolean fastForward, PrePushHook hook)
|
||||||
|
throws NotSupportedException, TransportException {
|
||||||
refUpdates.add(rru);
|
refUpdates.add(rru);
|
||||||
if (advertisedRef != null)
|
if (advertisedRef != null)
|
||||||
advertisedRefs.add(advertisedRef);
|
advertisedRefs.add(advertisedRef);
|
||||||
final PushResult result = executePush();
|
final PushResult result = executePush(hook);
|
||||||
assertEquals(expectedStatus, rru.getStatus());
|
assertEquals(expectedStatus, rru.getStatus());
|
||||||
if (fastForward != null)
|
if (fastForward != null)
|
||||||
assertEquals(fastForward, Boolean.valueOf(rru.isFastForward()));
|
assertEquals(fastForward, Boolean.valueOf(rru.isFastForward()));
|
||||||
|
@ -358,7 +394,12 @@ private PushResult testOneUpdateStatus(final RemoteRefUpdate rru,
|
||||||
|
|
||||||
private PushResult executePush() throws NotSupportedException,
|
private PushResult executePush() throws NotSupportedException,
|
||||||
TransportException {
|
TransportException {
|
||||||
process = new PushProcess(transport, refUpdates);
|
return executePush(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PushResult executePush(PrePushHook hook)
|
||||||
|
throws NotSupportedException, TransportException {
|
||||||
|
process = new PushProcess(transport, refUpdates, hook);
|
||||||
return process.execute(new TextProgressMonitor());
|
return process.execute(new TextProgressMonitor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,4 +457,20 @@ public void push(ProgressMonitor monitor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class MockPrePushHook extends PrePushHook {
|
||||||
|
|
||||||
|
private final PrintStream output;
|
||||||
|
|
||||||
|
public MockPrePushHook(Repository repo, PrintStream out,
|
||||||
|
PrintStream err) {
|
||||||
|
super(repo, out, err);
|
||||||
|
output = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doRun() throws AbortedByHookException, IOException {
|
||||||
|
output.print(getStdinArgs());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,13 @@
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.api.errors.AbortedByHookException;
|
||||||
import org.eclipse.jgit.errors.MissingObjectException;
|
import org.eclipse.jgit.errors.MissingObjectException;
|
||||||
import org.eclipse.jgit.errors.NotSupportedException;
|
import org.eclipse.jgit.errors.NotSupportedException;
|
||||||
import org.eclipse.jgit.errors.TransportException;
|
import org.eclipse.jgit.errors.TransportException;
|
||||||
|
import org.eclipse.jgit.hooks.PrePushHook;
|
||||||
import org.eclipse.jgit.internal.JGitText;
|
import org.eclipse.jgit.internal.JGitText;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.ProgressMonitor;
|
import org.eclipse.jgit.lib.ProgressMonitor;
|
||||||
|
@ -58,6 +61,8 @@ class PushProcess {
|
||||||
/** A list of option strings associated with this push */
|
/** A list of option strings associated with this push */
|
||||||
private List<String> pushOptions;
|
private List<String> pushOptions;
|
||||||
|
|
||||||
|
private final PrePushHook prePush;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create process for specified transport and refs updates specification.
|
* Create process for specified transport and refs updates specification.
|
||||||
*
|
*
|
||||||
|
@ -66,12 +71,15 @@ class PushProcess {
|
||||||
* connection.
|
* connection.
|
||||||
* @param toPush
|
* @param toPush
|
||||||
* specification of refs updates (and local tracking branches).
|
* specification of refs updates (and local tracking branches).
|
||||||
*
|
* @param prePush
|
||||||
|
* {@link PrePushHook} to run after the remote advertisement has
|
||||||
|
* been gotten
|
||||||
* @throws TransportException
|
* @throws TransportException
|
||||||
*/
|
*/
|
||||||
PushProcess(final Transport transport,
|
PushProcess(final Transport transport,
|
||||||
final Collection<RemoteRefUpdate> toPush) throws TransportException {
|
final Collection<RemoteRefUpdate> toPush, PrePushHook prePush)
|
||||||
this(transport, toPush, null);
|
throws TransportException {
|
||||||
|
this(transport, toPush, prePush, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -82,16 +90,20 @@ class PushProcess {
|
||||||
* connection.
|
* connection.
|
||||||
* @param toPush
|
* @param toPush
|
||||||
* specification of refs updates (and local tracking branches).
|
* specification of refs updates (and local tracking branches).
|
||||||
|
* @param prePush
|
||||||
|
* {@link PrePushHook} to run after the remote advertisement has
|
||||||
|
* been gotten
|
||||||
* @param out
|
* @param out
|
||||||
* OutputStream to write messages to
|
* OutputStream to write messages to
|
||||||
* @throws TransportException
|
* @throws TransportException
|
||||||
*/
|
*/
|
||||||
PushProcess(final Transport transport,
|
PushProcess(final Transport transport,
|
||||||
final Collection<RemoteRefUpdate> toPush, OutputStream out)
|
final Collection<RemoteRefUpdate> toPush, PrePushHook prePush,
|
||||||
throws TransportException {
|
OutputStream out) throws TransportException {
|
||||||
this.walker = new RevWalk(transport.local);
|
this.walker = new RevWalk(transport.local);
|
||||||
this.transport = transport;
|
this.transport = transport;
|
||||||
this.toPush = new LinkedHashMap<>();
|
this.toPush = new LinkedHashMap<>();
|
||||||
|
this.prePush = prePush;
|
||||||
this.out = out;
|
this.out = out;
|
||||||
this.pushOptions = transport.getPushOptions();
|
this.pushOptions = transport.getPushOptions();
|
||||||
for (RemoteRefUpdate rru : toPush) {
|
for (RemoteRefUpdate rru : toPush) {
|
||||||
|
@ -133,6 +145,30 @@ PushResult execute(ProgressMonitor monitor)
|
||||||
monitor.endTask();
|
monitor.endTask();
|
||||||
|
|
||||||
final Map<String, RemoteRefUpdate> preprocessed = prepareRemoteUpdates();
|
final Map<String, RemoteRefUpdate> preprocessed = prepareRemoteUpdates();
|
||||||
|
List<RemoteRefUpdate> willBeAttempted = preprocessed.values()
|
||||||
|
.stream().filter(u -> {
|
||||||
|
switch (u.getStatus()) {
|
||||||
|
case NON_EXISTING:
|
||||||
|
case REJECTED_NODELETE:
|
||||||
|
case REJECTED_NONFASTFORWARD:
|
||||||
|
case REJECTED_OTHER_REASON:
|
||||||
|
case REJECTED_REMOTE_CHANGED:
|
||||||
|
case UP_TO_DATE:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
if (!willBeAttempted.isEmpty()) {
|
||||||
|
if (prePush != null) {
|
||||||
|
try {
|
||||||
|
prePush.setRefs(willBeAttempted);
|
||||||
|
prePush.call();
|
||||||
|
} catch (AbortedByHookException | IOException e) {
|
||||||
|
throw new TransportException(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (transport.isDryRun())
|
if (transport.isDryRun())
|
||||||
modifyUpdatesForDryRun();
|
modifyUpdatesForDryRun();
|
||||||
else if (!preprocessed.isEmpty())
|
else if (!preprocessed.isEmpty())
|
||||||
|
|
|
@ -40,7 +40,6 @@
|
||||||
|
|
||||||
import org.eclipse.jgit.annotations.NonNull;
|
import org.eclipse.jgit.annotations.NonNull;
|
||||||
import org.eclipse.jgit.annotations.Nullable;
|
import org.eclipse.jgit.annotations.Nullable;
|
||||||
import org.eclipse.jgit.api.errors.AbortedByHookException;
|
|
||||||
import org.eclipse.jgit.errors.NotSupportedException;
|
import org.eclipse.jgit.errors.NotSupportedException;
|
||||||
import org.eclipse.jgit.errors.TransportException;
|
import org.eclipse.jgit.errors.TransportException;
|
||||||
import org.eclipse.jgit.hooks.Hooks;
|
import org.eclipse.jgit.hooks.Hooks;
|
||||||
|
@ -1375,16 +1374,9 @@ public PushResult push(final ProgressMonitor monitor,
|
||||||
if (toPush.isEmpty())
|
if (toPush.isEmpty())
|
||||||
throw new TransportException(JGitText.get().nothingToPush);
|
throw new TransportException(JGitText.get().nothingToPush);
|
||||||
}
|
}
|
||||||
if (prePush != null) {
|
|
||||||
try {
|
|
||||||
prePush.setRefs(toPush);
|
|
||||||
prePush.call();
|
|
||||||
} catch (AbortedByHookException | IOException e) {
|
|
||||||
throw new TransportException(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final PushProcess pushProcess = new PushProcess(this, toPush, out);
|
final PushProcess pushProcess = new PushProcess(this, toPush, prePush,
|
||||||
|
out);
|
||||||
return pushProcess.execute(monitor);
|
return pushProcess.execute(monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue