Sort HEAD high in the history view.
[egit.git] / org.spearce.jgit / src / org / spearce / jgit / lib / Walker.java
blob7f4b6901740dd3e06e1f73f9072cb8f841541de4
1 /**
2 *
3 */
4 package org.spearce.jgit.lib;
6 import java.io.IOException;
7 import java.util.ArrayList;
8 import java.util.Arrays;
9 import java.util.Collection;
10 import java.util.Iterator;
11 import java.util.Map;
13 public abstract class Walker {
14 private String[] relativeResourceName;
15 private boolean leafIsBlob;
16 private boolean followMainOnly;
17 protected Repository repository;
18 private ObjectId activeDiffLeafId;
19 protected final Commit[] starts;
20 private final Boolean merges;
21 private Map donewith = new ObjectIdMap();
22 private Collection<Todo> todo = new ArrayList<Todo>(20000);
24 protected abstract boolean isCancelled();
26 protected abstract void collect(Commit commit, int count,int breadth);
27 protected abstract void record(ObjectId pred, ObjectId succ);
29 /**
30 * Create a revision walker. A revision walker traverses the revision tree
31 * and collects revisions that represents changes. The first revision is
32 * a state outside the commit and is returned as null in the list. The
33 * changes are detected in reverse order, i.e. we start with the current
34 * state as represented by activeDiffLeafId and then we list all commits
35 * that introduces a change against the older state.
37 * Consider these states
38 * CURRENT
39 * HEAD
40 * HEAD~1
41 * HEAD~2
42 * HEAD~3
44 * If the state of CURRENT and HEAD is the same, but HEAD~1 has a different
45 * state, the HEAD is listed..
47 * If the state of CURRENT and HEAD is different CURRENT is listed. The default
48 * impleementation listes it as a null commit (without a commit id).
50 * @param repostory The repository to scan
51 * @param starts HEAD in this context
52 * @param relativeResourceName The path to log, split by path components
53 * @param leafIsBlob We refer to a CURRENT state which is a blob
54 * @param followMainOnly Follow the first parent only
55 * @param merges Include or eclude merges or both as a tristate Boolean.
56 * @param activeDiffLeafId a SHA-1 or null to start comparing with.2
58 protected Walker(Repository repostory, Commit[] starts,
59 String[] relativeResourceName, boolean leafIsBlob,
60 boolean followMainOnly, Boolean merges, ObjectId activeDiffLeafId) {
61 this.repository = repostory;
62 this.starts = starts;
63 this.relativeResourceName = relativeResourceName;
64 this.leafIsBlob = leafIsBlob;
65 this.followMainOnly = followMainOnly;
66 this.merges = merges;
67 this.activeDiffLeafId = activeDiffLeafId;
70 /**
71 * Entry point for executing the collection process.
73 * @return a Collection of Commit's (default), but subclasses could return another type.
75 public Collection collectHistory() {
76 for (int i=0; i<starts.length; ++i) {
77 ObjectId[] initialResourceHash = new ObjectId[relativeResourceName.length];
78 Arrays.fill(initialResourceHash, ObjectId.zeroId());
79 Commit initialPrevious;
80 // The first commit is special. We compare it with a reference
81 if (i == 0) {
82 if (activeDiffLeafId != null && initialResourceHash.length > 0) {
83 initialResourceHash[initialResourceHash.length-1] = activeDiffLeafId;
84 initialPrevious = new Commit(repository);
85 initialPrevious.setCommitId(ObjectId.zeroId());
86 record(null, initialPrevious.getCommitId());
87 } else {
88 record(ObjectId.zeroId(), starts[i].getCommitId());
89 initialPrevious = null;
91 } else {
92 initialPrevious = null;
94 todo.add(new Todo(0, 0, initialResourceHash, null, starts[i], initialPrevious));
96 for (Iterator<Todo> ti = todo.iterator(); ti.hasNext(); ti=todo.iterator()) {
97 Todo next = ti.next();
98 try {
99 next.run();
100 } catch (IOException e) {
101 // TODO Auto-generated catch block
102 e.printStackTrace();
104 todo.remove(next);
106 donewith = null; // turn over to gc
107 return null;
110 class Todo {
111 private int count;
112 private int breadth;
113 private ObjectId[] lastResourceHash;
114 private TreeEntry lastEntry;
115 private Commit top;
116 private Commit previous;
118 @Override
119 public boolean equals(Object other) {
120 return top.equals(((Todo)other).top);
123 @Override
124 public int hashCode() {
125 return top.hashCode();
128 Todo(int count, int breadth, ObjectId[] lastResourceHash, TreeEntry lastEntry,
129 Commit top, Commit previous) {
130 this.count = count;
131 this.breadth = breadth;
132 this.previous = previous;
133 this.lastResourceHash = new ObjectId[lastResourceHash.length];
134 for (int i=0; i<lastResourceHash.length; ++i)
135 this.lastResourceHash[i] = lastResourceHash[i];
136 this.lastEntry = lastEntry;
137 this.top = top;
138 assert previous==null || !previous.equals(top);
140 void run() throws IOException {
141 // System.out.println("todo.run(+"+top+"...");
142 if (top == null)
143 return;
144 Commit current = top;
146 do {
147 TreeEntry currentEntry = lastEntry;
148 ObjectId[] currentResourceHash = new ObjectId[lastResourceHash.length];
149 Tree t = current!=null ? current.getTree() : null;
150 for (int i = 0; i < currentResourceHash.length; ++i) {
151 TreeEntry m;
152 if (t != null)
153 if (i == relativeResourceName.length-1 && leafIsBlob)
154 m = t.findBlobMember(relativeResourceName[i]);
155 else
156 m = t.findTreeMember(relativeResourceName[i]);
157 else
158 m = null;
160 if (m != null) {
161 ObjectId id = m.getId();
162 currentResourceHash[i] = id;
163 if (id.equals(lastResourceHash[i])) {
164 while (++i < currentResourceHash.length) {
165 currentResourceHash[i] = lastResourceHash[i];
167 } else {
168 if (m instanceof Tree) {
169 t = (Tree)m;
170 } else {
171 if (i == currentResourceHash.length - 1) {
172 currentEntry = m;
173 } else {
174 currentEntry = null;
175 while (++i < currentResourceHash.length) {
176 currentResourceHash[i] = ObjectId.zeroId();
181 } else {
182 for (; i < currentResourceHash.length; ++i) {
183 currentResourceHash[i] = ObjectId.zeroId();
188 if (previous != null) {
189 if (currentResourceHash.length == 0 || !currentResourceHash[currentResourceHash.length-1].equals(lastResourceHash[currentResourceHash.length-1])) {
190 ObjectId[] pparents = previous.getParentIds();
191 if (current != previous) {
192 if (merges == null
193 || merges.booleanValue() && pparents.length > 1
194 || !merges.booleanValue() && pparents.length <= 1) {
195 collect(previous, count, breadth);
201 record(previous!=null ? previous.getCommitId() : null, current!=null ? current.getCommitId() : null);
203 if (current != null) {
204 if (!followMainOnly && donewith.put(current.getCommitId(), current.getCommitId()) != null) {
205 previous = current;
206 break;
210 lastResourceHash = currentResourceHash;
211 previous = current;
213 if (current != null) {
214 ObjectId[] parents = current.getParentIds();
215 if (!followMainOnly) {
216 for (int i = 1; i < parents.length; ++i) {
217 ObjectId mergeParentId = parents[i];
218 Commit mergeParent;
219 try {
220 mergeParent = repository.mapCommit(mergeParentId);
221 Todo newTodo = new Todo(0, breadth + 1, lastResourceHash,
222 currentEntry, mergeParent, previous);
223 if (!todo.contains(todo))
224 todo.add(newTodo);
225 } catch (IOException e) {
226 e.printStackTrace();
230 if (parents.length > 0) {
231 ObjectId parentId = parents[0];
232 try {
233 current = repository.mapCommit(parentId);
234 } catch (IOException e) {
235 e.printStackTrace();
236 current = null;
238 } else
239 current = null;
241 if (count>=0)
242 count++;
243 } while (previous != null && !isCancelled());