View Javadoc
1   /*
2    * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  
44  package org.eclipse.jgit.pgm;
45  
46  import java.text.MessageFormat;
47  import java.util.ArrayList;
48  import java.util.EnumSet;
49  import java.util.List;
50  
51  import org.eclipse.jgit.diff.DiffConfig;
52  import org.eclipse.jgit.errors.IncorrectObjectTypeException;
53  import org.eclipse.jgit.lib.Constants;
54  import org.eclipse.jgit.lib.ObjectId;
55  import org.eclipse.jgit.lib.Ref;
56  import org.eclipse.jgit.pgm.internal.CLIText;
57  import org.eclipse.jgit.pgm.opt.PathTreeFilterHandler;
58  import org.eclipse.jgit.revwalk.FollowFilter;
59  import org.eclipse.jgit.revwalk.ObjectWalk;
60  import org.eclipse.jgit.revwalk.RevCommit;
61  import org.eclipse.jgit.revwalk.RevFlag;
62  import org.eclipse.jgit.revwalk.RevObject;
63  import org.eclipse.jgit.revwalk.RevSort;
64  import org.eclipse.jgit.revwalk.RevWalk;
65  import org.eclipse.jgit.revwalk.filter.AndRevFilter;
66  import org.eclipse.jgit.revwalk.filter.AuthorRevFilter;
67  import org.eclipse.jgit.revwalk.filter.CommitterRevFilter;
68  import org.eclipse.jgit.revwalk.filter.MessageRevFilter;
69  import org.eclipse.jgit.revwalk.filter.RevFilter;
70  import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
71  import org.eclipse.jgit.treewalk.filter.TreeFilter;
72  import org.kohsuke.args4j.Argument;
73  import org.kohsuke.args4j.Option;
74  
75  abstract class RevWalkTextBuiltin extends TextBuiltin {
76  	RevWalk walk;
77  
78  	@Option(name = "--objects")
79  	boolean objects = false;
80  
81  	@Option(name = "--parents")
82  	boolean parents = false;
83  
84  	@Option(name = "--total-count")
85  	boolean count = false;
86  
87  	@Option(name = "--all")
88  	boolean all = false;
89  
90  	char[] outbuffer = new char[Constants.OBJECT_ID_LENGTH * 2];
91  
92  	private final EnumSet<RevSort> sorting = EnumSet.noneOf(RevSort.class);
93  
94  	private void enableRevSort(RevSort type, boolean on) {
95  		if (on)
96  			sorting.add(type);
97  		else
98  			sorting.remove(type);
99  	}
100 
101 	@Option(name = "--date-order")
102 	void enableDateOrder(boolean on) {
103 		enableRevSort(RevSort.COMMIT_TIME_DESC, on);
104 	}
105 
106 	@Option(name = "--topo-order")
107 	void enableTopoOrder(boolean on) {
108 		enableRevSort(RevSort.TOPO, on);
109 	}
110 
111 	@Option(name = "--reverse")
112 	void enableReverse(boolean on) {
113 		enableRevSort(RevSort.REVERSE, on);
114 	}
115 
116 	@Option(name = "--boundary")
117 	void enableBoundary(boolean on) {
118 		enableRevSort(RevSort.BOUNDARY, on);
119 	}
120 
121 	@Option(name = "--follow", metaVar = "metaVar_path")
122 	private String followPath;
123 
124 	@Argument(index = 0, metaVar = "metaVar_commitish")
125 	private List<RevCommit> commits = new ArrayList<>();
126 
127 	@Option(name = "--", metaVar = "metaVar_path", handler = PathTreeFilterHandler.class)
128 	protected TreeFilter pathFilter = TreeFilter.ALL;
129 
130 	private final List<RevFilter> revLimiter = new ArrayList<>();
131 
132 	@Option(name = "--author")
133 	void addAuthorRevFilter(String who) {
134 		revLimiter.add(AuthorRevFilter.create(who));
135 	}
136 
137 	@Option(name = "--committer")
138 	void addCommitterRevFilter(String who) {
139 		revLimiter.add(CommitterRevFilter.create(who));
140 	}
141 
142 	@Option(name = "--grep")
143 	void addCMessageRevFilter(String msg) {
144 		revLimiter.add(MessageRevFilter.create(msg));
145 	}
146 
147 	@Option(name = "--max-count", aliases = "-n", metaVar = "metaVar_n")
148 	private int maxCount = -1;
149 
150 	/** {@inheritDoc} */
151 	@Override
152 	protected void run() throws Exception {
153 		walk = createWalk();
154 		for (RevSort s : sorting)
155 			walk.sort(s, true);
156 
157 		if (pathFilter == TreeFilter.ALL) {
158 			if (followPath != null)
159 				walk.setTreeFilter(FollowFilter.create(followPath,
160 						db.getConfig().get(DiffConfig.KEY)));
161 		} else if (pathFilter != TreeFilter.ALL) {
162 			walk.setTreeFilter(AndTreeFilter.create(pathFilter,
163 					TreeFilter.ANY_DIFF));
164 		}
165 
166 		if (revLimiter.size() == 1)
167 			walk.setRevFilter(revLimiter.get(0));
168 		else if (revLimiter.size() > 1)
169 			walk.setRevFilter(AndRevFilter.create(revLimiter));
170 
171 		if (all) {
172 			for (Ref a : db.getRefDatabase().getRefs()) {
173 				ObjectId oid = a.getPeeledObjectId();
174 				if (oid == null)
175 					oid = a.getObjectId();
176 				try {
177 					commits.add(walk.parseCommit(oid));
178 				} catch (IncorrectObjectTypeException e) {
179 					// Ignore all refs which are not commits
180 				}
181 			}
182 		}
183 
184 		if (commits.isEmpty()) {
185 			final ObjectId head = db.resolve(Constants.HEAD);
186 			if (head == null)
187 				throw die(MessageFormat.format(CLIText.get().cannotResolve, Constants.HEAD));
188 			commits.add(walk.parseCommit(head));
189 		}
190 		for (RevCommit c : commits) {
191 			final RevCommit real = argWalk == walk ? c : walk.parseCommit(c);
192 			if (c.has(RevFlag.UNINTERESTING))
193 				walk.markUninteresting(real);
194 			else
195 				walk.markStart(real);
196 		}
197 
198 		final long start = System.currentTimeMillis();
199 		final int n = walkLoop();
200 		if (count) {
201 			final long end = System.currentTimeMillis();
202 			errw.print(n);
203 			errw.print(' ');
204 			errw.println(MessageFormat.format(
205 							CLIText.get().timeInMilliSeconds,
206 							Long.valueOf(end - start)));
207 		}
208 	}
209 
210 	/**
211 	 * Create RevWalk
212 	 *
213 	 * @return a {@link org.eclipse.jgit.revwalk.RevWalk} object.
214 	 */
215 	protected RevWalk createWalk() {
216 		RevWalk result;
217 		if (objects)
218 			result = new ObjectWalk(db);
219 		else if (argWalk != null)
220 			result = argWalk;
221 		else
222 		  result = argWalk = new RevWalk(db);
223 		result.setRewriteParents(false);
224 		return result;
225 	}
226 
227 	/**
228 	 * Loop the walk
229 	 *
230 	 * @return number of RevCommits walked
231 	 * @throws java.lang.Exception
232 	 *             if any.
233 	 */
234 	protected int walkLoop() throws Exception {
235 		int n = 0;
236 		for (RevCommit c : walk) {
237 			if (++n > maxCount && maxCount >= 0)
238 				break;
239 			show(c);
240 		}
241 		if (walk instanceof ObjectWalk) {
242 			final ObjectWalk ow = (ObjectWalk) walk;
243 			for (;;) {
244 				final RevObject obj = ow.nextObject();
245 				if (obj == null)
246 					break;
247 				show(ow, obj);
248 			}
249 		}
250 		return n;
251 	}
252 
253 	/**
254 	 * "Show" the current RevCommit when called from the main processing loop.
255 	 * <p>
256 	 * Implement this methods to define the behavior for subclasses of
257 	 * RevWalkTextBuiltin.
258 	 *
259 	 * @param c
260 	 *            The current {@link org.eclipse.jgit.revwalk.RevCommit}
261 	 * @throws java.lang.Exception
262 	 */
263 	protected abstract void show(RevCommit c) throws Exception;
264 
265 	/**
266 	 * "Show" the current RevCommit when called from the main processing loop.
267 	 * <p>
268 	 * The default implementation does nothing because most subclasses only
269 	 * process RevCommits.
270 	 *
271 	 * @param objectWalk
272 	 *            the {@link org.eclipse.jgit.revwalk.ObjectWalk} used by
273 	 *            {@link #walkLoop()}
274 	 * @param currentObject
275 	 *            The current {@link org.eclipse.jgit.revwalk.RevObject}
276 	 * @throws java.lang.Exception
277 	 */
278 	protected void show(final ObjectWalk objectWalk,
279 			final RevObject currentObject) throws Exception {
280 		// Do nothing by default. Most applications cannot show an object.
281 	}
282 }