{"thread":{"id":"14681","subject":"[JGIT PATCH 0/9] List commonly used (or recognized) commands","startedAt":"2008-07-25T19:45:58Z","lastAt":"2008-07-25T20:02:46Z","messageCount":12,"participants":["Shawn O. Pearce"],"isPatch":true,"patchVersion":1,"patchTotal":9},"messages":[{"id":"84929","messageId":"1217015167-4680-1-git-send-email-spearce@spearce.org","threadId":"14681","inReplyTo":null,"subject":"[JGIT PATCH 0/9] List commonly used (or recognized) commands","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-07-25T19:45:58Z","receivedAt":"2008-07-25T19:45:58Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"This series adds support to jgit to list commonly used subcommands\nif the user just executes `jgit` with no subcommand requested:\n\n  $ jgit\n  jgit --git-dir GIT_DIR --help (-h) --show-stack-trace command [ARG ...]\n  \n  The most commonly used commands are:\n   fetch  Update remote refs from another repository\n   log    View commit history\n   push   Update remote repository from local refs\n   tag    Create a tag\n\nCommands inside of the pgm.debug package are automatically given\nthe debug- prefix, allowing debug-show-commands to be used to show\nthe command table.\n\nCommands must be listed in the META-INF/services/org...TextBuiltin\nfile in order to be considered for execution.  This means that we\nmust add (or remove) command class names from the listing each time\nwe introduce or remove a command line subcommand.\n\nOne advantage to this structure is additional commands can defined\nin other packages, and are available so long as the classes are\nreachable through the CLASSPATH.  Since jgit.sh hardcodes the\nCLASSPATH to only itself this is not fully supported yet, but does\nopen the door for users to extend jgit's command line support.\n\n\nShawn O. Pearce (9):\n  Switch jgit.pgm to J2SE-1.5 execution environment\n  Remove unnecessary duplicate if (help) test inside TextBuiltin\n  Create an optional documentation annotation for TextBuiltin\n  Create a lightweight registration wrapper for TextBuiltin\n  Create a catalog of CommandRefs for lookup and enumeration\n  Document some common commands with the new Command annotation\n  Include commonly used commands in main help output\n  Refactor SubcommandHandler to use CommandCatalog instead of\n    reflection\n  Add debug-show-commands to display the command table\n\n org.spearce.jgit.pgm/.classpath                    |    2 +-\n .../services/org.spearce.jgit.pgm.TextBuiltin      |   14 ++\n .../src/org/spearce/jgit/pgm/Command.java          |   72 ++++++++\n .../src/org/spearce/jgit/pgm/CommandCatalog.java   |  188 ++++++++++++++++++++\n .../src/org/spearce/jgit/pgm/CommandRef.java       |  158 ++++++++++++++++\n .../src/org/spearce/jgit/pgm/Fetch.java            |    1 +\n .../src/org/spearce/jgit/pgm/Log.java              |    1 +\n .../src/org/spearce/jgit/pgm/Main.java             |   18 ++\n .../src/org/spearce/jgit/pgm/Push.java             |    1 +\n .../src/org/spearce/jgit/pgm/Tag.java              |    1 +\n .../src/org/spearce/jgit/pgm/TextBuiltin.java      |   17 +--\n .../org/spearce/jgit/pgm/debug/ShowCommands.java   |   78 ++++++++\n .../spearce/jgit/pgm/opt/SubcommandHandler.java    |   65 +------\n 13 files changed, 543 insertions(+), 73 deletions(-)\n create mode 100644 org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin\n create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Command.java\n create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandCatalog.java\n create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandRef.java\n create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/ShowCommands.java\n"},{"id":"84930","messageId":"1217015167-4680-2-git-send-email-spearce@spearce.org","threadId":"14681","inReplyTo":"1217015167-4680-1-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 1/9] Switch jgit.pgm to J2SE-1.5 execution environment","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-07-25T19:45:59Z","receivedAt":"2008-07-25T19:45:59Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"We have been keeping the jgit library itself on Java 5, and the\njgit command line tools should also be on Java 5 and avoid using\nany Java 6 APIs (for now).  Not all of our target platforms have\na Java 6 virtual machine available out of the box.\n\nSince the pgm project broke out of the library project our code\nalready conforms to Java 5 APIs, so we just have to switch the\nbuild path settings.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n org.spearce.jgit.pgm/.classpath |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/org.spearce.jgit.pgm/.classpath b/org.spearce.jgit.pgm/.classpath\nindex cc861d2..50dd6d3 100644\n--- a/org.spearce.jgit.pgm/.classpath\n+++ b/org.spearce.jgit.pgm/.classpath\n@@ -2,7 +2,7 @@\n <classpath>\n \t<classpathentry kind=\"src\" path=\"src\"/>\n \t<classpathentry exported=\"true\" kind=\"lib\" path=\"lib/args4j-2.0.9.jar\" sourcepath=\"lib/args4j-2.0.9.zip\"/>\n-\t<classpathentry exported=\"true\" kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER\"/>\n+\t<classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5\"/>\n \t<classpathentry combineaccessrules=\"false\" exported=\"true\" kind=\"src\" path=\"/org.spearce.jgit\"/>\n \t<classpathentry kind=\"output\" path=\"bin\"/>\n </classpath>\n-- \n1.6.0.rc0.182.gb96c7\n"},{"id":"84933","messageId":"1217015167-4680-3-git-send-email-spearce@spearce.org","threadId":"14681","inReplyTo":"1217015167-4680-2-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 2/9] Remove unnecessary duplicate if (help) test inside TextBuiltin","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-07-25T19:46:00Z","receivedAt":"2008-07-25T19:46:00Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"This was caused by a copy-and-paste error as I borrowed the\nglobal argument handling code in Main to help start writing\nthe command specific argument handling in TextBuiltin.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/pgm/TextBuiltin.java      |    9 ++++-----\n 1 files changed, 4 insertions(+), 5 deletions(-)\n\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/TextBuiltin.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/TextBuiltin.java\nindex d02a0a2..e2eef84 100644\n--- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/TextBuiltin.java\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/TextBuiltin.java\n@@ -145,11 +145,10 @@ public abstract class TextBuiltin {\n \t\t\tclp.printSingleLineUsage(System.err);\n \t\t\tSystem.err.println();\n \n-\t\t\tif (help) {\n-\t\t\t\tSystem.err.println();\n-\t\t\t\tclp.printUsage(System.err);\n-\t\t\t\tSystem.err.println();\n-\t\t\t}\n+\t\t\tSystem.err.println();\n+\t\t\tclp.printUsage(System.err);\n+\t\t\tSystem.err.println();\n+\n \t\t\tSystem.exit(1);\n \t\t}\n \n-- \n1.6.0.rc0.182.gb96c7\n"},{"id":"84931","messageId":"1217015167-4680-4-git-send-email-spearce@spearce.org","threadId":"14681","inReplyTo":"1217015167-4680-3-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 3/9] Create an optional documentation annotation for TextBuiltin","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-07-25T19:46:01Z","receivedAt":"2008-07-25T19:46:01Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"This new annotation can be used for automatic command list creation,\nor additional help generation by the default help generator.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/pgm/Command.java          |   72 ++++++++++++++++++++\n 1 files changed, 72 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Command.java\n\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Command.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Command.java\nnew file mode 100644\nindex 0000000..40be1f7\n--- /dev/null\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Command.java\n@@ -0,0 +1,72 @@\n+/*\n+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+package org.spearce.jgit.pgm;\n+\n+import static java.lang.annotation.ElementType.TYPE;\n+import static java.lang.annotation.RetentionPolicy.RUNTIME;\n+\n+import java.lang.annotation.Retention;\n+import java.lang.annotation.Target;\n+\n+/**\n+ * Annotation to document a {@link TextBuiltin}.\n+ * <p>\n+ * This is an optional annotation for TextBuiltin subclasses and it carries\n+ * documentation forward into the runtime system describing what the command is\n+ * and why users may want to invoke it.\n+ */\n+@Retention(RUNTIME)\n+@Target( { TYPE })\n+public @interface Command {\n+\t/**\n+\t * @return name the command is invoked as from the command line. If the\n+\t *         (default) empty string is supplied the name will be generated\n+\t *         from the class name.\n+\t */\n+\tpublic String name() default \"\";\n+\n+\t/**\n+\t * @return one line description of the command's feature set.\n+\t */\n+\tpublic String usage() default \"\";\n+\n+\t/**\n+\t * @return true if this command is considered to be commonly used.\n+\t */\n+\tpublic boolean common() default false;\n+}\n-- \n1.6.0.rc0.182.gb96c7\n"},{"id":"84932","messageId":"1217015167-4680-5-git-send-email-spearce@spearce.org","threadId":"14681","inReplyTo":"1217015167-4680-4-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 4/9] Create a lightweight registration wrapper for TextBuiltin","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-07-25T19:46:02Z","receivedAt":"2008-07-25T19:46:02Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Automatic command list generation requires knowing what commands\nare available to this runtime, and what name those commands can be\ncalled as by the end-user.  This lightweight wrappers carries the\ndata from the Command annotation, possibly filling in the name of\nthe command by generating it from the implementation class name.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/pgm/CommandRef.java       |  151 ++++++++++++++++++++\n 1 files changed, 151 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandRef.java\n\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandRef.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandRef.java\nnew file mode 100644\nindex 0000000..40400a7\n--- /dev/null\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandRef.java\n@@ -0,0 +1,151 @@\n+/*\n+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+package org.spearce.jgit.pgm;\n+\n+import java.lang.reflect.Constructor;\n+import java.lang.reflect.InvocationTargetException;\n+\n+/**\n+ * Description of a command (a {@link TextBuiltin} subclass.\n+ * <p>\n+ * These descriptions are lightweight compared to creating a command instance\n+ * and are therefore suitable for catalogs of \"known\" commands without linking\n+ * the command's implementation and creating a dummy instance of the command.\n+ */\n+public class CommandRef {\n+\tprivate final Class<? extends TextBuiltin> impl;\n+\n+\tprivate final String name;\n+\n+\tprivate String usage;\n+\n+\tboolean common;\n+\n+\tCommandRef(final Class<? extends TextBuiltin> clazz) {\n+\t\tthis(clazz, guessName(clazz));\n+\t}\n+\n+\tCommandRef(final Class<? extends TextBuiltin> clazz, final Command cmd) {\n+\t\tthis(clazz, cmd.name().length() > 0 ? cmd.name() : guessName(clazz));\n+\t\tusage = cmd.usage();\n+\t\tcommon = cmd.common();\n+\t}\n+\n+\tprivate CommandRef(final Class<? extends TextBuiltin> clazz, final String cn) {\n+\t\timpl = clazz;\n+\t\tname = cn;\n+\t\tusage = \"\";\n+\t}\n+\n+\tprivate static String guessName(final Class<? extends TextBuiltin> clazz) {\n+\t\tfinal StringBuilder s = new StringBuilder();\n+\t\tif (clazz.getName().startsWith(\"org.spearce.jgit.pgm.debug.\"))\n+\t\t\ts.append(\"debug-\");\n+\n+\t\tboolean lastWasDash = true;\n+\t\tfor (final char c : clazz.getSimpleName().toCharArray()) {\n+\t\t\tif (Character.isUpperCase(c)) {\n+\t\t\t\tif (!lastWasDash)\n+\t\t\t\t\ts.append('-');\n+\t\t\t\tlastWasDash = !lastWasDash;\n+\t\t\t\ts.append(Character.toLowerCase(c));\n+\t\t\t} else {\n+\t\t\t\ts.append(c);\n+\t\t\t}\n+\t\t}\n+\t\treturn s.toString();\n+\t}\n+\n+\t/**\n+\t * @return name the command is invoked as from the command line.\n+\t */\n+\tpublic String getName() {\n+\t\treturn name;\n+\t}\n+\n+\t/**\n+\t * @return one line description of the command's feature set.\n+\t */\n+\tpublic String getUsage() {\n+\t\treturn usage;\n+\t}\n+\n+\t/**\n+\t * @return true if this command is considered to be commonly used.\n+\t */\n+\tpublic boolean isCommon() {\n+\t\treturn common;\n+\t}\n+\n+\t/**\n+\t * @return name of the Java class which implements this command.\n+\t */\n+\tpublic String getImplementationClassName() {\n+\t\treturn impl.getName();\n+\t}\n+\n+\t/**\n+\t * @return a new instance of the command implementation.\n+\t */\n+\tpublic TextBuiltin create() {\n+\t\tfinal Constructor<? extends TextBuiltin> c;\n+\t\ttry {\n+\t\t\tc = impl.getDeclaredConstructor();\n+\t\t} catch (SecurityException e) {\n+\t\t\tthrow new RuntimeException(\"Cannot create command \" + getName(), e);\n+\t\t} catch (NoSuchMethodException e) {\n+\t\t\tthrow new RuntimeException(\"Cannot create command \" + getName(), e);\n+\t\t}\n+\t\tc.setAccessible(true);\n+\n+\t\tfinal TextBuiltin r;\n+\t\ttry {\n+\t\t\tr = c.newInstance();\n+\t\t} catch (InstantiationException e) {\n+\t\t\tthrow new RuntimeException(\"Cannot create command \" + getName(), e);\n+\t\t} catch (IllegalAccessException e) {\n+\t\t\tthrow new RuntimeException(\"Cannot create command \" + getName(), e);\n+\t\t} catch (IllegalArgumentException e) {\n+\t\t\tthrow new RuntimeException(\"Cannot create command \" + getName(), e);\n+\t\t} catch (InvocationTargetException e) {\n+\t\t\tthrow new RuntimeException(\"Cannot create command \" + getName(), e);\n+\t\t}\n+\t\tr.setCommandName(getName());\n+\t\treturn r;\n+\t}\n+}\n-- \n1.6.0.rc0.182.gb96c7\n"},{"id":"84938","messageId":"1217015167-4680-6-git-send-email-spearce@spearce.org","threadId":"14681","inReplyTo":"1217015167-4680-5-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 5/9] Create a catalog of CommandRefs for lookup and enumeration","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-07-25T19:46:03Z","receivedAt":"2008-07-25T19:46:03Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"The command catalog supports enumerating commands registered through\na services list, converting each entry into a CommandRef and making\nthat available to callers on demand.  The CommandRef can be later used\nto create a command instance or just to obtain documentation about it.\n\nAll current commands are listed in the service registration.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../services/org.spearce.jgit.pgm.TextBuiltin      |   12 ++\n .../src/org/spearce/jgit/pgm/CommandCatalog.java   |  188 ++++++++++++++++++++\n 2 files changed, 200 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin\n create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandCatalog.java\n\ndiff --git a/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin b/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin\nnew file mode 100644\nindex 0000000..69ef046\n--- /dev/null\n+++ b/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin\n@@ -0,0 +1,12 @@\n+org.spearce.jgit.pgm.DiffTree\n+org.spearce.jgit.pgm.Fetch\n+org.spearce.jgit.pgm.Glog\n+org.spearce.jgit.pgm.IndexPack\n+org.spearce.jgit.pgm.Log\n+org.spearce.jgit.pgm.LsRemote\n+org.spearce.jgit.pgm.LsTree\n+org.spearce.jgit.pgm.MergeBase\n+org.spearce.jgit.pgm.Push\n+org.spearce.jgit.pgm.RevList\n+org.spearce.jgit.pgm.ShowRev\n+org.spearce.jgit.pgm.Tag\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandCatalog.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandCatalog.java\nnew file mode 100644\nindex 0000000..13c585c\n--- /dev/null\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandCatalog.java\n@@ -0,0 +1,188 @@\n+/*\n+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+package org.spearce.jgit.pgm;\n+\n+import java.io.BufferedReader;\n+import java.io.IOException;\n+import java.io.InputStream;\n+import java.io.InputStreamReader;\n+import java.net.URL;\n+import java.util.ArrayList;\n+import java.util.Arrays;\n+import java.util.Collection;\n+import java.util.Comparator;\n+import java.util.Enumeration;\n+import java.util.HashMap;\n+import java.util.Map;\n+import java.util.Vector;\n+\n+/**\n+ * List of all commands known by jgit's command line tools.\n+ * <p>\n+ * Commands are implementations of {@link TextBuiltin}, with an optional\n+ * {@link Command} class annotation to insert additional documentation or\n+ * override the default command name (which is guessed from the class name).\n+ * <p>\n+ * Commands may be registered by adding them to a services file in the same JAR\n+ * (or classes directory) as the command implementation. The service file name\n+ * is <code>META-INF/services/org.spearce.jgit.pgm.TextBuiltin</code> and it\n+ * contains one concrete implementation class name per line.\n+ * <p>\n+ * Command registration is identical to Java 6's services, however the catalog\n+ * uses a lightweight wrapper to delay creating a command instance as much as\n+ * possible. This avoids initializing the AWT or SWT GUI toolkits even if the\n+ * command's constructor might require them.\n+ */\n+public class CommandCatalog {\n+\tprivate static final CommandCatalog INSTANCE = new CommandCatalog();\n+\n+\t/**\n+\t * Locate a single command by its user friendly name.\n+\t *\n+\t * @param name\n+\t *            name of the command. Typically in dash-lower-case-form, which\n+\t *            was derived from the DashLowerCaseForm class name.\n+\t * @return the command instance; null if no command exists by that name.\n+\t */\n+\tpublic static CommandRef get(final String name) {\n+\t\treturn INSTANCE.commands.get(name);\n+\t}\n+\n+\t/**\n+\t * @return all known commands, sorted by command name.\n+\t */\n+\tpublic static CommandRef[] all() {\n+\t\treturn toSortedArray(INSTANCE.commands.values());\n+\t}\n+\n+\t/**\n+\t * @return all common commands, sorted by command name.\n+\t */\n+\tpublic static CommandRef[] common() {\n+\t\tfinal ArrayList<CommandRef> common = new ArrayList<CommandRef>();\n+\t\tfor (final CommandRef c : INSTANCE.commands.values())\n+\t\t\tif (c.isCommon())\n+\t\t\t\tcommon.add(c);\n+\t\treturn toSortedArray(common);\n+\t}\n+\n+\tprivate static CommandRef[] toSortedArray(final Collection<CommandRef> c) {\n+\t\tfinal CommandRef[] r = c.toArray(new CommandRef[c.size()]);\n+\t\tArrays.sort(r, new Comparator<CommandRef>() {\n+\t\t\tpublic int compare(final CommandRef o1, final CommandRef o2) {\n+\t\t\t\treturn o1.getName().compareTo(o2.getName());\n+\t\t\t}\n+\t\t});\n+\t\treturn r;\n+\t}\n+\n+\tprivate final ClassLoader ldr;\n+\n+\tprivate final Map<String, CommandRef> commands;\n+\n+\tprivate CommandCatalog() {\n+\t\tldr = Thread.currentThread().getContextClassLoader();\n+\t\tcommands = new HashMap<String, CommandRef>();\n+\n+\t\tfinal Enumeration<URL> catalogs = catalogs();\n+\t\twhile (catalogs.hasMoreElements())\n+\t\t\tscan(catalogs.nextElement());\n+\t}\n+\n+\tprivate Enumeration<URL> catalogs() {\n+\t\ttry {\n+\t\t\tfinal String pfx = \"META-INF/services/\";\n+\t\t\treturn ldr.getResources(pfx + TextBuiltin.class.getName());\n+\t\t} catch (IOException err) {\n+\t\t\treturn new Vector<URL>().elements();\n+\t\t}\n+\t}\n+\n+\tprivate void scan(final URL cUrl) {\n+\t\tfinal BufferedReader cIn;\n+\t\ttry {\n+\t\t\tfinal InputStream in = cUrl.openStream();\n+\t\t\tcIn = new BufferedReader(new InputStreamReader(in, \"UTF-8\"));\n+\t\t} catch (IOException err) {\n+\t\t\t// If we cannot read from the service list, go to the next.\n+\t\t\t//\n+\t\t\treturn;\n+\t\t}\n+\n+\t\ttry {\n+\t\t\tString line;\n+\t\t\twhile ((line = cIn.readLine()) != null) {\n+\t\t\t\tif (line.length() > 0 && !line.startsWith(\"#\"))\n+\t\t\t\t\tload(line);\n+\t\t\t}\n+\t\t} catch (IOException err) {\n+\t\t\t// If we failed during a read, ignore the error.\n+\t\t\t//\n+\t\t} finally {\n+\t\t\ttry {\n+\t\t\t\tcIn.close();\n+\t\t\t} catch (IOException e) {\n+\t\t\t\t// Ignore the close error; we are only reading.\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tprivate void load(final String cn) {\n+\t\tfinal Class<? extends TextBuiltin> clazz;\n+\t\ttry {\n+\t\t\tclazz = Class.forName(cn, false, ldr).asSubclass(TextBuiltin.class);\n+\t\t} catch (ClassNotFoundException notBuiltin) {\n+\t\t\t// Doesn't exist, even though the service entry is present.\n+\t\t\t//\n+\t\t\treturn;\n+\t\t} catch (ClassCastException notBuiltin) {\n+\t\t\t// Isn't really a builtin, even though its listed as such.\n+\t\t\t//\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tfinal CommandRef cr;\n+\t\tfinal Command a = clazz.getAnnotation(Command.class);\n+\t\tif (a != null)\n+\t\t\tcr = new CommandRef(clazz, a);\n+\t\telse\n+\t\t\tcr = new CommandRef(clazz);\n+\n+\t\tcommands.put(cr.getName(), cr);\n+\t}\n+}\n-- \n1.6.0.rc0.182.gb96c7\n"},{"id":"84937","messageId":"1217015167-4680-7-git-send-email-spearce@spearce.org","threadId":"14681","inReplyTo":"1217015167-4680-6-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 6/9] Document some common commands with the new Command annotation","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-07-25T19:46:04Z","receivedAt":"2008-07-25T19:46:04Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"This way they are known to be common at runtime, by looking at\nthe annotation associated with the class instance.  Right now\nwe do not use these annotations but they will be useful soon.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/pgm/Fetch.java            |    1 +\n .../src/org/spearce/jgit/pgm/Log.java              |    1 +\n .../src/org/spearce/jgit/pgm/Push.java             |    1 +\n .../src/org/spearce/jgit/pgm/Tag.java              |    1 +\n 4 files changed, 4 insertions(+), 0 deletions(-)\n\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Fetch.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Fetch.java\nindex 194f669..dcad972 100644\n--- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Fetch.java\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Fetch.java\n@@ -47,6 +47,7 @@ import org.spearce.jgit.transport.RefSpec;\n import org.spearce.jgit.transport.TrackingRefUpdate;\n import org.spearce.jgit.transport.Transport;\n \n+@Command(common = true, usage = \"Update remote refs from another repository\")\n class Fetch extends TextBuiltin {\n \t@Argument(index = 0, metaVar = \"uri-ish\")\n \tprivate String remote = \"origin\";\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Log.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Log.java\nindex 780a63b..e16387b 100644\n--- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Log.java\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Log.java\n@@ -45,6 +45,7 @@ import java.util.TimeZone;\n import org.spearce.jgit.lib.PersonIdent;\n import org.spearce.jgit.revwalk.RevCommit;\n \n+@Command(common = true, usage = \"View commit history\")\n class Log extends RevWalkTextBuiltin {\n \tprivate final TimeZone myTZ = TimeZone.getDefault();\n \ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java\nindex df6c664..6b35ab8 100644\n--- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Push.java\n@@ -51,6 +51,7 @@ import org.spearce.jgit.transport.RemoteRefUpdate;\n import org.spearce.jgit.transport.Transport;\n import org.spearce.jgit.transport.RemoteRefUpdate.Status;\n \n+@Command(common = true, usage = \"Update remote repository from local refs\")\n class Push extends TextBuiltin {\n \t@Argument(index = 0, metaVar = \"uri-ish\")\n \tprivate String remote = \"origin\";\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Tag.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Tag.java\nindex a7bd037..6c73552 100644\n--- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Tag.java\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Tag.java\n@@ -45,6 +45,7 @@ import org.spearce.jgit.lib.ObjectId;\n import org.spearce.jgit.lib.ObjectLoader;\n import org.spearce.jgit.lib.PersonIdent;\n \n+@Command(common = true, usage = \"Create a tag\")\n class Tag extends TextBuiltin {\n \t@Option(name = \"-f\", usage = \"force replacing an existing tag\")\n \tprivate boolean force;\n-- \n1.6.0.rc0.182.gb96c7\n"},{"id":"84939","messageId":"1217015167-4680-8-git-send-email-spearce@spearce.org","threadId":"14681","inReplyTo":"1217015167-4680-7-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 7/9] Include commonly used commands in main help output","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-07-25T19:46:05Z","receivedAt":"2008-07-25T19:46:05Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"If the main loop did not get a subcommand during parsing of the\ncommand line then we should offer up a list of commonly used\ncommands and their one-line usage summary, to help the user make\na decision about which command they should try to execute.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/pgm/Main.java             |   18 ++++++++++++++++++\n 1 files changed, 18 insertions(+), 0 deletions(-)\n\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Main.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Main.java\nindex c069989..c8bade8 100644\n--- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Main.java\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Main.java\n@@ -121,6 +121,24 @@ public class Main {\n \t\t\t\tSystem.err.println();\n \t\t\t\tclp.printUsage(System.err);\n \t\t\t\tSystem.err.println();\n+\t\t\t} else if (subcommand == null) {\n+\t\t\t\tSystem.err.println();\n+\t\t\t\tSystem.err.println(\"The most commonly used commands are:\");\n+\t\t\t\tfinal CommandRef[] common = CommandCatalog.common();\n+\t\t\t\tint width = 0;\n+\t\t\t\tfor (final CommandRef c : common)\n+\t\t\t\t\twidth = Math.max(width, c.getName().length());\n+\t\t\t\twidth += 2;\n+\n+\t\t\t\tfor (final CommandRef c : common) {\n+\t\t\t\t\tSystem.err.print(' ');\n+\t\t\t\t\tSystem.err.print(c.getName());\n+\t\t\t\t\tfor (int i = c.getName().length(); i < width; i++)\n+\t\t\t\t\t\tSystem.err.print(' ');\n+\t\t\t\t\tSystem.err.print(c.getUsage());\n+\t\t\t\t\tSystem.err.println();\n+\t\t\t\t}\n+\t\t\t\tSystem.err.println();\n \t\t\t}\n \t\t\tSystem.exit(1);\n \t\t}\n-- \n1.6.0.rc0.182.gb96c7\n"},{"id":"84934","messageId":"1217015167-4680-9-git-send-email-spearce@spearce.org","threadId":"14681","inReplyTo":"1217015167-4680-8-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 8/9] Refactor SubcommandHandler to use CommandCatalog instead of reflection","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-07-25T19:46:06Z","receivedAt":"2008-07-25T19:46:06Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Now that all commands are known to the CommandCatalog we do not need\nto perform direct reflection inside of the SubcommandHandler.  Instead\nwe can reuse the lookup table already known to the CommandCatalog.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/pgm/TextBuiltin.java      |    8 +--\n .../spearce/jgit/pgm/opt/SubcommandHandler.java    |   65 ++------------------\n 2 files changed, 6 insertions(+), 67 deletions(-)\n\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/TextBuiltin.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/TextBuiltin.java\nindex e2eef84..5c066cb 100644\n--- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/TextBuiltin.java\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/TextBuiltin.java\n@@ -83,13 +83,7 @@ public abstract class TextBuiltin {\n \t/** RevWalk used during command line parsing, if it was required. */\n \tprotected RevWalk argWalk;\n \n-\t/**\n-\t * Set the name this command can be invoked as on the command line.\n-\t *\n-\t * @param name\n-\t *            the name of the command.\n-\t */\n-\tpublic void setCommandName(final String name) {\n+\tfinal void setCommandName(final String name) {\n \t\tcommandName = name;\n \t}\n \ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/opt/SubcommandHandler.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/opt/SubcommandHandler.java\nindex c7e1bf6..86004bb 100644\n--- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/opt/SubcommandHandler.java\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/opt/SubcommandHandler.java\n@@ -37,8 +37,6 @@\n \n package org.spearce.jgit.pgm.opt;\n \n-import java.lang.reflect.Constructor;\n-import java.lang.reflect.InvocationTargetException;\n import java.text.MessageFormat;\n \n import org.kohsuke.args4j.CmdLineException;\n@@ -47,7 +45,8 @@ import org.kohsuke.args4j.OptionDef;\n import org.kohsuke.args4j.spi.OptionHandler;\n import org.kohsuke.args4j.spi.Parameters;\n import org.kohsuke.args4j.spi.Setter;\n-import org.spearce.jgit.pgm.Main;\n+import org.spearce.jgit.pgm.CommandCatalog;\n+import org.spearce.jgit.pgm.CommandRef;\n import org.spearce.jgit.pgm.TextBuiltin;\n \n /**\n@@ -57,12 +56,6 @@ import org.spearce.jgit.pgm.TextBuiltin;\n  * we can execute at runtime with the remaining arguments of the parser.\n  */\n public class SubcommandHandler extends OptionHandler<TextBuiltin> {\n-\tprivate static String mypackage() {\n-\t\tfinal String p = Main.class.getName();\n-\t\tfinal int dot = p.lastIndexOf('.');\n-\t\treturn p.substring(0, dot);\n-\t}\n-\n \t/**\n \t * Create a new handler for the command name.\n \t * <p>\n@@ -80,65 +73,17 @@ public class SubcommandHandler extends OptionHandler<TextBuiltin> {\n \t@Override\n \tpublic int parseArguments(final Parameters params) throws CmdLineException {\n \t\tfinal String name = params.getParameter(0);\n-\t\tfinal StringBuilder s = new StringBuilder();\n-\t\ts.append(mypackage());\n-\t\ts.append('.');\n-\t\tboolean upnext = true;\n-\t\tfor (int i = 0; i < name.length(); i++) {\n-\t\t\tfinal char c = name.charAt(i);\n-\t\t\tif (c == '-') {\n-\t\t\t\tupnext = true;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (upnext)\n-\t\t\t\ts.append(Character.toUpperCase(c));\n-\t\t\telse\n-\t\t\t\ts.append(c);\n-\t\t\tupnext = false;\n-\t\t}\n-\n-\t\tfinal Class<?> clazz;\n-\t\ttry {\n-\t\t\tclazz = Class.forName(s.toString());\n-\t\t} catch (ClassNotFoundException e) {\n-\t\t\tthrow new CmdLineException(MessageFormat.format(\n-\t\t\t\t\t\"{0} is not a jgit command\", name));\n-\t\t}\n-\n-\t\tif (!TextBuiltin.class.isAssignableFrom(clazz))\n+\t\tfinal CommandRef cr = CommandCatalog.get(name);\n+\t\tif (cr == null)\n \t\t\tthrow new CmdLineException(MessageFormat.format(\n \t\t\t\t\t\"{0} is not a jgit command\", name));\n \n-\t\tfinal Constructor<?> cons;\n-\t\ttry {\n-\t\t\tcons = clazz.getDeclaredConstructor();\n-\t\t} catch (SecurityException e) {\n-\t\t\tthrow new CmdLineException(\"Cannot create \" + name, e);\n-\t\t} catch (NoSuchMethodException e) {\n-\t\t\tthrow new CmdLineException(\"Cannot create \" + name, e);\n-\t\t}\n-\t\tcons.setAccessible(true);\n-\n-\t\tfinal TextBuiltin cmd;\n-\t\ttry {\n-\t\t\tcmd = (TextBuiltin) cons.newInstance();\n-\t\t} catch (InstantiationException e) {\n-\t\t\tthrow new CmdLineException(\"Cannot create \" + name, e);\n-\t\t} catch (IllegalAccessException e) {\n-\t\t\tthrow new CmdLineException(\"Cannot create \" + name, e);\n-\t\t} catch (InvocationTargetException e) {\n-\t\t\tthrow new CmdLineException(\"Cannot create \" + name, e);\n-\t\t}\n-\n-\t\tcmd.setCommandName(name);\n-\t\tsetter.addValue(cmd);\n-\n \t\t// Force option parsing to stop. Everything after us should\n \t\t// be arguments known only to this command and must not be\n \t\t// recognized by the current parser.\n \t\t//\n \t\towner.stopOptionParsing();\n-\n+\t\tsetter.addValue(cr.create());\n \t\treturn 1;\n \t}\n \n-- \n1.6.0.rc0.182.gb96c7\n"},{"id":"84936","messageId":"1217015167-4680-10-git-send-email-spearce@spearce.org","threadId":"14681","inReplyTo":"1217015167-4680-9-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 9/9] Add debug-show-commands to display the command table","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-07-25T19:46:07Z","receivedAt":"2008-07-25T19:46:07Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"This can be useful to debug the command catalog.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../services/org.spearce.jgit.pgm.TextBuiltin      |    2 +\n .../src/org/spearce/jgit/pgm/CommandRef.java       |    7 ++\n .../org/spearce/jgit/pgm/debug/ShowCommands.java   |   78 ++++++++++++++++++++\n 3 files changed, 87 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/ShowCommands.java\n\ndiff --git a/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin b/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin\nindex 69ef046..1ff5a30 100644\n--- a/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin\n+++ b/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin\n@@ -10,3 +10,5 @@ org.spearce.jgit.pgm.Push\n org.spearce.jgit.pgm.RevList\n org.spearce.jgit.pgm.ShowRev\n org.spearce.jgit.pgm.Tag\n+\n+org.spearce.jgit.pgm.debug.ShowCommands\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandRef.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandRef.java\nindex 40400a7..8bf784b 100644\n--- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandRef.java\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandRef.java\n@@ -120,6 +120,13 @@ public class CommandRef {\n \t}\n \n \t/**\n+\t * @return loader for {@link #getImplementationClassName()}.\n+\t */\n+\tpublic ClassLoader getImplementationClassLoader() {\n+\t\treturn impl.getClassLoader();\n+\t}\n+\n+\t/**\n \t * @return a new instance of the command implementation.\n \t */\n \tpublic TextBuiltin create() {\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/ShowCommands.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/ShowCommands.java\nnew file mode 100644\nindex 0000000..64ecf7f\n--- /dev/null\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/ShowCommands.java\n@@ -0,0 +1,78 @@\n+package org.spearce.jgit.pgm.debug;\n+\n+import java.net.URL;\n+\n+import org.kohsuke.args4j.Option;\n+import org.spearce.jgit.pgm.Command;\n+import org.spearce.jgit.pgm.CommandCatalog;\n+import org.spearce.jgit.pgm.CommandRef;\n+import org.spearce.jgit.pgm.TextBuiltin;\n+\n+@Command(usage = \"Display a list of all registered jgit commands\")\n+class ShowCommands extends TextBuiltin {\n+\t@Option(name = \"--pretty\", usage = \"alter the detail shown\")\n+\tprivate Format pretty = Format.USAGE;\n+\n+\t@Override\n+\tprotected void run() throws Exception {\n+\t\tfinal CommandRef[] list = CommandCatalog.all();\n+\n+\t\tint width = 0;\n+\t\tfor (final CommandRef c : list)\n+\t\t\twidth = Math.max(width, c.getName().length());\n+\t\twidth += 2;\n+\n+\t\tfor (final CommandRef c : list) {\n+\t\t\tSystem.err.print(c.isCommon() ? '*' : ' ');\n+\t\t\tSystem.err.print(' ');\n+\n+\t\t\tSystem.err.print(c.getName());\n+\t\t\tfor (int i = c.getName().length(); i < width; i++)\n+\t\t\t\tSystem.err.print(' ');\n+\n+\t\t\tpretty.print(c);\n+\t\t\tSystem.err.println();\n+\t\t}\n+\t\tSystem.err.println();\n+\t}\n+\n+\tstatic enum Format {\n+\t\t/** */\n+\t\tUSAGE {\n+\t\t\tvoid print(final CommandRef c) {\n+\t\t\t\tSystem.err.print(c.getUsage());\n+\t\t\t}\n+\t\t},\n+\n+\t\t/** */\n+\t\tCLASSES {\n+\t\t\tvoid print(final CommandRef c) {\n+\t\t\t\tSystem.err.print(c.getImplementationClassName());\n+\t\t\t}\n+\t\t},\n+\n+\t\t/** */\n+\t\tURLS {\n+\t\t\tvoid print(final CommandRef c) {\n+\t\t\t\tfinal ClassLoader ldr = c.getImplementationClassLoader();\n+\n+\t\t\t\tString cn = c.getImplementationClassName();\n+\t\t\t\tcn = cn.replace('.', '/') + \".class\";\n+\n+\t\t\t\tfinal URL url = ldr.getResource(cn);\n+\t\t\t\tif (url == null) {\n+\t\t\t\t\tSystem.err.print(\"!! NOT FOUND !!\");\n+\t\t\t\t\treturn;\n+\t\t\t\t}\n+\n+\t\t\t\tString rn = url.toExternalForm();\n+\t\t\t\tif (rn.endsWith(cn))\n+\t\t\t\t\trn = rn.substring(0, rn.length() - cn.length());\n+\n+\t\t\t\tSystem.err.print(rn);\n+\t\t\t}\n+\t\t};\n+\n+\t\tabstract void print(CommandRef c);\n+\t}\n+}\n-- \n1.6.0.rc0.182.gb96c7\n"},{"id":"84940","messageId":"20080725195211.GF21117@spearce.org","threadId":"14681","inReplyTo":"1217015167-4680-1-git-send-email-spearce@spearce.org","subject":"Re: [JGIT PATCH 0/9] List commonly used (or recognized) commands","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-07-25T19:52:11Z","receivedAt":"2008-07-25T19:52:11Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"\"Shawn O. Pearce\" <spearce@spearce.org> wrote:\n> This series adds support to jgit to list commonly used subcommands\n> if the user just executes `jgit` with no subcommand requested:\n> \n>   $ jgit\n>   jgit --git-dir GIT_DIR --help (-h) --show-stack-trace command [ARG ...]\n>   \n>   The most commonly used commands are:\n>    fetch  Update remote refs from another repository\n>    log    View commit history\n>    push   Update remote repository from local refs\n>    tag    Create a tag\n\nScratch that.  This series is busted if you install jgit and actually\ntry to use it.  No subcommands get registered.  I suspect it is due\nto the shell script+ZIP file we have in the CLASSPATH confusing the\nJRE and making it impossible to read correctly.\n\n-- \nShawn.\n"},{"id":"84942","messageId":"20080725200246.GA23117@spearce.org","threadId":"14681","inReplyTo":"1217015167-4680-6-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 5/9 v2] Create a catalog of CommandRefs for lookup and enumeration","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-07-25T20:02:46Z","receivedAt":"2008-07-25T20:02:46Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"The command catalog supports enumerating commands registered through\na services list, converting each entry into a CommandRef and making\nthat available to callers on demand.  The CommandRef can be later used\nto create a command instance or just to obtain documentation about it.\n\nAll current commands are listed in the service registration.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n\n All that was missing was the services file entry in the built JAR.\n With this replacement for 5/9 the series should be fine.\n\n make_jgit.sh                                       |    1 +\n .../services/org.spearce.jgit.pgm.TextBuiltin      |   12 ++\n .../src/org/spearce/jgit/pgm/CommandCatalog.java   |  188 ++++++++++++++++++++\n 3 files changed, 201 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin\n create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandCatalog.java\n\ndiff --git a/make_jgit.sh b/make_jgit.sh\nindex bcb2df0..89df80c 100755\n--- a/make_jgit.sh\n+++ b/make_jgit.sh\n@@ -71,6 +71,7 @@ sed s/@@use_self@@/1/ jgit.sh >$O+ &&\n java org.spearce.jgit.pgm.build.JarLinkUtil \\\n \t`for p in $JARS   ; do printf %s \" -include $p\"     ;done` \\\n \t`for p in $PLUGINS; do printf %s \" -include $p/bin2\";done` \\\n+\t-file META-INF/services/org.spearce.jgit.pgm.TextBuiltin=org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin \\\n \t-file META-INF/MANIFEST.MF=$T_MF \\\n \t>>$O+ &&\n chmod 555 $O+ &&\ndiff --git a/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin b/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin\nnew file mode 100644\nindex 0000000..69ef046\n--- /dev/null\n+++ b/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin\n@@ -0,0 +1,12 @@\n+org.spearce.jgit.pgm.DiffTree\n+org.spearce.jgit.pgm.Fetch\n+org.spearce.jgit.pgm.Glog\n+org.spearce.jgit.pgm.IndexPack\n+org.spearce.jgit.pgm.Log\n+org.spearce.jgit.pgm.LsRemote\n+org.spearce.jgit.pgm.LsTree\n+org.spearce.jgit.pgm.MergeBase\n+org.spearce.jgit.pgm.Push\n+org.spearce.jgit.pgm.RevList\n+org.spearce.jgit.pgm.ShowRev\n+org.spearce.jgit.pgm.Tag\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandCatalog.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandCatalog.java\nnew file mode 100644\nindex 0000000..13c585c\n--- /dev/null\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/CommandCatalog.java\n@@ -0,0 +1,188 @@\n+/*\n+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+package org.spearce.jgit.pgm;\n+\n+import java.io.BufferedReader;\n+import java.io.IOException;\n+import java.io.InputStream;\n+import java.io.InputStreamReader;\n+import java.net.URL;\n+import java.util.ArrayList;\n+import java.util.Arrays;\n+import java.util.Collection;\n+import java.util.Comparator;\n+import java.util.Enumeration;\n+import java.util.HashMap;\n+import java.util.Map;\n+import java.util.Vector;\n+\n+/**\n+ * List of all commands known by jgit's command line tools.\n+ * <p>\n+ * Commands are implementations of {@link TextBuiltin}, with an optional\n+ * {@link Command} class annotation to insert additional documentation or\n+ * override the default command name (which is guessed from the class name).\n+ * <p>\n+ * Commands may be registered by adding them to a services file in the same JAR\n+ * (or classes directory) as the command implementation. The service file name\n+ * is <code>META-INF/services/org.spearce.jgit.pgm.TextBuiltin</code> and it\n+ * contains one concrete implementation class name per line.\n+ * <p>\n+ * Command registration is identical to Java 6's services, however the catalog\n+ * uses a lightweight wrapper to delay creating a command instance as much as\n+ * possible. This avoids initializing the AWT or SWT GUI toolkits even if the\n+ * command's constructor might require them.\n+ */\n+public class CommandCatalog {\n+\tprivate static final CommandCatalog INSTANCE = new CommandCatalog();\n+\n+\t/**\n+\t * Locate a single command by its user friendly name.\n+\t *\n+\t * @param name\n+\t *            name of the command. Typically in dash-lower-case-form, which\n+\t *            was derived from the DashLowerCaseForm class name.\n+\t * @return the command instance; null if no command exists by that name.\n+\t */\n+\tpublic static CommandRef get(final String name) {\n+\t\treturn INSTANCE.commands.get(name);\n+\t}\n+\n+\t/**\n+\t * @return all known commands, sorted by command name.\n+\t */\n+\tpublic static CommandRef[] all() {\n+\t\treturn toSortedArray(INSTANCE.commands.values());\n+\t}\n+\n+\t/**\n+\t * @return all common commands, sorted by command name.\n+\t */\n+\tpublic static CommandRef[] common() {\n+\t\tfinal ArrayList<CommandRef> common = new ArrayList<CommandRef>();\n+\t\tfor (final CommandRef c : INSTANCE.commands.values())\n+\t\t\tif (c.isCommon())\n+\t\t\t\tcommon.add(c);\n+\t\treturn toSortedArray(common);\n+\t}\n+\n+\tprivate static CommandRef[] toSortedArray(final Collection<CommandRef> c) {\n+\t\tfinal CommandRef[] r = c.toArray(new CommandRef[c.size()]);\n+\t\tArrays.sort(r, new Comparator<CommandRef>() {\n+\t\t\tpublic int compare(final CommandRef o1, final CommandRef o2) {\n+\t\t\t\treturn o1.getName().compareTo(o2.getName());\n+\t\t\t}\n+\t\t});\n+\t\treturn r;\n+\t}\n+\n+\tprivate final ClassLoader ldr;\n+\n+\tprivate final Map<String, CommandRef> commands;\n+\n+\tprivate CommandCatalog() {\n+\t\tldr = Thread.currentThread().getContextClassLoader();\n+\t\tcommands = new HashMap<String, CommandRef>();\n+\n+\t\tfinal Enumeration<URL> catalogs = catalogs();\n+\t\twhile (catalogs.hasMoreElements())\n+\t\t\tscan(catalogs.nextElement());\n+\t}\n+\n+\tprivate Enumeration<URL> catalogs() {\n+\t\ttry {\n+\t\t\tfinal String pfx = \"META-INF/services/\";\n+\t\t\treturn ldr.getResources(pfx + TextBuiltin.class.getName());\n+\t\t} catch (IOException err) {\n+\t\t\treturn new Vector<URL>().elements();\n+\t\t}\n+\t}\n+\n+\tprivate void scan(final URL cUrl) {\n+\t\tfinal BufferedReader cIn;\n+\t\ttry {\n+\t\t\tfinal InputStream in = cUrl.openStream();\n+\t\t\tcIn = new BufferedReader(new InputStreamReader(in, \"UTF-8\"));\n+\t\t} catch (IOException err) {\n+\t\t\t// If we cannot read from the service list, go to the next.\n+\t\t\t//\n+\t\t\treturn;\n+\t\t}\n+\n+\t\ttry {\n+\t\t\tString line;\n+\t\t\twhile ((line = cIn.readLine()) != null) {\n+\t\t\t\tif (line.length() > 0 && !line.startsWith(\"#\"))\n+\t\t\t\t\tload(line);\n+\t\t\t}\n+\t\t} catch (IOException err) {\n+\t\t\t// If we failed during a read, ignore the error.\n+\t\t\t//\n+\t\t} finally {\n+\t\t\ttry {\n+\t\t\t\tcIn.close();\n+\t\t\t} catch (IOException e) {\n+\t\t\t\t// Ignore the close error; we are only reading.\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tprivate void load(final String cn) {\n+\t\tfinal Class<? extends TextBuiltin> clazz;\n+\t\ttry {\n+\t\t\tclazz = Class.forName(cn, false, ldr).asSubclass(TextBuiltin.class);\n+\t\t} catch (ClassNotFoundException notBuiltin) {\n+\t\t\t// Doesn't exist, even though the service entry is present.\n+\t\t\t//\n+\t\t\treturn;\n+\t\t} catch (ClassCastException notBuiltin) {\n+\t\t\t// Isn't really a builtin, even though its listed as such.\n+\t\t\t//\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tfinal CommandRef cr;\n+\t\tfinal Command a = clazz.getAnnotation(Command.class);\n+\t\tif (a != null)\n+\t\t\tcr = new CommandRef(clazz, a);\n+\t\telse\n+\t\t\tcr = new CommandRef(clazz);\n+\n+\t\tcommands.put(cr.getName(), cr);\n+\t}\n+}\n-- \n1.6.0.rc0.182.gb96c7\n"}]}