Improve cancel logic for history view refresh.
[egit.git] / org.spearce.egit.core / src / org / spearce / egit / core / internal / mapping / GitFileHistory.java
blob889ca0cdb7d501d64ccebccf23e4811fb2b03ad7
1 /*
2 * Copyright (C) 2006 Robin Rosenberg
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License, version 2.1, as published by the Free Software Foundation.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
17 package org.spearce.egit.core.internal.mapping;
19 import java.io.IOException;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.Date;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.concurrent.CancellationException;
29 import org.eclipse.core.resources.IContainer;
30 import org.eclipse.core.resources.IResource;
31 import org.eclipse.core.runtime.IAdaptable;
32 import org.eclipse.core.runtime.IProgressMonitor;
33 import org.eclipse.team.core.RepositoryProvider;
34 import org.eclipse.team.core.history.IFileHistoryProvider;
35 import org.eclipse.team.core.history.IFileRevision;
36 import org.eclipse.team.core.history.provider.FileHistory;
37 import org.spearce.egit.core.GitIndexFileRevision;
38 import org.spearce.egit.core.GitProvider;
39 import org.spearce.egit.core.GitWorkspaceFileRevision;
40 import org.spearce.egit.core.project.RepositoryMapping;
41 import org.spearce.jgit.lib.Commit;
42 import org.spearce.jgit.lib.MappedList;
43 import org.spearce.jgit.lib.ObjectId;
44 import org.spearce.jgit.lib.Repository;
45 import org.spearce.jgit.lib.SuperList;
46 import org.spearce.jgit.lib.TopologicalWalker;
47 import org.spearce.jgit.lib.Tree;
48 import org.spearce.jgit.lib.TreeEntry;
49 import org.spearce.jgit.lib.GitIndex.Entry;
51 public class GitFileHistory extends FileHistory implements IAdaptable {
53 private final IResource resource;
55 private final String[] relativeResourceName;
57 private final int flags;
59 private List<IFileRevision> revisions;
61 private final boolean returnAll;
63 public GitFileHistory(IResource resource, int flags, IProgressMonitor monitor, boolean returnAll) {
64 this.resource = resource;
65 this.flags = flags;
66 this.returnAll = returnAll;
67 String prefix = RepositoryMapping.getMapping(resource).getSubset();
68 String[] prefixSegments = prefix!=null ? prefix.split("/") : new String[0];
69 String[] resourceSegments = resource.getProjectRelativePath().segments();
70 relativeResourceName = new String[prefixSegments.length + resourceSegments.length];
71 System.arraycopy(prefixSegments, 0, relativeResourceName, 0, prefixSegments.length);
72 System.arraycopy(resourceSegments, 0, relativeResourceName, prefixSegments.length, resourceSegments.length);
73 if ((flags & IFileHistoryProvider.SINGLE_LINE_OF_DESCENT) == 0) {
74 findSingleRevision(monitor);
75 } else {
76 try {
77 findRevisions(monitor);
78 } catch (IOException e) {
79 throw new Error(e);
84 public IFileRevision[] getContributors(IFileRevision revision) {
85 if (revision instanceof GitCommitFileRevision) {
86 GitCommitFileRevision grevision = (GitCommitFileRevision)revision;
87 ObjectId[] parents = grevision.getCommit().getParentIds();
88 IFileRevision[] ret = new IFileRevision[parents.length];
89 for (int i = 0; i < parents.length; ++i) {
90 ret[i] = new GitCommitFileRevision(parents[i], grevision
91 .getResource(), -1);
93 return ret;
95 return new IFileRevision[0];
98 public IFileRevision getFileRevision(String id) {
99 if (id.equals(""))
100 return new GitWorkspaceFileRevision(resource, -1);
101 if (id.equals("Index"))
102 return new GitIndexFileRevision(resource, 0);
103 return new GitCommitFileRevision(new ObjectId(id), resource, 0);
106 static class EclipseWalker extends TopologicalWalker {
108 IResource resource;
109 private final IProgressMonitor monitor;
110 private Map<ObjectId, IFileRevision> revisions = new HashMap<ObjectId, IFileRevision>();
112 EclipseWalker(Repository repository, Commit[] starts, String[] relativeResourceName,boolean leafIsBlob,IResource resource,boolean followMainOnly, Boolean merges, ObjectId lastActiveDiffId, boolean returnAll, IProgressMonitor monitor) {
113 super(repository, starts, relativeResourceName, leafIsBlob, followMainOnly, merges, lastActiveDiffId, returnAll);
114 this.resource = resource;
115 this.monitor = monitor;
118 protected void collect(Commit commit, int count, int breadth) {
119 super.collect(commit, count, breadth);
120 if (commit.getCommitId().equals(ObjectId.zeroId()))
121 revisions.put(commit.getCommitId(), new GitIndexFileRevision(resource, count));
122 else
123 revisions.put(commit.getCommitId(), new GitCommitFileRevision(commit.getCommitId(), resource, count));
126 public boolean isCancelled() {
127 return monitor.isCanceled();
130 @Override
131 public Collection collectHistory() {
132 Collection rawList = super.collectHistory();
133 List<IFileRevision> ret = new MappedList<ObjectId,IFileRevision>((List)rawList) {
134 public IFileRevision map(ObjectId in) {
135 GitFileRevision ret = (GitFileRevision)revisions.get(in);
136 if (ret == null && isReturnAll())
137 if (in.equals(ObjectId.zeroId()))
138 ret = new GitIndexFileRevision(resource, -1);
139 else
140 ret = new GitCommitFileRevision(in, resource, -1);
141 if (ret != null)
142 ret.setLane(getLane(in));
143 return ret;
146 return ret;
149 @Override
150 protected void record(ObjectId pred, ObjectId succ) {
151 super.record(pred, succ);
152 if (monitor.isCanceled()) {
153 System.out.println("Cancelled");
154 throw new CancellationException("history refresh cancelled");
159 public IFileRevision[] getFileRevisions() {
160 return revisions.toArray(new IFileRevision[revisions.size()]);
163 public List<IFileRevision> getFileRevisionsList() {
164 return revisions;
168 * Get a single file revision suitable for quickdiff.
170 * We have two modes. For a branch set up for Stacked Git that has a patch
171 * return the revision prior to the topmost patch, be it another patch or a
172 * normal Git Commit. This is the revision in HEAD^. Otherwise we return the
173 * revision in HEAD.
174 * @param monitor
176 private void findSingleRevision(IProgressMonitor monitor) {
177 try {
178 Repository repository = RepositoryMapping.getMapping(resource).getRepository();
179 ObjectId id = repository.resolve("HEAD");
180 Commit current = repository.mapCommit(id);
181 if (repository.isStGitMode()) {
182 ObjectId[] parentIds = current.getParentIds();
183 if (parentIds != null && parentIds.length > 0)
184 current = repository.mapCommit(parentIds[0]);
185 else {
186 revisions = Collections.emptyList();
187 return;
190 TreeEntry currentEntry = current.getTree();
191 for (int i=0; i < relativeResourceName.length && currentEntry != null; ++i) {
192 if (i == relativeResourceName.length-1 && resource.getType() == IResource.FILE)
193 ((Tree)currentEntry).findBlobMember(relativeResourceName[i]);
194 else
195 ((Tree)currentEntry).findTreeMember(relativeResourceName[i]);
197 if (currentEntry != null)
198 revisions = Collections.singletonList(
199 (IFileRevision)new GitCommitFileRevision(current.getCommitId(), resource, -1));
200 else
201 revisions = Collections.emptyList();
203 } catch (IOException e) {
204 e.printStackTrace();
205 revisions = Collections.emptyList();
209 private void findRevisions(IProgressMonitor monitor) throws IOException {
210 RepositoryProvider provider = RepositoryProvider.getProvider(resource
211 .getProject());
212 if (provider instanceof GitProvider) {
213 GitWorkspaceFileRevision wsrevision = new GitWorkspaceFileRevision(resource, -1);
215 long time0 = new Date().getTime();
216 System.out.println("getting file history");
217 SuperList<IFileRevision> ret = new SuperList<IFileRevision>();
218 ObjectId activeDiffLeafId = null;
219 RepositoryMapping mapping = RepositoryMapping.getMapping(resource);
220 Repository repository = mapping.getRepository();
221 if (!(resource instanceof IContainer)) {
222 String relativeResourceNameString = mapping
223 .getRepoRelativePath(resource);
224 Entry entry = repository.getIndex().getEntry(
225 relativeResourceNameString);
226 if (entry != null)
227 if (entry.isModified(mapping.getWorkDir(), entry.getSize() < 500000)) {
228 activeDiffLeafId = ObjectId.zeroId();
229 wsrevision = new GitWorkspaceFileRevision(resource, 0); // mark "interesting"
230 } else {
231 activeDiffLeafId = entry.getObjectId();
233 else
234 activeDiffLeafId = ObjectId.zeroId();
238 Collection<IFileRevision> githistory;
239 ObjectId head = repository.resolve("HEAD");
240 if (head != null) {
241 List<Commit> startList = new ArrayList<Commit>();
243 startList.add(repository.mapCommit(head));
244 for(String branch : repository.getBranches()) {
245 Commit commit = repository.mapCommit(branch);
246 if (commit != null)
247 startList.add(commit);
249 Commit[] starts = startList.toArray(new Commit[startList.size()]);
250 EclipseWalker walker = new EclipseWalker(
251 repository,
252 starts,
253 relativeResourceName,
254 resource.getType() == IResource.FILE,
255 resource,
256 (flags & IFileHistoryProvider.SINGLE_LINE_OF_DESCENT) == 0,
257 null,
258 activeDiffLeafId,
259 returnAll,
260 monitor);
261 githistory = walker.collectHistory();
262 } else {
263 githistory = new ArrayList<IFileRevision>();
265 if (githistory.size() > 0) {
266 if (resource.getType()==IResource.FILE) {
267 if (returnAll)
268 ret.add(wsrevision);
269 ret.addAll(githistory);
270 } else {
271 ret.addAll(githistory);
273 } else {
274 ret.add(wsrevision);
276 long time1 = new Date().getTime();
277 System.out.println("got file history in " + (time1 - time0)
278 / 1000.0 + "s");
280 revisions = ret;
282 } else {
283 revisions = Collections.emptyList();
287 public IFileRevision[] getTargets(IFileRevision revision) {
288 if (revision instanceof GitCommitFileRevision) {
289 GitCommitFileRevision grevision = (GitCommitFileRevision) revision;
290 ObjectId targetCommitId = grevision.getCommit().getCommitId();
291 List<IFileRevision> ret = new ArrayList<IFileRevision>(4);
292 for(IFileRevision r: revisions) {
293 Commit ref = ((GitCommitFileRevision)r).getCommit();
294 ObjectId[] parentIds = ref.getParentIds();
295 for (int j = 0; j < parentIds.length; ++j) {
296 if (parentIds[j].equals(targetCommitId)) {
297 ret.add(r);
298 break;
302 return ret.toArray(new IFileRevision[ret.size()]);
304 return new IFileRevision[0];
307 public Object getAdapter(Class adapter) {
308 System.out.println("GitFileHistory.getAdapter "+adapter.getName());
309 return null;