Fixed issue #1991: Disable (by default) certain operations with stash commits
[TortoiseGit.git] / src / TortoiseProc / lanes.cpp
blobacfc478e2501739c67488dfc5245c897e6540d89
1 /*
2 Description: history graph computation
4 Author: Marco Costalba (C) 2005-2007
6 Copyright: See COPYING file that comes with this distribution
8 */
9 #include "stdafx.h"
10 #include "lanes.h"
12 #define IS_NODE(x) (x == NODE || x == NODE_R || x == NODE_L)
15 void Lanes::init(const CGitHash& expectedSha) {
17 clear();
18 activeLane = 0;
19 setBoundary(false);
20 bool wasEmptyCross = false;
21 add(BRANCH, expectedSha, activeLane, wasEmptyCross);
24 void Lanes::clear() {
26 typeVec.clear();
27 nextShaVec.clear();
30 void Lanes::setBoundary(bool b) {
31 // changes the state so must be called as first one
33 NODE = b ? BOUNDARY_C : MERGE_FORK;
34 NODE_R = b ? BOUNDARY_R : MERGE_FORK_R;
35 NODE_L = b ? BOUNDARY_L : MERGE_FORK_L;
36 boundary = b;
38 if (boundary)
39 typeVec[activeLane] = BOUNDARY;
42 bool Lanes::isFork(const CGitHash& sha, bool& isDiscontinuity) {
44 int pos = findNextSha(sha, 0);
45 isDiscontinuity = (activeLane != pos);
46 if (pos == -1) // new branch case
47 return false;
49 return (findNextSha(sha, pos + 1) != -1);
51 int cnt = 0;
52 while (pos != -1) {
53 cnt++;
54 pos = findNextSha(sha, pos + 1);
55 // if (isDiscontinuity)
56 // isDiscontinuity = (activeLane != pos);
58 return (cnt > 1);
62 void Lanes::setFork(const CGitHash& sha) {
64 int rangeStart, rangeEnd, idx;
65 rangeStart = rangeEnd = idx = findNextSha(sha, 0);
67 while (idx != -1) {
68 rangeEnd = idx;
69 typeVec[idx] = TAIL;
70 idx = findNextSha(sha, idx + 1);
72 typeVec[activeLane] = NODE;
74 int& startT = typeVec[rangeStart];
75 int& endT = typeVec[rangeEnd];
77 if (startT == NODE)
78 startT = NODE_L;
80 if (endT == NODE)
81 endT = NODE_R;
83 if (startT == TAIL)
84 startT = TAIL_L;
86 if (endT == TAIL)
87 endT = TAIL_R;
89 for (int i = rangeStart + 1; i < rangeEnd; ++i) {
91 int& t = typeVec[i];
93 if (t == NOT_ACTIVE)
94 t = CROSS;
96 else if (t == EMPTY)
97 t = CROSS_EMPTY;
101 void Lanes::setMerge(const CGitHashList& parents) {
102 // setFork() must be called before setMerge()
104 if (boundary)
105 return; // handle as a simple active line
107 int& t = typeVec[activeLane];
108 bool wasFork = (t == NODE);
109 bool wasFork_L = (t == NODE_L);
110 bool wasFork_R = (t == NODE_R);
111 bool startJoinWasACross = false, endJoinWasACross = false;
112 bool endWasEmptyCross = false;
114 t = NODE;
116 int rangeStart = activeLane, rangeEnd = activeLane;
117 CGitHashList::const_iterator it(parents.begin());
118 for (++it; it != parents.end(); ++it) { // skip first parent
120 int idx = findNextSha(*it, 0);
121 if (idx != -1) {
123 if (idx > rangeEnd) {
125 rangeEnd = idx;
126 endJoinWasACross = typeVec[idx] == CROSS;
129 if (idx < rangeStart) {
131 rangeStart = idx;
132 startJoinWasACross = typeVec[idx] == CROSS;
135 typeVec[idx] = JOIN;
137 else
138 rangeEnd = add(HEAD, *it, rangeEnd + 1, endWasEmptyCross);
140 int& startT = typeVec[rangeStart];
141 int& endT = typeVec[rangeEnd];
143 if (startT == NODE && !wasFork && !wasFork_R)
144 startT = NODE_L;
146 if (endT == NODE && !wasFork && !wasFork_L)
147 endT = NODE_R;
149 if (startT == JOIN && !startJoinWasACross)
150 startT = JOIN_L;
152 if (endT == JOIN && !endJoinWasACross)
153 endT = JOIN_R;
155 if (startT == HEAD)
156 startT = HEAD_L;
158 if (endT == HEAD && !endWasEmptyCross)
159 endT = HEAD_R;
161 for (int i = rangeStart + 1; i < rangeEnd; ++i) {
163 int& t = typeVec[i];
165 if (t == NOT_ACTIVE)
166 t = CROSS;
168 else if (t == EMPTY)
169 t = CROSS_EMPTY;
171 else if (t == TAIL_R || t == TAIL_L)
172 t = TAIL;
176 void Lanes::setInitial() {
178 int& t = typeVec[activeLane];
179 if (!IS_NODE(t) && t != APPLIED)
180 t = (boundary ? BOUNDARY : INITIAL);
183 void Lanes::setApplied() {
185 // applied patches are not merges, nor forks
186 typeVec[activeLane] = APPLIED; // TODO test with boundaries
189 void Lanes::changeActiveLane(const CGitHash& sha) {
191 int& t = typeVec[activeLane];
192 if (t == INITIAL || isBoundary(t))
193 t = EMPTY;
194 else
195 t = NOT_ACTIVE;
197 int idx = findNextSha(sha, 0); // find first sha
198 if (idx != -1)
199 typeVec[idx] = ACTIVE; // called before setBoundary()
200 else {
201 bool wasEmptyCross = false;
202 idx = add(BRANCH, sha, activeLane, wasEmptyCross); // new branch
205 activeLane = idx;
208 void Lanes::afterMerge() {
210 if (boundary)
211 return; // will be reset by changeActiveLane()
213 for (unsigned int i = 0; i < typeVec.size(); ++i) {
215 int& t = typeVec[i];
217 if (isHead(t) || isJoin(t) || t == CROSS)
218 t = NOT_ACTIVE;
220 else if (t == CROSS_EMPTY)
221 t = EMPTY;
223 else if (IS_NODE(t))
224 t = ACTIVE;
228 void Lanes::afterFork() {
230 for (unsigned int i = 0; i < typeVec.size(); ++i) {
232 int& t = typeVec[i];
234 if (t == CROSS)
235 t = NOT_ACTIVE;
237 else if (isTail(t) || t == CROSS_EMPTY)
238 t = EMPTY;
240 if (!boundary && IS_NODE(t))
241 t = ACTIVE; // boundary will be reset by changeActiveLane()
243 while (typeVec.back() == EMPTY) {
244 typeVec.pop_back();
245 nextShaVec.pop_back();
249 bool Lanes::isBranch() {
251 return (typeVec[activeLane] == BRANCH);
254 void Lanes::afterBranch() {
256 typeVec[activeLane] = ACTIVE; // TODO test with boundaries
259 void Lanes::afterApplied() {
261 typeVec[activeLane] = ACTIVE; // TODO test with boundaries
264 void Lanes::nextParent(const CGitHash& sha) {
266 if(boundary)
267 nextShaVec[activeLane].Empty();
268 else
269 nextShaVec[activeLane] = sha;
272 int Lanes::findNextSha(const CGitHash& next, int pos) {
274 for (unsigned int i = pos; i < nextShaVec.size(); ++i)
275 if (nextShaVec[i] == next)
276 return i;
277 return -1;
280 int Lanes::findType(int type, int pos) {
282 for (unsigned int i = pos; i < typeVec.size(); ++i)
283 if (typeVec[i] == type)
284 return i;
285 return -1;
288 int Lanes::add(int type, const CGitHash& next, int pos, bool& wasEmptyCross) {
290 wasEmptyCross = false;
291 // first check empty lanes starting from pos
292 if (pos < (int)typeVec.size()) {
293 int posEmpty = findType(EMPTY, pos);
294 int posCrossEmpty = findType(CROSS_EMPTY, pos);
295 // Use first "empty" position.
296 if (posEmpty != -1 && posCrossEmpty != -1)
297 pos = min(posEmpty, posCrossEmpty);
298 else if (posEmpty != -1)
299 pos = posEmpty;
300 else if (posCrossEmpty != -1)
301 pos = posCrossEmpty;
302 else
303 pos = -1;
305 if (pos != -1) {
306 wasEmptyCross = (pos == posCrossEmpty);
308 typeVec[pos] = type;
309 nextShaVec[pos] = next;
310 return pos;
313 // if all lanes are occupied add a new lane
314 typeVec.push_back(type);
315 nextShaVec.push_back(next);
316 return (int)typeVec.size() - 1;