blob: b6f12a1ab1090f77b03f708fe73cd02bfc3757df [file] [log] [blame]
/*
* 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.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.text.MessageFormat;
import java.util.ResourceBundle;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.Option;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.pgm.opt.CmdLineParser;
import org.eclipse.jgit.revwalk.RevWalk;
/**
* 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;
/** Stream to output to, typically this is standard output. */
protected PrintWriter out;
/** Git repository the command was invoked within. */
protected Repository db;
/** Directory supplied via --git-dir command line option. */
protected File gitdir;
/** RevWalk used during command line parsing, if it was required. */
protected RevWalk argWalk;
final void setCommandName(final String name) {
commandName = name;
}
/** @return true if {@link #db}/{@link #getRepository()} is required. */
protected boolean requiresRepository() {
return true;
}
void init(final Repository repo, final File gd) {
try {
final String outputEncoding = repo != null ? repo.getConfig()
.getString("i18n", null, "logOutputEncoding") : null;
if (outputEncoding != null)
out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(System.out, outputEncoding)));
else
out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(System.out)));
} catch (IOException e) {
throw die(CLIText.get().cannotCreateOutputStream);
}
if (repo != null) {
db = repo;
gitdir = repo.getDirectory();
} else {
db = null;
gitdir = gd;
}
}
/**
* Parse arguments and run this command.
*
* @param args
* command line arguments passed after the command name.
* @throws 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.
*/
protected void parseArguments(final String[] args) {
final CmdLineParser clp = new CmdLineParser(this);
try {
clp.parseArgument(args);
} catch (CmdLineException err) {
if (!help) {
System.err.println(MessageFormat.format(CLIText.get().fatalError, err.getMessage()));
System.exit(1);
}
}
if (help) {
printUsageAndExit(clp);
}
argWalk = clp.getRevWalkGently();
}
/**
* Print the usage line
*
* @param clp
*/
public void printUsageAndExit(final CmdLineParser clp) {
printUsageAndExit("", clp);
}
/**
* Print an error message and the usage line
*
* @param message
* @param clp
*/
public void printUsageAndExit(final String message, final CmdLineParser clp) {
PrintWriter writer = new PrintWriter(System.err);
writer.println(message);
writer.print("jgit ");
writer.print(commandName);
clp.printSingleLineUsage(writer, getResourceBundle());
writer.println();
writer.println();
clp.printUsage(writer, getResourceBundle());
writer.println();
writer.flush();
System.exit(1);
}
/**
* @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 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;
/**
* @return the repository this command accesses.
*/
public Repository getRepository() {
return db;
}
ObjectId resolve(final String s) throws IOException {
final ObjectId r = db.resolve(s);
if (r == null)
throw die(MessageFormat.format(CLIText.get().notARevision, s));
return r;
}
/**
* @param why
* textual explanation
* @return a runtime exception the caller is expected to throw
*/
protected static Die die(final String why) {
return new Die(why);
}
/**
* @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) {
return new Die(why, 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;
}
}