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 }