diff options
Diffstat (limited to 'org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java')
-rw-r--r-- | org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java | 327 |
1 files changed, 231 insertions, 96 deletions
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java index 8e8b82fe07..c572e3bc7d 100644 --- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java +++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/TextBuiltin.java @@ -1,49 +1,19 @@ /* * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com> - * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> - * and other copyright owners as documented in the project's IP log. + * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others * - * 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 + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://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. + * SPDX-License-Identifier: BSD-3-Clause */ package org.eclipse.jgit.pgm; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_LOG_OUTPUT_ENCODING; +import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_SECTION_I18N; import static org.eclipse.jgit.lib.Constants.R_HEADS; import static org.eclipse.jgit.lib.Constants.R_REMOTES; import static org.eclipse.jgit.lib.Constants.R_TAGS; @@ -56,15 +26,21 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; -import java.io.PrintWriter; +import java.nio.charset.Charset; import java.text.MessageFormat; import java.util.ResourceBundle; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.pgm.internal.CLIText; +import org.eclipse.jgit.pgm.internal.SshDriver; import org.eclipse.jgit.pgm.opt.CmdLineParser; import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.transport.SshSessionFactory; +import org.eclipse.jgit.transport.ssh.jsch.JschConfigSessionFactory; +import org.eclipse.jgit.transport.sshd.DefaultProxyDataFactory; +import org.eclipse.jgit.transport.sshd.JGitKeyCache; +import org.eclipse.jgit.transport.sshd.SshdSessionFactory; import org.eclipse.jgit.util.io.ThrowingPrintWriter; import org.kohsuke.args4j.CmdLineException; import org.kohsuke.args4j.Option; @@ -86,6 +62,9 @@ public abstract class TextBuiltin { @Option(name = "--help", usage = "usage_displayThisHelpText", aliases = { "-h" }) private boolean help; + @Option(name = "--ssh", usage = "usage_sshDriver") + private SshDriver sshDriver = SshDriver.APACHE; + /** * Input stream, typically this is standard input. * @@ -108,14 +87,6 @@ public abstract class TextBuiltin { protected OutputStream outs; /** - * Stream to output to, typically this is standard output. - * - * @deprecated Use outw instead - */ - @Deprecated - protected PrintWriter out; - - /** * Error writer, typically this is standard error. * * @since 3.4 @@ -138,52 +109,90 @@ public abstract class TextBuiltin { /** RevWalk used during command line parsing, if it was required. */ protected RevWalk argWalk; - final void setCommandName(final String name) { + final void setCommandName(String name) { commandName = name; } - /** @return true if {@link #db}/{@link #getRepository()} is required. */ + /** + * If this command requires a repository. + * + * @return true if {@link #db}/{@link #getRepository()} is required + */ protected boolean requiresRepository() { return true; } /** - * Initialize the command to work with a repository. + * Initializes the command to work with a repository, including setting the + * output and error streams. * * @param repository * the opened repository that the command should work on. * @param gitDir * value of the {@code --git-dir} command line option, if * {@code repository} is null. + * @param input + * input stream from which input will be read + * @param output + * output stream to which output will be written + * @param error + * error stream to which errors will be written + * @since 4.9 */ - protected void init(final Repository repository, final String gitDir) { - try { - final String outputEncoding = repository != null ? repository - .getConfig().getString("i18n", null, "logOutputEncoding") : null; //$NON-NLS-1$ //$NON-NLS-2$ - if (ins == null) - ins = new FileInputStream(FileDescriptor.in); - if (outs == null) - outs = new FileOutputStream(FileDescriptor.out); - if (errs == null) - errs = new FileOutputStream(FileDescriptor.err); - BufferedWriter outbufw; - if (outputEncoding != null) - outbufw = new BufferedWriter(new OutputStreamWriter(outs, - outputEncoding)); - else - outbufw = new BufferedWriter(new OutputStreamWriter(outs)); - out = new PrintWriter(outbufw); - outw = new ThrowingPrintWriter(outbufw); - BufferedWriter errbufw; - if (outputEncoding != null) - errbufw = new BufferedWriter(new OutputStreamWriter(errs, - outputEncoding)); - else - errbufw = new BufferedWriter(new OutputStreamWriter(errs)); - errw = new ThrowingPrintWriter(errbufw); - } catch (IOException e) { - throw die(CLIText.get().cannotCreateOutputStream); + public void initRaw(final Repository repository, final String gitDir, + InputStream input, OutputStream output, OutputStream error) { + this.ins = input; + this.outs = output; + this.errs = error; + init(repository, gitDir); + } + + /** + * Get the log output encoding specified in the repository's + * {@code i18n.logOutputEncoding} configuration. + * + * @param repository + * the repository. + * @return Charset corresponding to {@code i18n.logOutputEncoding}, or + * {@code UTF_8}. + */ + private Charset getLogOutputEncodingCharset(Repository repository) { + if (repository != null) { + String logOutputEncoding = repository.getConfig().getString( + CONFIG_SECTION_I18N, null, CONFIG_KEY_LOG_OUTPUT_ENCODING); + if (logOutputEncoding != null) { + try { + return Charset.forName(logOutputEncoding); + } catch (IllegalArgumentException e) { + throw die(CLIText.get().cannotCreateOutputStream, e); + } + } } + return UTF_8; + } + + /** + * Initialize the command to work with a repository. + * + * @param repository + * the opened repository that the command should work on. + * @param gitDir + * value of the {@code --git-dir} command line option, if + * {@code repository} is null. + */ + protected void init(Repository repository, String gitDir) { + Charset charset = getLogOutputEncodingCharset(repository); + + if (ins == null) + ins = new FileInputStream(FileDescriptor.in); + if (outs == null) + outs = new FileOutputStream(FileDescriptor.out); + if (errs == null) + errs = new FileOutputStream(FileDescriptor.err); + outw = new ThrowingPrintWriter(new BufferedWriter( + new OutputStreamWriter(outs, charset))); + errw = new ThrowingPrintWriter(new BufferedWriter( + new OutputStreamWriter(errs, charset))); if (repository != null && repository.getDirectory() != null) { db = repository; @@ -199,13 +208,34 @@ public abstract class TextBuiltin { * * @param args * command line arguments passed after the command name. - * @throws Exception + * @throws java.lang.Exception * an error occurred while processing the command. The main * framework will catch the exception and print a message on * standard error. */ public final void execute(String[] args) throws Exception { parseArguments(args); + switch (sshDriver) { + case APACHE: { + SshdSessionFactory factory = new SshdSessionFactory( + new JGitKeyCache(), new DefaultProxyDataFactory()); + try { + Runtime.getRuntime() + .addShutdownHook(new Thread(factory::close)); + } catch (IllegalStateException e) { + // ignore - the VM is already shutting down + } + SshSessionFactory.setInstance(factory); + break; + } + case JSCH: + JschConfigSessionFactory factory = new JschConfigSessionFactory(); + SshSessionFactory.setInstance(factory); + break; + default: + SshSessionFactory.setInstance(null); + break; + } run(); } @@ -218,21 +248,25 @@ public abstract class TextBuiltin { * * @param args * the arguments supplied on the command line, if any. - * @throws IOException + * @throws java.io.IOException + * if an IO error occurred */ - protected void parseArguments(final String[] args) throws IOException { + protected void parseArguments(String[] args) throws IOException { final CmdLineParser clp = new CmdLineParser(this); + help = containsHelp(args); try { clp.parseArgument(args); } catch (CmdLineException err) { - if (!help) { - this.errw.println(MessageFormat.format(CLIText.get().fatalError, err.getMessage())); - throw die(true); + this.errw.println(CLIText.fatalError(err.getMessage())); + if (help) { + printUsage("", clp); //$NON-NLS-1$ } + throw die(true, err); } if (help) { - printUsageAndExit(clp); + printUsage("", clp); //$NON-NLS-1$ + throw new TerminatedByHelpException(); } argWalk = clp.getRevWalkGently(); @@ -242,9 +276,11 @@ public abstract class TextBuiltin { * Print the usage line * * @param clp - * @throws IOException + * a {@link org.eclipse.jgit.pgm.opt.CmdLineParser} object. + * @throws java.io.IOException + * if an IO error occurred */ - public void printUsageAndExit(final CmdLineParser clp) throws IOException { + public void printUsageAndExit(CmdLineParser clp) throws IOException { printUsageAndExit("", clp); //$NON-NLS-1$ } @@ -252,10 +288,30 @@ public abstract class TextBuiltin { * Print an error message and the usage line * * @param message + * a {@link java.lang.String} object. * @param clp - * @throws IOException + * a {@link org.eclipse.jgit.pgm.opt.CmdLineParser} object. + * @throws java.io.IOException + * if an IO error occurred */ - public void printUsageAndExit(final String message, final CmdLineParser clp) throws IOException { + public void printUsageAndExit(String message, CmdLineParser clp) throws IOException { + printUsage(message, clp); + throw die(true); + } + + /** + * Print usage help text. + * + * @param message + * non null + * @param clp + * parser used to print options + * @throws java.io.IOException + * if an IO error occurred + * @since 4.2 + */ + protected void printUsage(String message, CmdLineParser clp) + throws IOException { errw.println(message); errw.print("jgit "); //$NON-NLS-1$ errw.print(commandName); @@ -267,12 +323,33 @@ public abstract class TextBuiltin { errw.println(); errw.flush(); - throw die(true); } /** - * @return the resource bundle that will be passed to args4j for purpose - * of string localization + * Get error writer + * + * @return error writer, typically this is standard error. + * @since 4.2 + */ + public ThrowingPrintWriter getErrorWriter() { + return errw; + } + + /** + * Get output writer + * + * @return output writer, typically this is standard output. + * @since 4.9 + */ + public ThrowingPrintWriter getOutputWriter() { + return outw; + } + + /** + * Get resource bundle with localized texts + * + * @return the resource bundle that will be passed to args4j for purpose of + * string localization */ protected ResourceBundle getResourceBundle() { return CLIText.get().resourceBundle(); @@ -283,7 +360,7 @@ public abstract class TextBuiltin { * <p> * This method should only be invoked by {@link #execute(String[])}. * - * @throws Exception + * @throws java.lang.Exception * an error occurred while processing the command. The main * framework will catch the exception and print a message on * standard error. @@ -291,13 +368,15 @@ public abstract class TextBuiltin { protected abstract void run() throws Exception; /** + * Get the repository + * * @return the repository this command accesses. */ public Repository getRepository() { return db; } - ObjectId resolve(final String s) throws IOException { + ObjectId resolve(String s) throws IOException { final ObjectId r = db.resolve(s); if (r == null) throw die(MessageFormat.format(CLIText.get().notARevision, s)); @@ -305,28 +384,35 @@ public abstract class TextBuiltin { } /** + * Exit the command with an error message + * * @param why * textual explanation * @return a runtime exception the caller is expected to throw */ - protected static Die die(final String why) { + protected static Die die(String why) { return new Die(why); } /** + * Exit the command with an error message and an exception + * * @param why * textual explanation * @param cause * why the command has failed. * @return a runtime exception the caller is expected to throw */ - protected static Die die(final String why, final Throwable cause) { + protected static Die die(String why, Throwable cause) { return new Die(why, cause); } /** + * Exit the command + * * @param aborted - * boolean indicating that the execution has been aborted before running + * boolean indicating that the execution has been aborted before + * running * @return a runtime exception the caller is expected to throw * @since 3.4 */ @@ -334,6 +420,21 @@ public abstract class TextBuiltin { return new Die(aborted); } + /** + * Exit the command + * + * @param aborted + * boolean indicating that the execution has been aborted before + * running + * @param cause + * why the command has failed. + * @return a runtime exception the caller is expected to throw + * @since 4.2 + */ + protected static Die die(boolean aborted, Throwable cause) { + return new Die(aborted, cause); + } + String abbreviateRef(String dst, boolean abbreviateRemote) { if (dst.startsWith(R_HEADS)) dst = dst.substring(R_HEADS.length()); @@ -343,4 +444,38 @@ public abstract class TextBuiltin { dst = dst.substring(R_REMOTES.length()); return dst; } + + /** + * Check if the arguments contain a help option + * + * @param args + * non null + * @return true if the given array contains help option + * @since 4.2 + */ + public static boolean containsHelp(String[] args) { + for (String str : args) { + if (str.equals("-h") || str.equals("--help")) { //$NON-NLS-1$ //$NON-NLS-2$ + return true; + } + } + return false; + } + + /** + * Exception thrown by {@link TextBuiltin} if it proceeds 'help' option + * + * @since 4.2 + */ + public static class TerminatedByHelpException extends Die { + private static final long serialVersionUID = 1L; + + /** + * Default constructor + */ + public TerminatedByHelpException() { + super(true); + } + + } } |