| /* |
| * 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. |
| * |
| * 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.pgm; |
| |
| 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; |
| |
| import java.io.BufferedWriter; |
| import java.io.FileDescriptor; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.io.OutputStreamWriter; |
| 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.opt.CmdLineParser; |
| import org.eclipse.jgit.revwalk.RevWalk; |
| import org.eclipse.jgit.util.io.ThrowingPrintWriter; |
| import org.kohsuke.args4j.CmdLineException; |
| import org.kohsuke.args4j.Option; |
| |
| /** |
| * Abstract command which can be invoked from the command line. |
| * <p> |
| * Commands are configured with a single "current" repository and then the |
| * {@link #execute(String[])} method is invoked with the arguments that appear |
| * on the command line after the command name. |
| * <p> |
| * Command constructors should perform as little work as possible as they may be |
| * invoked very early during process loading, and the command may not execute |
| * even though it was constructed. |
| */ |
| public abstract class TextBuiltin { |
| private String commandName; |
| |
| @Option(name = "--help", usage = "usage_displayThisHelpText", aliases = { "-h" }) |
| private boolean help; |
| |
| /** |
| * Input stream, typically this is standard input. |
| * |
| * @since 3.4 |
| */ |
| protected InputStream ins; |
| |
| /** |
| * Writer to output to, typically this is standard output. |
| * |
| * @since 2.2 |
| */ |
| protected ThrowingPrintWriter outw; |
| |
| /** |
| * Stream to output to, typically this is standard output. |
| * |
| * @since 2.2 |
| */ |
| protected OutputStream outs; |
| |
| /** |
| * Error writer, typically this is standard error. |
| * |
| * @since 3.4 |
| */ |
| protected ThrowingPrintWriter errw; |
| |
| /** |
| * Error output stream, typically this is standard error. |
| * |
| * @since 3.4 |
| */ |
| protected OutputStream errs; |
| |
| /** Git repository the command was invoked within. */ |
| protected Repository db; |
| |
| /** Directory supplied via --git-dir command line option. */ |
| protected String gitdir; |
| |
| /** RevWalk used during command line parsing, if it was required. */ |
| protected RevWalk argWalk; |
| |
| final void setCommandName(String name) { |
| commandName = name; |
| } |
| |
| /** |
| * If this command requires a repository. |
| * |
| * @return true if {@link #db}/{@link #getRepository()} is required |
| */ |
| protected boolean requiresRepository() { |
| return true; |
| } |
| |
| /** |
| * 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 |
| */ |
| 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); |
| } |
| |
| /** |
| * 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) { |
| 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)); |
| 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); |
| } |
| |
| if (repository != null && repository.getDirectory() != null) { |
| db = repository; |
| gitdir = repository.getDirectory().getAbsolutePath(); |
| } else { |
| db = repository; |
| gitdir = gitDir; |
| } |
| } |
| |
| /** |
| * Parse arguments and run this command. |
| * |
| * @param args |
| * command line arguments passed after the command name. |
| * @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); |
| run(); |
| } |
| |
| /** |
| * Parses the command line arguments prior to running. |
| * <p> |
| * This method should only be invoked by {@link #execute(String[])}, prior |
| * to calling {@link #run()}. The default implementation parses all |
| * arguments into this object's instance fields. |
| * |
| * @param args |
| * the arguments supplied on the command line, if any. |
| * @throws java.io.IOException |
| */ |
| protected void parseArguments(String[] args) throws IOException { |
| final CmdLineParser clp = new CmdLineParser(this); |
| help = containsHelp(args); |
| try { |
| clp.parseArgument(args); |
| } catch (CmdLineException err) { |
| this.errw.println(CLIText.fatalError(err.getMessage())); |
| if (help) { |
| printUsage("", clp); //$NON-NLS-1$ |
| } |
| throw die(true, err); |
| } |
| |
| if (help) { |
| printUsage("", clp); //$NON-NLS-1$ |
| throw new TerminatedByHelpException(); |
| } |
| |
| argWalk = clp.getRevWalkGently(); |
| } |
| |
| /** |
| * Print the usage line |
| * |
| * @param clp |
| * a {@link org.eclipse.jgit.pgm.opt.CmdLineParser} object. |
| * @throws java.io.IOException |
| */ |
| public void printUsageAndExit(CmdLineParser clp) throws IOException { |
| printUsageAndExit("", clp); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Print an error message and the usage line |
| * |
| * @param message |
| * a {@link java.lang.String} object. |
| * @param clp |
| * a {@link org.eclipse.jgit.pgm.opt.CmdLineParser} object. |
| * @throws java.io.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 |
| * @since 4.2 |
| */ |
| protected void printUsage(String message, CmdLineParser clp) |
| throws IOException { |
| errw.println(message); |
| errw.print("jgit "); //$NON-NLS-1$ |
| errw.print(commandName); |
| clp.printSingleLineUsage(errw, getResourceBundle()); |
| errw.println(); |
| |
| errw.println(); |
| clp.printUsage(errw, getResourceBundle()); |
| errw.println(); |
| |
| errw.flush(); |
| } |
| |
| /** |
| * 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(); |
| } |
| |
| /** |
| * Perform the actions of this command. |
| * <p> |
| * This method should only be invoked by {@link #execute(String[])}. |
| * |
| * @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. |
| */ |
| protected abstract void run() throws Exception; |
| |
| /** |
| * Get the repository |
| * |
| * @return the repository this command accesses. |
| */ |
| public Repository getRepository() { |
| return db; |
| } |
| |
| ObjectId resolve(String s) throws IOException { |
| final ObjectId r = db.resolve(s); |
| if (r == null) |
| throw die(MessageFormat.format(CLIText.get().notARevision, s)); |
| return r; |
| } |
| |
| /** |
| * 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(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(String why, Throwable cause) { |
| return new Die(why, cause); |
| } |
| |
| /** |
| * Exit the command |
| * |
| * @param aborted |
| * boolean indicating that the execution has been aborted before |
| * running |
| * @return a runtime exception the caller is expected to throw |
| * @since 3.4 |
| */ |
| protected static Die die(boolean aborted) { |
| 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()); |
| else if (dst.startsWith(R_TAGS)) |
| dst = dst.substring(R_TAGS.length()); |
| else if (abbreviateRemote && dst.startsWith(R_REMOTES)) |
| 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); |
| } |
| |
| } |
| } |