1 /*******************************************************************************
2 * Copyright (c) 2011 GitHub Inc.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
9 * Kevin Sawicki (GitHub Inc.) - initial API and implementation
10 *******************************************************************************/
11 package org
.eclipse
.egit
.ui
.internal
.search
;
14 import java
.io
.IOException
;
15 import java
.text
.MessageFormat
;
16 import java
.util
.LinkedList
;
17 import java
.util
.List
;
18 import java
.util
.regex
.Pattern
;
20 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
21 import org
.eclipse
.core
.runtime
.IStatus
;
22 import org
.eclipse
.core
.runtime
.OperationCanceledException
;
23 import org
.eclipse
.core
.runtime
.Status
;
24 import org
.eclipse
.egit
.core
.Activator
;
25 import org
.eclipse
.egit
.ui
.internal
.UIText
;
26 import org
.eclipse
.egit
.ui
.internal
.commit
.RepositoryCommit
;
27 import org
.eclipse
.jgit
.lib
.Constants
;
28 import org
.eclipse
.jgit
.lib
.ObjectId
;
29 import org
.eclipse
.jgit
.lib
.PersonIdent
;
30 import org
.eclipse
.jgit
.lib
.Ref
;
31 import org
.eclipse
.jgit
.lib
.Repository
;
32 import org
.eclipse
.jgit
.revwalk
.RevCommit
;
33 import org
.eclipse
.jgit
.revwalk
.RevTree
;
34 import org
.eclipse
.jgit
.revwalk
.RevWalk
;
35 import org
.eclipse
.search
.ui
.ISearchQuery
;
36 import org
.eclipse
.search
.ui
.ISearchResult
;
39 * Commit search query class that runs a {@link RevWalk} for all
40 * {@link Repository} objects included in the {@link CommitSearchSettings} and
41 * matches all {@link RevCommit} objects against the search settings.
43 public class CommitSearchQuery
implements ISearchQuery
{
45 private abstract class SearchMatcher
{
47 abstract boolean matches(Pattern pattern
, RevCommit commit
);
49 protected boolean matches(Pattern pattern
, String input
) {
50 return input
!= null && input
.length() > 0
51 && pattern
.matcher(input
).find();
56 private class AuthorMatcher
extends SearchMatcher
{
59 public boolean matches(Pattern pattern
, RevCommit commit
) {
60 PersonIdent author
= commit
.getAuthorIdent();
62 return matches(pattern
, author
.getName())
63 || matches(pattern
, author
.getEmailAddress());
69 private class CommitterMatcher
extends SearchMatcher
{
72 public boolean matches(Pattern pattern
, RevCommit commit
) {
73 PersonIdent committer
= commit
.getCommitterIdent();
74 if (committer
!= null)
75 return matches(pattern
, committer
.getName())
76 || matches(pattern
, committer
.getEmailAddress());
82 private class MessageMatcher
extends SearchMatcher
{
85 public boolean matches(Pattern pattern
, RevCommit commit
) {
86 return matches(pattern
, commit
.getFullMessage());
90 private class CommitNameMatcher
extends SearchMatcher
{
93 public boolean matches(Pattern pattern
, RevCommit commit
) {
94 return matches(pattern
, commit
.name());
99 private class TreeMatcher
extends SearchMatcher
{
102 public boolean matches(Pattern pattern
, RevCommit commit
) {
103 RevTree tree
= commit
.getTree();
104 return tree
!= null ?
matches(pattern
, tree
.name()) : false;
108 private class ParentMatcher
extends SearchMatcher
{
111 public boolean matches(Pattern pattern
, RevCommit commit
) {
112 for (RevCommit parent
: commit
.getParents())
113 if (matches(pattern
, parent
.name()))
120 private CommitSearchResult result
= new CommitSearchResult(this);
122 private CommitSearchSettings settings
;
124 private List
<SearchMatcher
> matchers
= new LinkedList
<SearchMatcher
>();
127 * Create git search query
131 public CommitSearchQuery(CommitSearchSettings settings
) {
132 this.settings
= settings
;
134 if (this.settings
.isMatchAuthor())
135 matchers
.add(new AuthorMatcher());
136 if (this.settings
.isMatchCommitter())
137 matchers
.add(new CommitterMatcher());
138 if (this.settings
.isMatchMessage())
139 matchers
.add(new MessageMatcher());
140 if (this.settings
.isMatchCommit())
141 matchers
.add(new CommitNameMatcher());
142 if (this.settings
.isMatchTree())
143 matchers
.add(new TreeMatcher());
144 if (this.settings
.isMatchParents())
145 matchers
.add(new ParentMatcher());
149 * Get text pattern being searched for
153 public String
getPattern() {
154 return this.settings
.getTextPattern();
157 private Repository
getRepository(String name
) throws IOException
{
158 Repository repository
= null;
159 File path
= new File(name
);
161 repository
= Activator
.getDefault().getRepositoryCache()
162 .lookupRepository(path
);
167 * @see org.eclipse.search.ui.ISearchQuery#run(org.eclipse.core.runtime.IProgressMonitor)
170 public IStatus
run(IProgressMonitor monitor
)
171 throws OperationCanceledException
{
172 this.result
.removeAll();
174 Pattern pattern
= PatternUtils
.createPattern(
175 this.settings
.getTextPattern(),
176 this.settings
.isCaseSensitive(), this.settings
.isRegExSearch());
177 List
<String
> paths
= settings
.getRepositories();
179 for (String path
: paths
) {
180 if (monitor
.isCanceled())
181 throw new OperationCanceledException();
183 Repository repo
= getRepository(path
);
187 monitor
.setTaskName(MessageFormat
.format(
188 UIText
.CommitSearchQuery_TaskSearchCommits
, repo
189 .getDirectory().getParentFile().getName()));
190 walkRepository(repo
, pattern
, monitor
);
192 } catch (IOException e
) {
193 org
.eclipse
.egit
.ui
.Activator
.handleError(
194 "Error searching commits", e
, true); //$NON-NLS-1$
196 return Status
.OK_STATUS
;
199 private void walkRepository(Repository repository
, Pattern pattern
,
200 IProgressMonitor monitor
) throws IOException
{
201 try (RevWalk walk
= new RevWalk(repository
)) {
202 walk
.setRetainBody(true);
203 List
<RevCommit
> commits
= new LinkedList
<RevCommit
>();
204 if (this.settings
.isAllBranches()) {
205 for (Ref ref
: repository
.getRefDatabase()
206 .getRefs(Constants
.R_HEADS
).values())
207 if (!ref
.isSymbolic())
208 commits
.add(walk
.parseCommit(ref
.getObjectId()));
209 for (Ref ref
: repository
.getRefDatabase()
210 .getRefs(Constants
.R_REMOTES
).values())
211 if (!ref
.isSymbolic())
212 commits
.add(walk
.parseCommit(ref
.getObjectId()));
214 ObjectId headCommit
= repository
.resolve(Constants
.HEAD
);
215 if (headCommit
!= null)
216 commits
.add(walk
.parseCommit(headCommit
));
219 if (!commits
.isEmpty()) {
220 walk
.markStart(commits
);
221 for (RevCommit commit
: walk
) {
222 if (monitor
.isCanceled())
223 throw new OperationCanceledException();
224 for (SearchMatcher matcher
: this.matchers
)
225 if (matcher
.matches(pattern
, commit
)) {
226 result
.addResult(new RepositoryCommit(repository
,
236 * @see org.eclipse.search.ui.ISearchQuery#getLabel()
239 public String
getLabel() {
240 return UIText
.CommitSearchQuery_Label
;
244 * @see org.eclipse.search.ui.ISearchQuery#canRerun()
247 public boolean canRerun() {
252 * @see org.eclipse.search.ui.ISearchQuery#canRunInBackground()
255 public boolean canRunInBackground() {
260 * @see org.eclipse.search.ui.ISearchQuery#getSearchResult()
263 public ISearchResult
getSearchResult() {