1 /*
2 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> 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
11 package org.eclipse.jgit.revwalk.filter;
12
13 import java.io.IOException;
14
15 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
16 import org.eclipse.jgit.errors.MissingObjectException;
17 import org.eclipse.jgit.errors.StopWalkException;
18 import org.eclipse.jgit.internal.JGitText;
19 import org.eclipse.jgit.revwalk.RevCommit;
20 import org.eclipse.jgit.revwalk.RevWalk;
21
22 /**
23 * Selects interesting revisions during walking.
24 * <p>
25 * This is an abstract interface. Applications may implement a subclass, or use
26 * one of the predefined implementations already available within this package.
27 * Filters may be chained together using <code>AndRevFilter</code> and
28 * <code>OrRevFilter</code> to create complex boolean expressions.
29 * <p>
30 * Applications should install the filter on a RevWalk by
31 * {@link org.eclipse.jgit.revwalk.RevWalk#setRevFilter(RevFilter)} prior to
32 * starting traversal.
33 * <p>
34 * Unless specifically noted otherwise a RevFilter implementation is not thread
35 * safe and may not be shared by different RevWalk instances at the same time.
36 * This restriction allows RevFilter implementations to cache state within their
37 * instances during {@link #include(RevWalk, RevCommit)} if it is beneficial to
38 * their implementation. Deep clones created by {@link #clone()} may be used to
39 * construct a thread-safe copy of an existing filter.
40 *
41 * <p>
42 * <b>Message filters:</b>
43 * <ul>
44 * <li>Author name/email:
45 * {@link org.eclipse.jgit.revwalk.filter.AuthorRevFilter}</li>
46 * <li>Committer name/email:
47 * {@link org.eclipse.jgit.revwalk.filter.CommitterRevFilter}</li>
48 * <li>Message body:
49 * {@link org.eclipse.jgit.revwalk.filter.MessageRevFilter}</li>
50 * </ul>
51 *
52 * <p>
53 * <b>Merge filters:</b>
54 * <ul>
55 * <li>Skip all merges: {@link #NO_MERGES}.</li>
56 * <li>Skip all non-merges: {@link #ONLY_MERGES}</li>
57 * </ul>
58 *
59 * <p>
60 * <b>Boolean modifiers:</b>
61 * <ul>
62 * <li>AND: {@link org.eclipse.jgit.revwalk.filter.AndRevFilter}</li>
63 * <li>OR: {@link org.eclipse.jgit.revwalk.filter.OrRevFilter}</li>
64 * <li>NOT: {@link org.eclipse.jgit.revwalk.filter.NotRevFilter}</li>
65 * </ul>
66 */
67 public abstract class RevFilter {
68 /** Default filter that always returns true (thread safe). */
69 public static final RevFilter ALL = new AllFilter();
70
71 private static final class AllFilter extends RevFilter {
72 @Override
73 public boolean include(RevWalk walker, RevCommit c) {
74 return true;
75 }
76
77 @Override
78 public RevFilter clone() {
79 return this;
80 }
81
82 @Override
83 public boolean requiresCommitBody() {
84 return false;
85 }
86
87 @Override
88 public String toString() {
89 return "ALL"; //$NON-NLS-1$
90 }
91 }
92
93 /** Default filter that always returns false (thread safe). */
94 public static final RevFilter NONE = new NoneFilter();
95
96 private static final class NoneFilter extends RevFilter {
97 @Override
98 public boolean include(RevWalk walker, RevCommit c) {
99 return false;
100 }
101
102 @Override
103 public RevFilter clone() {
104 return this;
105 }
106
107 @Override
108 public boolean requiresCommitBody() {
109 return false;
110 }
111
112 @Override
113 public String toString() {
114 return "NONE"; //$NON-NLS-1$
115 }
116 }
117
118 /**
119 * Filter including only merge commits, excluding all commits with less than
120 * two parents (thread safe).
121 *
122 * @since 4.4
123 */
124 public static final RevFilter ONLY_MERGES = new OnlyMergesFilter();
125
126 private static final class OnlyMergesFilter extends RevFilter {
127
128 @Override
129 public boolean include(RevWalk walker, RevCommit c) {
130 return c.getParentCount() >= 2;
131 }
132
133 @Override
134 public RevFilter clone() {
135 return this;
136 }
137
138 @Override
139 public boolean requiresCommitBody() {
140 return false;
141 }
142
143 @Override
144 public String toString() {
145 return "ONLY_MERGES"; //$NON-NLS-1$
146 }
147 }
148
149 /** Excludes commits with more than one parent (thread safe). */
150 public static final RevFilter NO_MERGES = new NoMergesFilter();
151
152 private static final class NoMergesFilter extends RevFilter {
153 @Override
154 public boolean include(RevWalk walker, RevCommit c) {
155 return c.getParentCount() < 2;
156 }
157
158 @Override
159 public RevFilter clone() {
160 return this;
161 }
162
163 @Override
164 public boolean requiresCommitBody() {
165 return false;
166 }
167
168 @Override
169 public String toString() {
170 return "NO_MERGES"; //$NON-NLS-1$
171 }
172 }
173
174 /**
175 * Selects only merge bases of the starting points (thread safe).
176 * <p>
177 * This is a special case filter that cannot be combined with any other
178 * filter. Its include method always throws an exception as context
179 * information beyond the arguments is necessary to determine if the
180 * supplied commit is a merge base.
181 */
182 public static final RevFilter MERGE_BASE = new MergeBaseFilter();
183
184 private static final class MergeBaseFilter extends RevFilter {
185 @Override
186 public boolean include(RevWalk walker, RevCommit c) {
187 throw new UnsupportedOperationException(JGitText.get().cannotBeCombined);
188 }
189
190 @Override
191 public RevFilter clone() {
192 return this;
193 }
194
195 @Override
196 public boolean requiresCommitBody() {
197 return false;
198 }
199
200 @Override
201 public String toString() {
202 return "MERGE_BASE"; //$NON-NLS-1$
203 }
204 }
205
206 /**
207 * Create a new filter that does the opposite of this filter.
208 *
209 * @return a new filter that includes commits this filter rejects.
210 */
211 public RevFilter negate() {
212 return NotRevFilter.create(this);
213 }
214
215 /**
216 * Whether the filter needs the commit body to be parsed.
217 *
218 * @return true if the filter needs the commit body to be parsed.
219 */
220 public boolean requiresCommitBody() {
221 // Assume true to be backward compatible with prior behavior.
222 return true;
223 }
224
225 /**
226 * Determine if the supplied commit should be included in results.
227 *
228 * @param walker
229 * the active walker this filter is being invoked from within.
230 * @param cmit
231 * the commit currently being tested. The commit has been parsed
232 * and its body is available for inspection only if the filter
233 * returns true from {@link #requiresCommitBody()}.
234 * @return true to include this commit in the results; false to have this
235 * commit be omitted entirely from the results.
236 * @throws org.eclipse.jgit.errors.StopWalkException
237 * the filter knows for certain that no additional commits can
238 * ever match, and the current commit doesn't match either. The
239 * walk is halted and no more results are provided.
240 * @throws org.eclipse.jgit.errors.MissingObjectException
241 * an object the filter needs to consult to determine its answer
242 * does not exist in the Git repository the walker is operating
243 * on. Filtering this commit is impossible without the object.
244 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
245 * an object the filter needed to consult was not of the
246 * expected object type. This usually indicates a corrupt
247 * repository, as an object link is referencing the wrong type.
248 * @throws java.io.IOException
249 * a loose object or pack file could not be read to obtain data
250 * necessary for the filter to make its decision.
251 */
252 public abstract boolean include(RevWalk walker, RevCommit cmit)
253 throws StopWalkException, MissingObjectException,
254 IncorrectObjectTypeException, IOException;
255
256 /**
257 * {@inheritDoc}
258 * <p>
259 * Clone this revision filter, including its parameters.
260 * <p>
261 * This is a deep clone. If this filter embeds objects or other filters it
262 * must also clone those, to ensure the instances do not share mutable data.
263 */
264 @Override
265 public abstract RevFilter clone();
266
267 /** {@inheritDoc} */
268 @Override
269 public String toString() {
270 String n = getClass().getName();
271 int lastDot = n.lastIndexOf('.');
272 if (lastDot >= 0) {
273 n = n.substring(lastDot + 1);
274 }
275 return n.replace('$', '.');
276 }
277 }