View Javadoc
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  
12  import static java.nio.charset.StandardCharsets.UTF_8;
13  import static org.junit.Assert.assertNull;
14  
15  import java.io.ByteArrayOutputStream;
16  import java.io.File;
17  import java.io.IOException;
18  import java.io.OutputStreamWriter;
19  import java.io.PrintWriter;
20  import java.util.ArrayList;
21  import java.util.List;
22  
23  import org.eclipse.jgit.internal.storage.file.FileRepository;
24  import org.eclipse.jgit.lib.Repository;
25  import org.eclipse.jgit.pgm.TextBuiltin.TerminatedByHelpException;
26  import org.eclipse.jgit.util.IO;
27  
28  public class CLIGitCommand extends Main {
29  
30  	private final Result result;
31  
32  	private final Repository db;
33  
34  	public CLIGitCommand(Repository db) {
35  		super();
36  		this.db = db;
37  		result = new Result();
38  	}
39  
40  	/**
41  	 * Executes git commands (with arguments) specified on the command line. The
42  	 * git repository (same for all commands) can be specified via system
43  	 * property "-Dgit_work_tree=path_to_work_tree". If the property is not set,
44  	 * current directory is used.
45  	 *
46  	 * @param args
47  	 *            each element in the array must be a valid git command line,
48  	 *            e.g. "git branch -h"
49  	 * @throws Exception
50  	 */
51  	public static void main(String[] args) throws Exception {
52  		String workDir = System.getProperty("git_work_tree");
53  		if (workDir == null) {
54  			workDir = ".";
55  			System.out.println(
56  					"System property 'git_work_tree' not specified, using current directory: "
57  							+ new File(workDir).getAbsolutePath());
58  		}
59  		try (Repository db = new FileRepository(workDir + "/.git")) {
60  			for (String cmd : args) {
61  				List<String> result = execute(cmd, db);
62  				for (String line : result) {
63  					System.out.println(line);
64  				}
65  			}
66  		}
67  	}
68  
69  	public static List<String> execute(String str, Repository db)
70  			throws Exception {
71  		Result result = executeRaw(str, db);
72  		return getOutput(result);
73  	}
74  
75  	public static Result executeRaw(String str, Repository db)
76  			throws Exception {
77  		CLIGitCommand cmd = new CLIGitCommand(db);
78  		cmd.run(str);
79  		return cmd.result;
80  	}
81  
82  	public static List<String> executeUnchecked(String str, Repository db)
83  			throws Exception {
84  		CLIGitCommand cmd = new CLIGitCommand(db);
85  		try {
86  			cmd.run(str);
87  			return getOutput(cmd.result);
88  		} catch (Throwable e) {
89  			return cmd.result.errLines();
90  		}
91  	}
92  
93  	private static List<String> getOutput(Result result) {
94  		if (result.ex instanceof TerminatedByHelpException) {
95  			return result.errLines();
96  		}
97  		return result.outLines();
98  	}
99  
100 	private void run(String commandLine) throws Exception {
101 		String[] argv = convertToMainArgs(commandLine);
102 		try {
103 			super.run(argv);
104 		} catch (TerminatedByHelpException e) {
105 			// this is not a failure, super called exit() on help
106 		} finally {
107 			writer.flush();
108 		}
109 	}
110 
111 	private static String[] convertToMainArgs(String str)
112 			throws Exception {
113 		String[] args = split(str);
114 		if (!args[0].equalsIgnoreCase("git") || args.length < 2) {
115 			throw new IllegalArgumentException(
116 					"Expected 'git <command> [<args>]', was:" + str);
117 		}
118 		String[] argv = new String[args.length - 1];
119 		System.arraycopy(args, 1, argv, 0, args.length - 1);
120 		return argv;
121 	}
122 
123 	@Override
124 	PrintWriter createErrorWriter() {
125 		return new PrintWriter(new OutputStreamWriter(
126 				result.err, UTF_8));
127 	}
128 
129 	@Override
130 	void init(TextBuiltin cmd) throws IOException {
131 		cmd.outs = result.out;
132 		cmd.errs = result.err;
133 		super.init(cmd);
134 	}
135 
136 	@Override
137 	protected Repository openGitDir(String aGitdir) throws IOException {
138 		assertNull(aGitdir);
139 		return db;
140 	}
141 
142 	@Override
143 	void exit(int status, Exception t) throws Exception {
144 		if (t == null) {
145 			t = new IllegalStateException(Integer.toString(status));
146 		}
147 		result.ex = t;
148 		throw t;
149 	}
150 
151 	/**
152 	 * Split a command line into a string array.
153 	 *
154 	 * A copy of Gerrit's
155 	 * com.google.gerrit.sshd.CommandFactoryProvider#split(String)
156 	 *
157 	 * @param commandLine
158 	 *            a command line
159 	 * @return the array
160 	 */
161 	static String[] split(String commandLine) {
162 		final List<String> list = new ArrayList<>();
163 		boolean inquote = false;
164 		boolean inDblQuote = false;
165 		StringBuilder r = new StringBuilder();
166 		for (int ip = 0; ip < commandLine.length();) {
167 			final char b = commandLine.charAt(ip++);
168 			switch (b) {
169 			case '\t':
170 			case ' ':
171 				if (inquote || inDblQuote)
172 					r.append(b);
173 				else if (r.length() > 0) {
174 					list.add(r.toString());
175 					r = new StringBuilder();
176 				}
177 				continue;
178 			case '\"':
179 				if (inquote)
180 					r.append(b);
181 				else
182 					inDblQuote = !inDblQuote;
183 				continue;
184 			case '\'':
185 				if (inDblQuote)
186 					r.append(b);
187 				else
188 					inquote = !inquote;
189 				continue;
190 			case '\\':
191 				if (inDblQuote || inquote || ip == commandLine.length())
192 					r.append(b); // literal within a quote
193 				else
194 					r.append(commandLine.charAt(ip++));
195 				continue;
196 			default:
197 				r.append(b);
198 				continue;
199 			}
200 		}
201 		if (r.length() > 0)
202 			list.add(r.toString());
203 		return list.toArray(new String[0]);
204 	}
205 
206 	public static class Result {
207 		public final ByteArrayOutputStream out = new ByteArrayOutputStream();
208 
209 		public final ByteArrayOutputStream err = new ByteArrayOutputStream();
210 
211 		public Exception ex;
212 
213 		public byte[] outBytes() {
214 			return out.toByteArray();
215 		}
216 
217 		public byte[] errBytes() {
218 			return err.toByteArray();
219 		}
220 
221 		public String outString() {
222 			return new String(out.toByteArray(), UTF_8);
223 		}
224 
225 		public List<String> outLines() {
226 			return IO.readLines(outString());
227 		}
228 
229 		public String errString() {
230 			return new String(err.toByteArray(), UTF_8);
231 		}
232 
233 		public List<String> errLines() {
234 			return IO.readLines(errString());
235 		}
236 	}
237 
238 }