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
.BufferedInputStream
;
20 import java
.io
.IOException
;
21 import java
.io
.InputStream
;
22 import java
.util
.ArrayList
;
23 import java
.util
.Arrays
;
24 import java
.util
.Collection
;
25 import java
.util
.Collections
;
26 import java
.util
.Date
;
27 import java
.util
.List
;
29 import org
.eclipse
.core
.resources
.IResource
;
30 import org
.eclipse
.core
.runtime
.CoreException
;
31 import org
.eclipse
.core
.runtime
.IAdaptable
;
32 import org
.eclipse
.team
.core
.RepositoryProvider
;
33 import org
.eclipse
.team
.core
.history
.IFileHistoryProvider
;
34 import org
.eclipse
.team
.core
.history
.IFileRevision
;
35 import org
.eclipse
.team
.core
.history
.provider
.FileHistory
;
36 import org
.spearce
.egit
.core
.GitProvider
;
37 import org
.spearce
.egit
.core
.GitWorkspaceFileRevision
;
38 import org
.spearce
.egit
.core
.project
.GitProjectData
;
39 import org
.spearce
.egit
.core
.project
.RepositoryMapping
;
40 import org
.spearce
.jgit
.lib
.Commit
;
41 import org
.spearce
.jgit
.lib
.ObjectId
;
42 import org
.spearce
.jgit
.lib
.Repository
;
43 import org
.spearce
.jgit
.lib
.Tree
;
44 import org
.spearce
.jgit
.lib
.TreeEntry
;
46 public class GitFileHistory
extends FileHistory
implements IAdaptable
{
48 private final IResource resource
;
50 private final String
[] relativeResourceName
;
52 private final int flags
;
54 private IFileRevision
[] revisions
;
56 public GitFileHistory(IResource resource
, int flags
) {
57 this.resource
= resource
;
59 String prefix
= getRepositoryMapping().getSubset();
60 String
[] prefixSegments
= prefix
!=null ? prefix
.split("/") : new String
[0];
61 String
[] resourceSegments
= resource
.getProjectRelativePath().segments();
62 relativeResourceName
= new String
[prefixSegments
.length
+ resourceSegments
.length
];
63 System
.arraycopy(prefixSegments
, 0, relativeResourceName
, 0, prefixSegments
.length
);
64 System
.arraycopy(resourceSegments
, 0, relativeResourceName
, prefixSegments
.length
, resourceSegments
.length
);
67 public IFileRevision
[] getContributors(IFileRevision revision
) {
68 GitFileRevision grevision
= (GitFileRevision
) revision
;
69 List parents
= grevision
.getCommit().getParentIds();
70 IFileRevision
[] ret
= new IFileRevision
[parents
.size()];
71 Repository repository
= getRepository();
72 for (int i
= 0; i
< parents
.size(); ++i
) {
74 ret
[i
] = new GitFileRevision(repository
75 .mapCommit((ObjectId
) parents
.get(i
)), grevision
77 } catch (IOException e
) {
85 public IFileRevision
getFileRevision(String id
) {
87 return new GitWorkspaceFileRevision(resource
);
90 Repository repository
= getRepository();
91 return new GitFileRevision(repository
.mapCommit(id
), resource
, 0);
92 } catch (IOException e
) {
99 private Repository
getRepository() {
100 return getRepositoryMapping().getRepository();
103 private GitProjectData
getData() {
104 GitProvider provider
= (GitProvider
) RepositoryProvider
105 .getProvider(resource
.getProject());
106 return provider
.getData();
109 private RepositoryMapping
getRepositoryMapping() {
110 return getData().getRepositoryMapping(resource
.getProject());
113 private Collection
collectHistory() {
114 Repository repository
= getRepository();
116 ObjectId id
= repository
.resolve("HEAD");
117 Commit commit
= repository
.mapCommit(id
);
118 ObjectId
[] initialResourceHash
= new ObjectId
[relativeResourceName
.length
];
119 Arrays
.fill(initialResourceHash
, ObjectId
.zeroId());
120 TreeEntry
[] activeDiffTreeEntries
= null;
122 activeDiffTreeEntries
= getData().getActiveDiffTreeEntries(resource
);
123 } catch (CoreException e1
) {
124 // TODO: eclipse excetion logging
125 e1
.printStackTrace();
127 if (activeDiffTreeEntries
!=null)
128 initialResourceHash
[initialResourceHash
.length
-1] = activeDiffTreeEntries
[0].getId();
129 return collectHistory(0, initialResourceHash
, null,
131 } catch (IOException e
) {
133 return Collections
.EMPTY_LIST
;
137 private Collection
collectHistory(int count
, ObjectId
[] lastResourceHash
, TreeEntry lastEntry
,
138 Repository repository
, Commit top
) throws IOException
{
140 return Collections
.EMPTY_LIST
;
141 Collection ret
= new ArrayList(10000);
142 Commit current
= top
;
143 Commit previous
= top
;
146 TreeEntry currentEntry
= lastEntry
;
147 ObjectId
[] currentResourceHash
= new ObjectId
[lastResourceHash
.length
];
148 Tree t
= current
.getTree();
149 for (int i
= 0; i
< currentResourceHash
.length
; ++i
) {
151 if (i
== relativeResourceName
.length
-1 && resource
.getType() == IResource
.FILE
)
152 m
= t
.findBlobMember(relativeResourceName
[i
]);
154 m
= t
.findTreeMember(relativeResourceName
[i
]);
156 ObjectId id
= m
.getId();
157 currentResourceHash
[i
] = id
;
158 if (id
.equals(lastResourceHash
[i
])) {
159 while (++i
< currentResourceHash
.length
) {
160 currentResourceHash
[i
] = lastResourceHash
[i
];
163 if (m
instanceof Tree
) {
166 if (i
== currentResourceHash
.length
- 1) {
170 while (++i
< currentResourceHash
.length
) {
171 currentResourceHash
[i
] = ObjectId
.zeroId();
177 for (; i
< currentResourceHash
.length
; ++i
) {
178 currentResourceHash
[i
] = ObjectId
.zeroId();
183 if (currentResourceHash
.length
== 0 || !currentResourceHash
[currentResourceHash
.length
-1].equals(lastResourceHash
[currentResourceHash
.length
-1]))
184 ret
.add(new GitFileRevision(previous
, resource
, count
));
186 lastResourceHash
= currentResourceHash
;
189 // TODO: we may need to list more revisions when traversing
191 List parents
= current
.getParentIds();
192 if ((flags
& IFileHistoryProvider
.SINGLE_LINE_OF_DESCENT
) == 0) {
193 for (int i
= 1; i
< parents
.size(); ++i
) {
194 ObjectId mergeParentId
= (ObjectId
) parents
.get(i
);
197 mergeParent
= repository
.mapCommit(mergeParentId
);
198 ret
.addAll(collectHistory(0, lastResourceHash
, currentEntry
, repository
,
200 // TODO: this gets us a lot of duplicates that we need
202 // Leave that til we get a GUI.
203 } catch (IOException e
) {
208 if (parents
.size() > 0) {
209 ObjectId parentId
= (ObjectId
) parents
.get(0);
211 current
= repository
.mapCommit(parentId
);
212 } catch (IOException e
) {
220 } while (current
!= null);
225 public IFileRevision
[] getFileRevisions() {
226 if (revisions
== null)
227 if ((flags
& IFileHistoryProvider
.SINGLE_LINE_OF_DESCENT
) == 0)
228 findSingleRevision();
235 * Get a single file revision suitable for quickdiff.
237 * We have two modes. For a branch set up for Stacked Git that has a patch
238 * return the revision prior to the topmost patch, be it another patch or a
239 * normal Git Commit. This is the revision in HEAD^. Otherwise we return the
242 private void findSingleRevision() {
244 Repository repository
= getRepository();
245 ObjectId id
= repository
.resolve("HEAD");
246 Commit current
= repository
.mapCommit(id
);
247 if (repository
.isStGitMode()) {
248 List parentIds
= current
.getParentIds();
249 if (parentIds
!= null && parentIds
.size() > 0)
250 current
= repository
.mapCommit((ObjectId
) parentIds
.get(0));
252 revisions
= new IFileRevision
[0];
256 TreeEntry currentEntry
= current
.getTree();
257 for (int i
=0; i
< relativeResourceName
.length
&& currentEntry
!= null; ++i
) {
258 if (i
== relativeResourceName
.length
-1 && resource
.getType() == IResource
.FILE
)
259 ((Tree
)currentEntry
).findBlobMember(relativeResourceName
[i
]);
261 ((Tree
)currentEntry
).findTreeMember(relativeResourceName
[i
]);
263 if (currentEntry
!= null)
264 revisions
= new IFileRevision
[] { new GitFileRevision(current
, resource
,
267 revisions
= new IFileRevision
[0];
269 } catch (IOException e
) {
271 revisions
= new IFileRevision
[0];
275 private void findRevisions() {
276 RepositoryProvider provider
= RepositoryProvider
.getProvider(resource
278 if (provider
instanceof GitProvider
) {
279 GitWorkspaceFileRevision wsrevision
= new GitWorkspaceFileRevision(resource
);
281 long time0
= new Date().getTime();
282 System
.out
.println("getting file history");
283 List ret
= new ArrayList();
284 Collection githistory
= collectHistory();
285 if (githistory
.size() >0) {
286 if (resource
.getType()==IResource
.FILE
) {
287 // TODO: consider index in future versions
289 InputStream wsContents
= new BufferedInputStream(wsrevision
.getStorage(null).getContents());
290 InputStream headContents
= ((IFileRevision
)githistory
.toArray()[0]).getStorage(null).getContents();
291 if (!streamsEqual(wsContents
,headContents
)) {
293 ret
.addAll(githistory
);
295 ret
.addAll(githistory
);
298 headContents
.close();
299 } catch (IOException e
) {
300 // TODO: Eclipse error handling
302 } catch (CoreException e
) {
303 // TODO: Eclipse error handling
307 ret
.addAll(githistory
);
312 long time1
= new Date().getTime();
313 System
.out
.println("got file history in " + (time1
- time0
)
316 revisions
= (IFileRevision
[]) ret
.toArray(new IFileRevision
[ret
320 revisions
= new IFileRevision
[0];
324 private boolean streamsEqual(InputStream s1
, InputStream s2
) {
328 while ((c1
=s1
.read()) == (c2
=s2
.read()) && c1
!=-1)
330 return c1
== -1 && c2
==-1;
331 } catch (IOException e
) {
332 // TODO: eclipse error handling
338 public IFileRevision
[] getTargets(IFileRevision revision
) {
339 GitFileRevision grevision
= (GitFileRevision
) revision
;
340 ObjectId targetCommitId
= grevision
.getCommit().getCommitId();
341 List ret
= new ArrayList(4);
342 for (int i
= 0; i
< revisions
.length
; ++i
) {
343 Commit ref
= ((GitFileRevision
) revisions
[i
]).getCommit();
344 List parentIds
= ref
.getParentIds();
345 if (parentIds
.contains(targetCommitId
)) {
346 ret
.add(revisions
[i
]);
349 return (IFileRevision
[]) ret
.toArray(new IFileRevision
[ret
.size()]);
352 public Object
getAdapter(Class adapter
) {
353 System
.out
.println("GitFileHistory.getAdapter "+adapter
.getName());