CLIGitCommand.java

  1. /*
  2.  * Copyright (C) 2011-2012, IBM Corporation and others. and others
  3.  *
  4.  * This program and the accompanying materials are made available under the
  5.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  6.  * https://www.eclipse.org/org/documents/edl-v10.php.
  7.  *
  8.  * SPDX-License-Identifier: BSD-3-Clause
  9.  */
  10. package org.eclipse.jgit.pgm;

  11. import static java.nio.charset.StandardCharsets.UTF_8;
  12. import static org.junit.Assert.assertNull;

  13. import java.io.ByteArrayOutputStream;
  14. import java.io.File;
  15. import java.io.IOException;
  16. import java.io.OutputStreamWriter;
  17. import java.io.PrintWriter;
  18. import java.util.ArrayList;
  19. import java.util.List;

  20. import org.eclipse.jgit.internal.storage.file.FileRepository;
  21. import org.eclipse.jgit.lib.Repository;
  22. import org.eclipse.jgit.pgm.TextBuiltin.TerminatedByHelpException;
  23. import org.eclipse.jgit.util.IO;

  24. public class CLIGitCommand extends Main {

  25.     private final Result result;

  26.     private final Repository db;

  27.     public CLIGitCommand(Repository db) {
  28.         super();
  29.         this.db = db;
  30.         result = new Result();
  31.     }

  32.     /**
  33.      * Executes git commands (with arguments) specified on the command line. The
  34.      * git repository (same for all commands) can be specified via system
  35.      * property "-Dgit_work_tree=path_to_work_tree". If the property is not set,
  36.      * current directory is used.
  37.      *
  38.      * @param args
  39.      *            each element in the array must be a valid git command line,
  40.      *            e.g. "git branch -h"
  41.      * @throws Exception
  42.      */
  43.     public static void main(String[] args) throws Exception {
  44.         String workDir = System.getProperty("git_work_tree");
  45.         if (workDir == null) {
  46.             workDir = ".";
  47.             System.out.println(
  48.                     "System property 'git_work_tree' not specified, using current directory: "
  49.                             + new File(workDir).getAbsolutePath());
  50.         }
  51.         try (Repository db = new FileRepository(workDir + "/.git")) {
  52.             for (String cmd : args) {
  53.                 List<String> result = execute(cmd, db);
  54.                 for (String line : result) {
  55.                     System.out.println(line);
  56.                 }
  57.             }
  58.         }
  59.     }

  60.     public static List<String> execute(String str, Repository db)
  61.             throws Exception {
  62.         Result result = executeRaw(str, db);
  63.         return getOutput(result);
  64.     }

  65.     public static Result executeRaw(String str, Repository db)
  66.             throws Exception {
  67.         CLIGitCommand cmd = new CLIGitCommand(db);
  68.         cmd.run(str);
  69.         return cmd.result;
  70.     }

  71.     public static List<String> executeUnchecked(String str, Repository db)
  72.             throws Exception {
  73.         CLIGitCommand cmd = new CLIGitCommand(db);
  74.         try {
  75.             cmd.run(str);
  76.             return getOutput(cmd.result);
  77.         } catch (Throwable e) {
  78.             return cmd.result.errLines();
  79.         }
  80.     }

  81.     private static List<String> getOutput(Result result) {
  82.         if (result.ex instanceof TerminatedByHelpException) {
  83.             return result.errLines();
  84.         }
  85.         return result.outLines();
  86.     }

  87.     private void run(String commandLine) throws Exception {
  88.         String[] argv = convertToMainArgs(commandLine);
  89.         try {
  90.             super.run(argv);
  91.         } catch (TerminatedByHelpException e) {
  92.             // this is not a failure, super called exit() on help
  93.         } finally {
  94.             writer.flush();
  95.         }
  96.     }

  97.     private static String[] convertToMainArgs(String str)
  98.             throws Exception {
  99.         String[] args = split(str);
  100.         if (!args[0].equalsIgnoreCase("git") || args.length < 2) {
  101.             throw new IllegalArgumentException(
  102.                     "Expected 'git <command> [<args>]', was:" + str);
  103.         }
  104.         String[] argv = new String[args.length - 1];
  105.         System.arraycopy(args, 1, argv, 0, args.length - 1);
  106.         return argv;
  107.     }

  108.     @Override
  109.     PrintWriter createErrorWriter() {
  110.         return new PrintWriter(new OutputStreamWriter(
  111.                 result.err, UTF_8));
  112.     }

  113.     @Override
  114.     void init(TextBuiltin cmd) throws IOException {
  115.         cmd.outs = result.out;
  116.         cmd.errs = result.err;
  117.         super.init(cmd);
  118.     }

  119.     @Override
  120.     protected Repository openGitDir(String aGitdir) throws IOException {
  121.         assertNull(aGitdir);
  122.         return db;
  123.     }

  124.     @Override
  125.     void exit(int status, Exception t) throws Exception {
  126.         if (t == null) {
  127.             t = new IllegalStateException(Integer.toString(status));
  128.         }
  129.         result.ex = t;
  130.         throw t;
  131.     }

  132.     /**
  133.      * Split a command line into a string array.
  134.      *
  135.      * A copy of Gerrit's
  136.      * com.google.gerrit.sshd.CommandFactoryProvider#split(String)
  137.      *
  138.      * @param commandLine
  139.      *            a command line
  140.      * @return the array
  141.      */
  142.     static String[] split(String commandLine) {
  143.         final List<String> list = new ArrayList<>();
  144.         boolean inquote = false;
  145.         boolean inDblQuote = false;
  146.         StringBuilder r = new StringBuilder();
  147.         for (int ip = 0; ip < commandLine.length();) {
  148.             final char b = commandLine.charAt(ip++);
  149.             switch (b) {
  150.             case '\t':
  151.             case ' ':
  152.                 if (inquote || inDblQuote)
  153.                     r.append(b);
  154.                 else if (r.length() > 0) {
  155.                     list.add(r.toString());
  156.                     r = new StringBuilder();
  157.                 }
  158.                 continue;
  159.             case '\"':
  160.                 if (inquote)
  161.                     r.append(b);
  162.                 else
  163.                     inDblQuote = !inDblQuote;
  164.                 continue;
  165.             case '\'':
  166.                 if (inDblQuote)
  167.                     r.append(b);
  168.                 else
  169.                     inquote = !inquote;
  170.                 continue;
  171.             case '\\':
  172.                 if (inDblQuote || inquote || ip == commandLine.length())
  173.                     r.append(b); // literal within a quote
  174.                 else
  175.                     r.append(commandLine.charAt(ip++));
  176.                 continue;
  177.             default:
  178.                 r.append(b);
  179.                 continue;
  180.             }
  181.         }
  182.         if (r.length() > 0)
  183.             list.add(r.toString());
  184.         return list.toArray(new String[0]);
  185.     }

  186.     public static class Result {
  187.         public final ByteArrayOutputStream out = new ByteArrayOutputStream();

  188.         public final ByteArrayOutputStream err = new ByteArrayOutputStream();

  189.         public Exception ex;

  190.         public byte[] outBytes() {
  191.             return out.toByteArray();
  192.         }

  193.         public byte[] errBytes() {
  194.             return err.toByteArray();
  195.         }

  196.         public String outString() {
  197.             return new String(out.toByteArray(), UTF_8);
  198.         }

  199.         public List<String> outLines() {
  200.             return IO.readLines(outString());
  201.         }

  202.         public String errString() {
  203.             return new String(err.toByteArray(), UTF_8);
  204.         }

  205.         public List<String> errLines() {
  206.             return IO.readLines(errString());
  207.         }
  208.     }

  209. }