Add support for pre-push hooks
When the file <git-dir>/hooks/pre-push exists make sure that is is executing during a push. The pre-push hook runs during git push, after the remote refs have been updated but before any objects have been transferred. Change-Id: Ibbb58ee3227742d1a2f913134ce11e7a135c7f4c
This commit is contained in:
parent
67a77d402a
commit
edc4daf2e8
|
@ -47,6 +47,7 @@
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
@ -55,7 +56,10 @@
|
||||||
import org.eclipse.jgit.api.errors.JGitInternalException;
|
import org.eclipse.jgit.api.errors.JGitInternalException;
|
||||||
import org.eclipse.jgit.api.errors.TransportException;
|
import org.eclipse.jgit.api.errors.TransportException;
|
||||||
import org.eclipse.jgit.errors.MissingObjectException;
|
import org.eclipse.jgit.errors.MissingObjectException;
|
||||||
|
import org.eclipse.jgit.hooks.PrePushHook;
|
||||||
|
import org.eclipse.jgit.junit.JGitTestUtil;
|
||||||
import org.eclipse.jgit.junit.RepositoryTestCase;
|
import org.eclipse.jgit.junit.RepositoryTestCase;
|
||||||
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.Ref;
|
import org.eclipse.jgit.lib.Ref;
|
||||||
import org.eclipse.jgit.lib.RefUpdate;
|
import org.eclipse.jgit.lib.RefUpdate;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
@ -66,6 +70,7 @@
|
||||||
import org.eclipse.jgit.transport.RemoteConfig;
|
import org.eclipse.jgit.transport.RemoteConfig;
|
||||||
import org.eclipse.jgit.transport.TrackingRefUpdate;
|
import org.eclipse.jgit.transport.TrackingRefUpdate;
|
||||||
import org.eclipse.jgit.transport.URIish;
|
import org.eclipse.jgit.transport.URIish;
|
||||||
|
import org.eclipse.jgit.util.FS;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class PushCommandTest extends RepositoryTestCase {
|
public class PushCommandTest extends RepositoryTestCase {
|
||||||
|
@ -107,6 +112,48 @@ public void testPush() throws JGitInternalException, IOException,
|
||||||
db2.resolve(tagRef.getObjectId().getName()));
|
db2.resolve(tagRef.getObjectId().getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPrePushHook() throws JGitInternalException, IOException,
|
||||||
|
GitAPIException, URISyntaxException {
|
||||||
|
|
||||||
|
// create other repository
|
||||||
|
Repository db2 = createWorkRepository();
|
||||||
|
|
||||||
|
// setup the first repository
|
||||||
|
final StoredConfig config = db.getConfig();
|
||||||
|
RemoteConfig remoteConfig = new RemoteConfig(config, "test");
|
||||||
|
URIish uri = new URIish(db2.getDirectory().toURI().toURL());
|
||||||
|
remoteConfig.addURI(uri);
|
||||||
|
remoteConfig.update(config);
|
||||||
|
config.save();
|
||||||
|
|
||||||
|
File hookOutput = new File(getTemporaryDirectory(), "hookOutput");
|
||||||
|
writeHookFile(PrePushHook.NAME, "#!/bin/sh\necho 1:$1, 2:$2, 3:$3 >\""
|
||||||
|
+ hookOutput.toPath() + "\"\ncat - >>\"" + hookOutput.toPath()
|
||||||
|
+ "\"\nexit 0");
|
||||||
|
|
||||||
|
Git git1 = new Git(db);
|
||||||
|
// create some refs via commits and tag
|
||||||
|
RevCommit commit = git1.commit().setMessage("initial commit").call();
|
||||||
|
|
||||||
|
RefSpec spec = new RefSpec("refs/heads/master:refs/heads/x");
|
||||||
|
git1.push().setRemote("test").setRefSpecs(spec).call();
|
||||||
|
assertEquals(
|
||||||
|
"1:test, 2:file://" + db2.getDirectory().toPath() //
|
||||||
|
+ "/, 3:\n" + "refs/heads/master " + commit.getName()
|
||||||
|
+ " refs/heads/x " + ObjectId.zeroId().name(),
|
||||||
|
read(hookOutput));
|
||||||
|
}
|
||||||
|
|
||||||
|
private File writeHookFile(final String name, final String data)
|
||||||
|
throws IOException {
|
||||||
|
File path = new File(db.getWorkTree() + "/.git/hooks/", name);
|
||||||
|
JGitTestUtil.write(path, data);
|
||||||
|
FS.DETECTED.setExecute(path, true);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTrackingUpdate() throws Exception {
|
public void testTrackingUpdate() throws Exception {
|
||||||
Repository db2 = createBareRepository();
|
Repository db2 = createBareRepository();
|
||||||
|
|
|
@ -74,4 +74,15 @@ public static CommitMsgHook commitMsg(Repository repo,
|
||||||
PrintStream outputStream) {
|
PrintStream outputStream) {
|
||||||
return new CommitMsgHook(repo, outputStream);
|
return new CommitMsgHook(repo, outputStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param repo
|
||||||
|
* @param outputStream
|
||||||
|
* The output stream, or {@code null} to use {@code System.out}
|
||||||
|
* @return The pre-push hook for the given repository.
|
||||||
|
* @since 4.2
|
||||||
|
*/
|
||||||
|
public static PrePushHook prePush(Repository repo, PrintStream outputStream) {
|
||||||
|
return new PrePushHook(repo, outputStream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,156 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Obeo.
|
||||||
|
* 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.hooks;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.api.errors.AbortedByHookException;
|
||||||
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
import org.eclipse.jgit.transport.RemoteRefUpdate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The <code>pre-push</code> hook implementation. The pre-push hook runs during
|
||||||
|
* git push, after the remote refs have been updated but before any objects have
|
||||||
|
* been transferred.
|
||||||
|
*
|
||||||
|
* @since 4.2
|
||||||
|
*/
|
||||||
|
public class PrePushHook extends GitHook<String> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant indicating the name of the pre-push hook.
|
||||||
|
*/
|
||||||
|
public static final String NAME = "pre-push"; //$NON-NLS-1$
|
||||||
|
|
||||||
|
private String remoteName;
|
||||||
|
|
||||||
|
private String remoteLocation;
|
||||||
|
|
||||||
|
private String refs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param repo
|
||||||
|
* The repository
|
||||||
|
* @param outputStream
|
||||||
|
* The output stream the hook must use. {@code null} is allowed,
|
||||||
|
* in which case the hook will use {@code System.out}.
|
||||||
|
*/
|
||||||
|
protected PrePushHook(Repository repo, PrintStream outputStream) {
|
||||||
|
super(repo, outputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getStdinArgs() {
|
||||||
|
return refs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String call() throws IOException, AbortedByHookException {
|
||||||
|
if (canRun()) {
|
||||||
|
doRun();
|
||||||
|
}
|
||||||
|
return ""; //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {@code true}
|
||||||
|
*/
|
||||||
|
private boolean canRun() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHookName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This hook receives two parameters, which is the name and the location of
|
||||||
|
* the remote repository.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected String[] getParameters() {
|
||||||
|
return new String[] { remoteName, remoteLocation };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param name
|
||||||
|
*/
|
||||||
|
public void setRemoteName(String name) {
|
||||||
|
remoteName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param location
|
||||||
|
*/
|
||||||
|
public void setRemoteLocation(String location) {
|
||||||
|
remoteLocation = location;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param toRefs
|
||||||
|
*/
|
||||||
|
public void setRefs(Collection<RemoteRefUpdate> toRefs) {
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
boolean first = true;
|
||||||
|
for (RemoteRefUpdate u : toRefs) {
|
||||||
|
if (!first)
|
||||||
|
b.append("\n"); //$NON-NLS-1$
|
||||||
|
else
|
||||||
|
first = false;
|
||||||
|
b.append(u.getSrcRef());
|
||||||
|
b.append(" "); //$NON-NLS-1$
|
||||||
|
b.append(u.getNewObjectId().getName());
|
||||||
|
b.append(" "); //$NON-NLS-1$
|
||||||
|
b.append(u.getRemoteName());
|
||||||
|
b.append(" "); //$NON-NLS-1$
|
||||||
|
ObjectId ooid = u.getExpectedOldObjectId();
|
||||||
|
b.append((ooid == null) ? ObjectId.zeroId().getName() : ooid
|
||||||
|
.getName());
|
||||||
|
}
|
||||||
|
refs = b.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -53,6 +53,7 @@
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
|
@ -70,8 +71,11 @@
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
|
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.PrePushHook;
|
||||||
import org.eclipse.jgit.internal.JGitText;
|
import org.eclipse.jgit.internal.JGitText;
|
||||||
import org.eclipse.jgit.lib.Constants;
|
import org.eclipse.jgit.lib.Constants;
|
||||||
import org.eclipse.jgit.lib.NullProgressMonitor;
|
import org.eclipse.jgit.lib.NullProgressMonitor;
|
||||||
|
@ -557,8 +561,13 @@ public static Transport open(Repository local, URIish uri, String remoteName)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (proto.canHandle(uri, local, remoteName))
|
if (proto.canHandle(uri, local, remoteName)) {
|
||||||
return proto.open(uri, local, remoteName);
|
Transport tn = proto.open(uri, local, remoteName);
|
||||||
|
tn.prePush = Hooks.prePush(local, tn.hookOutRedirect);
|
||||||
|
tn.prePush.setRemoteLocation(uri.toString());
|
||||||
|
tn.prePush.setRemoteName(remoteName);
|
||||||
|
return tn;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new NotSupportedException(MessageFormat.format(JGitText.get().URINotSupported, uri));
|
throw new NotSupportedException(MessageFormat.format(JGitText.get().URINotSupported, uri));
|
||||||
|
@ -761,6 +770,9 @@ private static String findTrackingRefName(final String remoteName,
|
||||||
/** Assists with authentication the connection. */
|
/** Assists with authentication the connection. */
|
||||||
private CredentialsProvider credentialsProvider;
|
private CredentialsProvider credentialsProvider;
|
||||||
|
|
||||||
|
private PrintStream hookOutRedirect;
|
||||||
|
|
||||||
|
private PrePushHook prePush;
|
||||||
/**
|
/**
|
||||||
* Create a new transport instance.
|
* Create a new transport instance.
|
||||||
*
|
*
|
||||||
|
@ -778,6 +790,7 @@ protected Transport(final Repository local, final URIish uri) {
|
||||||
this.uri = uri;
|
this.uri = uri;
|
||||||
this.objectChecker = tc.newObjectChecker();
|
this.objectChecker = tc.newObjectChecker();
|
||||||
this.credentialsProvider = CredentialsProvider.getDefault();
|
this.credentialsProvider = CredentialsProvider.getDefault();
|
||||||
|
prePush = Hooks.prePush(local, hookOutRedirect);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1196,6 +1209,15 @@ 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, out);
|
||||||
return pushProcess.execute(monitor);
|
return pushProcess.execute(monitor);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue