import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.pgm.TextBuiltin.TerminatedByHelpException;
import org.eclipse.jgit.pgm.internal.CLIText;
import org.eclipse.jgit.pgm.opt.CmdLineParser;
import org.eclipse.jgit.pgm.opt.SubcommandHandler;
System.arraycopy(args, 1, argv, 0, args.length - 1);
CLIGitCommand bean = new CLIGitCommand();
- final CmdLineParser clp = new CmdLineParser(bean);
+ final CmdLineParser clp = new TestCmdLineParser(bean);
clp.parseArgument(argv);
final TextBuiltin cmd = bean.getSubcommand();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
cmd.outs = baos;
+ ByteArrayOutputStream errs = new ByteArrayOutputStream();
+ cmd.errs = errs;
+ boolean seenHelp = TextBuiltin.containsHelp(argv);
if (cmd.requiresRepository())
cmd.init(db, null);
else
try {
cmd.execute(bean.getArguments().toArray(
new String[bean.getArguments().size()]));
+ } catch (TerminatedByHelpException e) {
+ seenHelp = true;
+ // this is not a failure, command execution should just not happen
} finally {
- if (cmd.outw != null)
+ if (cmd.outw != null) {
cmd.outw.flush();
+ }
+ if (cmd.errw != null) {
+ cmd.errw.flush();
+ }
+ if (seenHelp) {
+ return errs.toByteArray();
+ } else if (errs.size() > 0) {
+ // forward the errors to the standard err
+ System.err.print(errs.toString());
+ }
}
return baos.toByteArray();
}
return list.toArray(new String[list.size()]);
}
+ static class TestCmdLineParser extends CmdLineParser {
+ public TestCmdLineParser(Object bean) {
+ super(bean);
+ }
+
+ @Override
+ protected boolean containsHelp(String... args) {
+ return false;
+ }
+ }
}
package org.eclipse.jgit.pgm;
import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.CLIRepositoryTestCase;
assertArrayEquals(new String[] { "v1.0-0-g6fd41be", "" },
execute("git describe --long HEAD"));
}
+
+ @Test
+ public void testHelpArgumentBeforeUnknown() throws Exception {
+ String[] output = execute("git describe -h -XYZ");
+ String all = Arrays.toString(output);
+ assertTrue("Unexpected help output: " + all,
+ all.contains("jgit describe"));
+ assertFalse("Unexpected help output: " + all, all.contains("fatal"));
+ }
+
+ @Test
+ public void testHelpArgumentAfterUnknown() throws Exception {
+ String[] output = execute("git describe -XYZ -h");
+ String all = Arrays.toString(output);
+ assertTrue("Unexpected help output: " + all,
+ all.contains("jgit describe"));
+ assertTrue("Unexpected help output: " + all, all.contains("fatal"));
+ }
}
}
private void execute(final String[] argv) throws Exception {
- final CmdLineParser clp = new CmdLineParser(this);
+ final CmdLineParser clp = new SubcommandLineParser(this);
PrintWriter writer = new PrintWriter(System.err);
try {
clp.parseArgument(argv);
}
}
}
+
+ /**
+ * Parser for subcommands which doesn't stop parsing on help options and so
+ * proceeds all specified options
+ */
+ static class SubcommandLineParser extends CmdLineParser {
+ public SubcommandLineParser(Object bean) {
+ super(bean);
+ }
+
+ @Override
+ protected boolean containsHelp(String... args) {
+ return false;
+ }
+ }
}
}
@Override
- public void printUsageAndExit(final String message, final CmdLineParser clp)
+ public void printUsage(final String message, final CmdLineParser clp)
throws IOException {
errw.println(message);
errw.println("jgit remote [--verbose (-v)] [--help (-h)]"); //$NON-NLS-1$
errw.println();
errw.flush();
- throw die(true);
}
private void print(List<RemoteConfig> remotes) throws IOException {
*/
protected void parseArguments(final 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, err);
+ this.errw.println(MessageFormat.format(CLIText.get().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();
* @throws IOException
*/
public void printUsageAndExit(final String message, final CmdLineParser clp) throws IOException {
+ printUsage(message, clp);
+ throw die(true);
+ }
+
+ /**
+ * @param message
+ * non null
+ * @param clp
+ * parser used to print options
+ * @throws IOException
+ * @since 4.2
+ */
+ protected void printUsage(final String message, final CmdLineParser clp)
+ throws IOException {
errw.println(message);
errw.print("jgit "); //$NON-NLS-1$
errw.print(commandName);
errw.println();
errw.flush();
- throw die(true);
}
/**
dst = dst.substring(R_REMOTES.length());
return dst;
}
+
+ /**
+ * @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);
+ }
+
+ }
}
import java.lang.reflect.Field;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
-import org.kohsuke.args4j.Argument;
-import org.kohsuke.args4j.CmdLineException;
-import org.kohsuke.args4j.IllegalAnnotationError;
-import org.kohsuke.args4j.NamedOptionDef;
-import org.kohsuke.args4j.Option;
-import org.kohsuke.args4j.OptionDef;
-import org.kohsuke.args4j.spi.OptionHandler;
-import org.kohsuke.args4j.spi.Setter;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.pgm.TextBuiltin;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
+import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.CmdLineException;
+import org.kohsuke.args4j.IllegalAnnotationError;
+import org.kohsuke.args4j.NamedOptionDef;
+import org.kohsuke.args4j.Option;
+import org.kohsuke.args4j.OptionDef;
+import org.kohsuke.args4j.spi.OptionHandler;
+import org.kohsuke.args4j.spi.Setter;
/**
* Extended command line parser which handles --foo=value arguments.
private RevWalk walk;
+ private boolean seenHelp;
+
/**
* Creates a new command line owner that parses arguments/options and set
* them into the given object.
}
tmp.add(str);
+
+ if (containsHelp(args)) {
+ // suppress exceptions on required parameters if help is present
+ seenHelp = true;
+ // stop argument parsing here
+ break;
+ }
}
+ List<OptionHandler> backup = null;
+ if (seenHelp) {
+ backup = unsetRequiredOptions();
+ }
+
+ try {
+ super.parseArgument(tmp.toArray(new String[tmp.size()]));
+ } finally {
+ // reset "required" options to defaults for correct command printout
+ if (backup != null && !backup.isEmpty()) {
+ restoreRequiredOptions(backup);
+ }
+ seenHelp = false;
+ }
+ }
+
+ private List<OptionHandler> unsetRequiredOptions() {
+ List<OptionHandler> options = getOptions();
+ List<OptionHandler> backup = new ArrayList<>(options);
+ for (Iterator<OptionHandler> iterator = options.iterator(); iterator
+ .hasNext();) {
+ OptionHandler handler = iterator.next();
+ if (handler.option instanceof NamedOptionDef
+ && handler.option.required()) {
+ iterator.remove();
+ }
+ }
+ return backup;
+ }
+
+ private void restoreRequiredOptions(List<OptionHandler> backup) {
+ List<OptionHandler> options = getOptions();
+ options.clear();
+ options.addAll(backup);
+ }
- super.parseArgument(tmp.toArray(new String[tmp.size()]));
+ /**
+ * @param args
+ * non null
+ * @return true if the given array contains help option
+ * @since 4.2
+ */
+ protected boolean containsHelp(final String... args) {
+ return TextBuiltin.containsHelp(args);
}
/**
return walk;
}
- static class MyOptionDef extends OptionDef {
+ class MyOptionDef extends OptionDef {
public MyOptionDef(OptionDef o) {
super(o.usage(), o.metaVar(), o.required(), o.handler(), o
return metaVar();
}
}
+
+ @Override
+ public boolean required() {
+ return seenHelp ? false : super.required();
+ }
}
@Override
return super.createOptionHandler(new MyOptionDef(o), setter);
}
+
+ @SuppressWarnings("unchecked")
+ private List<OptionHandler> getOptions() {
+ List<OptionHandler> options = null;
+ try {
+ Field field = org.kohsuke.args4j.CmdLineParser.class
+ .getDeclaredField("options"); //$NON-NLS-1$
+ field.setAccessible(true);
+ options = (List<OptionHandler>) field.get(this);
+ } catch (NoSuchFieldException | SecurityException
+ | IllegalArgumentException | IllegalAccessException e) {
+ // ignore
+ }
+ if (options == null) {
+ return Collections.emptyList();
+ }
+ return options;
+ }
}