Use Constant.HEAD for "HEAD" references
[egit/imyousuf.git] / org.spearce.egit.core / src / org / spearce / egit / core / internal / storage / GitFileHistory.java
blob8b47c3787be50ecaeb110499385809693f85c65c
1 /*******************************************************************************
2 * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
3 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
5 * All rights reserved. This program and the accompanying materials
6 * are made available under the terms of the Eclipse Public License v1.0
7 * See LICENSE for the full license text, also available.
8 *******************************************************************************/
9 package org.spearce.egit.core.internal.storage;
11 import java.io.IOException;
12 import java.util.Collections;
14 import org.eclipse.core.resources.IResource;
15 import org.eclipse.core.runtime.IAdaptable;
16 import org.eclipse.core.runtime.IProgressMonitor;
17 import org.eclipse.team.core.history.IFileHistoryProvider;
18 import org.eclipse.team.core.history.IFileRevision;
19 import org.eclipse.team.core.history.provider.FileHistory;
20 import org.spearce.egit.core.Activator;
21 import org.spearce.egit.core.project.RepositoryMapping;
22 import org.spearce.jgit.lib.AnyObjectId;
23 import org.spearce.jgit.lib.Constants;
24 import org.spearce.jgit.lib.ObjectId;
25 import org.spearce.jgit.lib.Repository;
26 import org.spearce.jgit.revwalk.RevCommit;
27 import org.spearce.jgit.treewalk.filter.AndTreeFilter;
28 import org.spearce.jgit.treewalk.filter.PathFilterGroup;
29 import org.spearce.jgit.treewalk.filter.TreeFilter;
31 /**
32 * A list of revisions for a specific resource according to some filtering
33 * criterion. Though git really does not do file tracking, this corresponds to
34 * listing all files with the same path.
36 class GitFileHistory extends FileHistory implements IAdaptable {
37 private static final int SINGLE_REVISION = IFileHistoryProvider.SINGLE_REVISION;
39 private static final IFileRevision[] NO_REVISIONS = {};
41 private static final int BATCH_SIZE = 256;
43 private final IResource resource;
45 private String gitPath;
47 private final KidWalk walk;
49 private final IFileRevision[] revisions;
51 GitFileHistory(final IResource rsrc, final int flags,
52 final IProgressMonitor monitor) {
53 resource = rsrc;
54 walk = buildWalk(flags);
55 revisions = buildRevisions(monitor, flags);
58 private KidWalk buildWalk(final int flags) {
59 final RepositoryMapping rm = RepositoryMapping.getMapping(resource);
60 if (rm == null) {
61 Activator.logError("Git not attached to project "
62 + resource.getProject().getName() + ".", null);
63 return null;
66 final KidWalk w = new KidWalk(rm.getRepository());
67 gitPath = rm.getRepoRelativePath(resource);
68 w.setTreeFilter(AndTreeFilter.create(PathFilterGroup
69 .createFromStrings(Collections.singleton(gitPath)),
70 TreeFilter.ANY_DIFF));
71 return w;
74 private IFileRevision[] buildRevisions(final IProgressMonitor monitor,
75 final int flags) {
76 if (walk == null)
77 return NO_REVISIONS;
79 final Repository db = walk.getRepository();
80 final RevCommit root;
81 try {
82 final AnyObjectId headId = db.resolve(Constants.HEAD);
83 if (headId == null) {
84 Activator.logError("No HEAD revision available from Git"
85 + " for project " + resource.getProject().getName()
86 + ".", null);
87 return NO_REVISIONS;
90 root = walk.parseCommit(headId);
91 if ((flags & SINGLE_REVISION) == SINGLE_REVISION) {
92 // If all Eclipse wants is one revision it probably is
93 // for the editor "quick diff" feature. We can pass off
94 // just the repository HEAD, even though it may not be
95 // the revision that most recently modified the path.
97 final CommitFileRevision single;
98 single = new CommitFileRevision(db, root, gitPath);
99 return new IFileRevision[] { single };
102 walk.markStart(root);
103 } catch (IOException e) {
104 Activator.logError("Invalid HEAD revision for project "
105 + resource.getProject().getName() + ".", e);
106 return NO_REVISIONS;
109 final KidCommitList list = new KidCommitList();
110 list.source(walk);
111 try {
112 for (;;) {
113 final int oldsz = list.size();
114 list.fillTo(oldsz + BATCH_SIZE - 1);
115 if (oldsz == list.size())
116 break;
117 if (monitor != null && monitor.isCanceled())
118 break;
120 } catch (IOException e) {
121 Activator.logError("Error parsing history for "
122 + resource.getFullPath() + ".", e);
123 return NO_REVISIONS;
126 final IFileRevision[] r = new IFileRevision[list.size()];
127 for (int i = 0; i < r.length; i++)
128 r[i] = new CommitFileRevision(db, list.get(i), gitPath);
129 return r;
132 public IFileRevision[] getContributors(final IFileRevision ifr) {
133 if (!(ifr instanceof CommitFileRevision))
134 return NO_REVISIONS;
136 final CommitFileRevision rev = (CommitFileRevision) ifr;
137 final Repository db = walk.getRepository();
138 final String p = rev.getGitPath();
139 final RevCommit c = rev.getRevCommit();
140 final IFileRevision[] r = new IFileRevision[c.getParentCount()];
141 for (int i = 0; i < r.length; i++)
142 r[i] = new CommitFileRevision(db, c.getParent(i), p);
143 return r;
146 public IFileRevision[] getTargets(final IFileRevision ifr) {
147 if (!(ifr instanceof CommitFileRevision))
148 return NO_REVISIONS;
150 final CommitFileRevision rev = (CommitFileRevision) ifr;
151 final Repository db = walk.getRepository();
152 final String p = rev.getGitPath();
153 final RevCommit rc = rev.getRevCommit();
154 if (!(rc instanceof KidCommit))
155 return NO_REVISIONS;
157 final KidCommit c = (KidCommit) rc;
158 final IFileRevision[] r = new IFileRevision[c.children.length];
159 for (int i = 0; i < r.length; i++)
160 r[i] = new CommitFileRevision(db, c.children[i], p);
161 return r;
164 public IFileRevision getFileRevision(final String id) {
165 if (id == null || id.equals("") || GitFileRevision.WORKSPACE.equals(id))
166 return new WorkspaceFileRevision(resource);
167 if (GitFileRevision.INDEX.equals(id))
168 return new IndexFileRevision(walk.getRepository(), gitPath);
170 // If we have no walk we somehow failed to initialize during our
171 // constructor, but there is no clear way to report errors other
172 // than to just construct yourself anyway and then later return
173 // no results to the user.
175 if (walk == null)
176 return null;
178 try {
179 final ObjectId binId = ObjectId.fromString(id);
180 final Repository db = walk.getRepository();
181 return new CommitFileRevision(db, walk.parseCommit(binId), gitPath);
182 } catch (IllegalArgumentException e) {
183 return null;
184 } catch (IOException e) {
185 // The interface definition suggests we should be returning
186 // null if the supplied id string is invalid for a revision.
188 Activator.logError("Error commit for file revision " + id + " of "
189 + resource.getFullPath(), e);
190 return null;
194 public IFileRevision[] getFileRevisions() {
195 final IFileRevision[] r = new IFileRevision[revisions.length];
196 System.arraycopy(revisions, 0, r, 0, r.length);
197 return r;
200 public Object getAdapter(Class adapter) {
201 return null;