1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.revwalk;
12
13 import java.io.IOException;
14 import java.text.MessageFormat;
15 import java.util.LinkedList;
16
17 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
18 import org.eclipse.jgit.errors.MissingObjectException;
19 import org.eclipse.jgit.internal.JGitText;
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 class MergeBaseGenerator extends Generator {
38 private static final int PARSED = RevWalk.PARSED;
39 private static final int IN_PENDING = RevWalk.SEEN;
40 private static final int POPPED = RevWalk.TEMP_MARK;
41 private static final int MERGE_BASE = RevWalk.REWRITE;
42
43 private final RevWalk walker;
44 private final DateRevQueue pending;
45
46 private int branchMask;
47 private int recarryTest;
48 private int recarryMask;
49 private int mergeBaseAncestor = -1;
50 private LinkedList<RevCommit> ret = new LinkedList<>();
51
52 private CarryStack stack;
53
54 MergeBaseGenerator(RevWalk w) {
55 super(w.isFirstParent());
56 walker = w;
57 pending = new DateRevQueue(firstParent);
58 }
59
60 void init(AbstractRevQueue p) throws IOException {
61 try {
62 for (;;) {
63 final RevCommit c = p.next();
64 if (c == null)
65 break;
66 add(c);
67 }
68
69
70
71 recarryTest = branchMask | POPPED;
72 recarryMask = branchMask | POPPED | MERGE_BASE;
73 mergeBaseAncestor = walker.allocFlag();
74
75 for (;;) {
76 RevCommit c = _next();
77 if (c == null) {
78 break;
79 }
80 ret.add(c);
81 }
82 } finally {
83
84
85
86 walker.freeFlag(branchMask | mergeBaseAncestor);
87 }
88 }
89
90 private void add(RevCommit c) {
91 final int flag = walker.allocFlag();
92 branchMask |= flag;
93 if ((c.flags & branchMask) != 0) {
94
95
96
97
98 throw new IllegalStateException(MessageFormat.format(JGitText.get().staleRevFlagsOn, c.name()));
99 }
100 c.flags |= flag;
101 pending.add(c);
102 }
103
104 @Override
105 int outputType() {
106 return 0;
107 }
108
109 private RevCommit _next() throws MissingObjectException,
110 IncorrectObjectTypeException, IOException {
111 for (;;) {
112 final RevCommit c = pending.next();
113 if (c == null) {
114 return null;
115 }
116
117 for (RevCommit p : c.parents) {
118 if ((p.flags & IN_PENDING) != 0)
119 continue;
120 if ((p.flags & PARSED) == 0)
121 p.parseHeaders(walker);
122 p.flags |= IN_PENDING;
123 pending.add(p);
124 }
125
126 int carry = c.flags & branchMask;
127 boolean mb = carry == branchMask;
128 if (mb) {
129
130
131
132
133 carry |= MERGE_BASE | mergeBaseAncestor;
134 }
135 carryOntoHistory(c, carry);
136
137 if ((c.flags & MERGE_BASE) != 0) {
138
139
140
141
142
143 if (pending.everbodyHasFlag(MERGE_BASE))
144 return null;
145 continue;
146 }
147 c.flags |= POPPED;
148
149 if (mb) {
150 c.flags |= MERGE_BASE;
151 return c;
152 }
153 }
154 }
155
156 @Override
157 RevCommit next() throws MissingObjectException,
158 IncorrectObjectTypeException, IOException {
159 while (!ret.isEmpty()) {
160 RevCommit commit = ret.remove();
161 if ((commit.flags & mergeBaseAncestor) == 0) {
162 return commit;
163 }
164 }
165 return null;
166 }
167
168 private void carryOntoHistory(RevCommit c, int carry) {
169 stack = null;
170 for (;;) {
171 carryOntoHistoryInnerLoop(c, carry);
172 if (stack == null) {
173 break;
174 }
175 c = stack.c;
176 carry = stack.carry;
177 stack = stack.prev;
178 }
179 }
180
181 private void carryOntoHistoryInnerLoop(RevCommit c, int carry) {
182 for (;;) {
183 RevCommit[] parents = c.parents;
184 if (parents == null || parents.length == 0) {
185 break;
186 }
187
188 int e = parents.length - 1;
189 for (int i = 0; i < e; i++) {
190 RevCommit p = parents[i];
191 if (carryOntoOne(p, carry) == CONTINUE) {
192
193 stack = new CarryStack(stack, p, carry);
194 }
195
196
197
198 }
199
200 c = parents[e];
201 if (carryOntoOne(c, carry) != CONTINUE) {
202 break;
203 }
204 }
205 }
206
207 private static final int CONTINUE = 0;
208 private static final int HAVE_ALL = 1;
209 private static final int CONTINUE_ON_STACK = 2;
210
211 private int carryOntoOne(RevCommit p, int carry) {
212
213
214
215
216
217 int rc = (p.flags & carry) == carry ? HAVE_ALL : CONTINUE;
218 p.flags |= carry;
219
220 if ((p.flags & recarryMask) == recarryTest) {
221
222
223
224
225 p.flags &= ~POPPED;
226 pending.add(p);
227 stack = new CarryStack(stack, p, branchMask | MERGE_BASE);
228 return CONTINUE_ON_STACK;
229 }
230 return rc;
231 }
232
233 private static class CarryStack {
234 final CarryStack prev;
235 final RevCommit c;
236 final int carry;
237
238 CarryStack(CarryStack prev, RevCommit c, int carry) {
239 this.prev = prev;
240 this.c = c;
241 this.carry = carry;
242 }
243 }
244 }