Initial EGit contribution to eclipse.org
[egit.git] / org.eclipse.egit.core / src / org / eclipse / egit / core / internal / storage / GitFileHistory.java
blob377b6abcd121b7f1b87b165f3460d9b02f6bb93a
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 * which accompanies this distribution, and is available at
8 * http://www.eclipse.org/legal/epl-v10.html
9 *******************************************************************************/
10 package org.eclipse.egit.core.internal.storage;
12 import java.io.IOException;
13 import java.util.Collections;
15 import org.eclipse.core.resources.IResource;
16 import org.eclipse.core.runtime.IAdaptable;
17 import org.eclipse.core.runtime.IProgressMonitor;
18 import org.eclipse.egit.core.Activator;
19 import org.eclipse.egit.core.project.RepositoryMapping;
20 import org.eclipse.team.core.history.IFileHistoryProvider;
21 import org.eclipse.team.core.history.IFileRevision;
22 import org.eclipse.team.core.history.provider.FileHistory;
23 import org.eclipse.jgit.lib.AnyObjectId;
24 import org.eclipse.jgit.lib.Constants;
25 import org.eclipse.jgit.lib.Repository;
26 import org.eclipse.jgit.revwalk.RevCommit;
27 import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
28 import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
29 import org.eclipse.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();
55 revisions = buildRevisions(monitor, flags);
58 private KidWalk buildWalk() {
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 // Only return a revision if it was matched by this filtered history
171 for (IFileRevision r : revisions) {
172 if (r.getContentIdentifier().equals(id))
173 return r;
175 return null;
178 public IFileRevision[] getFileRevisions() {
179 final IFileRevision[] r = new IFileRevision[revisions.length];
180 System.arraycopy(revisions, 0, r, 0, r.length);
181 return r;
184 public Object getAdapter(Class adapter) {
185 return null;