Fossil SCM

Show cherrypick merges on the default timeline. Disable the display of cherrypicks using the "ncp" query parameter. Currently, the only display difference between a normal merge and a cherrypick merges is in the final horizontal segment of the merge line as it moves into the child node. More work is needed on the display logic.

drh 2018-12-27 19:39 trunk
Commit 55ab522ceee25230fb628d9e8cafbdb5fb11143d5e25bd5f0f0a9284f97ae5bc
--- src/default_css.txt
+++ src/default_css.txt
@@ -158,10 +158,25 @@
158158
.tl-arrow.merge.r {
159159
border-left: 3px solid #000;
160160
}
161161
.tl-line.merge {
162162
width: 1px;
163
+}
164
+.tl-arrow.cherrypick {
165
+ height: 1px;
166
+ border-width: 2px 0;
167
+}
168
+.tl-arrow.cherrypick.l {
169
+ border-right: 3px solid #000;
170
+}
171
+.tl-arrow.cherrypick.r {
172
+ border-left: 3px solid #000;
173
+}
174
+.tl-line.cherrypick {
175
+ width: 0px;
176
+ border-top: 1px dashed #000;
177
+ background: #fff;
163178
}
164179
.tl-arrow.warp {
165180
margin-left: 1px;
166181
border-width: 3px 0;
167182
border-left: 7px solid #600000;
168183
--- src/default_css.txt
+++ src/default_css.txt
@@ -158,10 +158,25 @@
158 .tl-arrow.merge.r {
159 border-left: 3px solid #000;
160 }
161 .tl-line.merge {
162 width: 1px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163 }
164 .tl-arrow.warp {
165 margin-left: 1px;
166 border-width: 3px 0;
167 border-left: 7px solid #600000;
168
--- src/default_css.txt
+++ src/default_css.txt
@@ -158,10 +158,25 @@
158 .tl-arrow.merge.r {
159 border-left: 3px solid #000;
160 }
161 .tl-line.merge {
162 width: 1px;
163 }
164 .tl-arrow.cherrypick {
165 height: 1px;
166 border-width: 2px 0;
167 }
168 .tl-arrow.cherrypick.l {
169 border-right: 3px solid #000;
170 }
171 .tl-arrow.cherrypick.r {
172 border-left: 3px solid #000;
173 }
174 .tl-line.cherrypick {
175 width: 0px;
176 border-top: 1px dashed #000;
177 background: #fff;
178 }
179 .tl-arrow.warp {
180 margin-left: 1px;
181 border-width: 3px 0;
182 border-left: 7px solid #600000;
183
+1 -1
--- src/finfo.c
+++ src/finfo.c
@@ -503,11 +503,11 @@
503503
zBgClr = hash_color(zUser);
504504
}else if( brBg || zBgClr==0 || zBgClr[0]==0 ){
505505
zBgClr = strcmp(zBr,"trunk")==0 ? "" : hash_color(zBr);
506506
}
507507
gidx = graph_add_row(pGraph, frid>0 ? frid : fpid+1000000000,
508
- nParent, aParent, zBr, zBgClr,
508
+ nParent, 0, aParent, zBr, zBgClr,
509509
zUuid, 0);
510510
if( strncmp(zDate, zPrevDate, 10) ){
511511
sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate);
512512
@ <tr><td>
513513
@ <div class="divider timelineDate">%s(zPrevDate)</div>
514514
--- src/finfo.c
+++ src/finfo.c
@@ -503,11 +503,11 @@
503 zBgClr = hash_color(zUser);
504 }else if( brBg || zBgClr==0 || zBgClr[0]==0 ){
505 zBgClr = strcmp(zBr,"trunk")==0 ? "" : hash_color(zBr);
506 }
507 gidx = graph_add_row(pGraph, frid>0 ? frid : fpid+1000000000,
508 nParent, aParent, zBr, zBgClr,
509 zUuid, 0);
510 if( strncmp(zDate, zPrevDate, 10) ){
511 sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate);
512 @ <tr><td>
513 @ <div class="divider timelineDate">%s(zPrevDate)</div>
514
--- src/finfo.c
+++ src/finfo.c
@@ -503,11 +503,11 @@
503 zBgClr = hash_color(zUser);
504 }else if( brBg || zBgClr==0 || zBgClr[0]==0 ){
505 zBgClr = strcmp(zBr,"trunk")==0 ? "" : hash_color(zBr);
506 }
507 gidx = graph_add_row(pGraph, frid>0 ? frid : fpid+1000000000,
508 nParent, 0, aParent, zBr, zBgClr,
509 zUuid, 0);
510 if( strncmp(zDate, zPrevDate, 10) ){
511 sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate);
512 @ <tr><td>
513 @ <div class="divider timelineDate">%s(zPrevDate)</div>
514
+34 -10
--- src/graph.c
+++ src/graph.c
@@ -34,10 +34,12 @@
3434
** but which are included just so that we can capture their background color.
3535
*/
3636
struct GraphRow {
3737
int rid; /* The rid for the check-in */
3838
i8 nParent; /* Number of parents. -1 for technote lines */
39
+ i8 nCherrypick; /* Subset of aParent that are cherrypicks */
40
+ i8 nNonCherrypick; /* Number of non-cherrypick parents */
3941
int *aParent; /* Array of parents. 0 element is primary .*/
4042
char *zBranch; /* Branch name */
4143
char *zBgClr; /* Background Color */
4244
char zUuid[HNAME_MAX+1]; /* Check-in for file ID */
4345
@@ -55,10 +57,11 @@
5557
i8 mergeOut; /* Merge out to this rail. -1 if no merge-out */
5658
u8 mergeIn[GR_MAX_RAIL]; /* Merge in from non-zero rails */
5759
int aiRiser[GR_MAX_RAIL]; /* Risers from this node to a higher row. */
5860
int mergeUpto; /* Draw the mergeOut rail up to this level */
5961
u64 mergeDown; /* Draw merge lines up from bottom of graph */
62
+ u64 cherrypickDown; /* Draw cherrypick lines up from bottom */
6063
6164
u64 railInUse; /* Mask of occupied rails at this row */
6265
};
6366
6467
/* Context while building a graph
@@ -179,10 +182,11 @@
179182
*/
180183
int graph_add_row(
181184
GraphContext *p, /* The context to which the row is added */
182185
int rid, /* RID for the check-in */
183186
int nParent, /* Number of parents */
187
+ int nCherrypick, /* How many of aParent[] are actually cherrypicks */
184188
int *aParent, /* Array of parents */
185189
const char *zBranch, /* Branch for this check-in */
186190
const char *zBgClr, /* Background color. NULL or "" for white. */
187191
const char *zUuid, /* hash name of the object being graphed */
188192
int isLeaf /* True if this row is a leaf */
@@ -195,11 +199,16 @@
195199
nByte = sizeof(GraphRow);
196200
if( nParent>0 ) nByte += sizeof(pRow->aParent[0])*nParent;
197201
pRow = (GraphRow*)safeMalloc( nByte );
198202
pRow->aParent = nParent>0 ? (int*)&pRow[1] : 0;
199203
pRow->rid = rid;
204
+ if( nCherrypick>=nParent ){
205
+ nCherrypick = nParent-1; /* Safety. Should never happen. */
206
+ }
200207
pRow->nParent = nParent;
208
+ pRow->nCherrypick = nCherrypick;
209
+ pRow->nNonCherrypick = nParent - nCherrypick;
201210
pRow->zBranch = persistBranchName(p, zBranch);
202211
if( zUuid==0 ) zUuid = "";
203212
sqlite3_snprintf(sizeof(pRow->zUuid), pRow->zUuid, "%s", zUuid);
204213
pRow->isLeaf = isLeaf;
205214
memset(pRow->aiRiser, -1, sizeof(pRow->aiRiser));
@@ -284,11 +293,12 @@
284293
** Create a merge-arrow riser going from pParent up to pChild.
285294
*/
286295
static void createMergeRiser(
287296
GraphContext *p,
288297
GraphRow *pParent,
289
- GraphRow *pChild
298
+ GraphRow *pChild,
299
+ int isCherrypick
290300
){
291301
int u;
292302
u64 mask;
293303
GraphRow *pLoop;
294304
@@ -311,11 +321,11 @@
311321
pLoop=pLoop->pNext){
312322
pLoop->railInUse |= mask;
313323
}
314324
}
315325
}
316
- pChild->mergeIn[pParent->mergeOut] = 1;
326
+ pChild->mergeIn[pParent->mergeOut] = isCherrypick ? 2 : 1;
317327
}
318328
319329
/*
320330
** Compute the maximum rail number.
321331
*/
@@ -323,11 +333,13 @@
323333
GraphRow *pRow;
324334
p->mxRail = 0;
325335
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
326336
if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail;
327337
if( pRow->mergeOut>p->mxRail ) p->mxRail = pRow->mergeOut;
328
- while( p->mxRail<GR_MAX_RAIL && pRow->mergeDown>(BIT(p->mxRail+1)-1) ){
338
+ while( p->mxRail<GR_MAX_RAIL
339
+ && (pRow->mergeDown|pRow->cherrypickDown)>(BIT(p->mxRail+1)-1)
340
+ ){
329341
p->mxRail++;
330342
}
331343
}
332344
}
333345
@@ -395,11 +407,18 @@
395407
*/
396408
if( omitDescenders ){
397409
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
398410
for(i=1; i<pRow->nParent; i++){
399411
if( hashFind(p, pRow->aParent[i])==0 ){
400
- pRow->aParent[i] = pRow->aParent[--pRow->nParent];
412
+ memmove(pRow->aParent+i, pRow->aParent+i+1,
413
+ sizeof(pRow->aParent[0])*(pRow->nParent-i-1));
414
+ pRow->nParent--;
415
+ if( i<pRow->nNonCherrypick ){
416
+ pRow->nNonCherrypick--;
417
+ }else{
418
+ pRow->nCherrypick--;
419
+ }
401420
i--;
402421
}
403422
}
404423
}
405424
}
@@ -408,15 +427,15 @@
408427
** other parents in the same branch, reorder the parents to make
409428
** the parent from the same branch the primary parent.
410429
*/
411430
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
412431
if( pRow->isDup ) continue;
413
- if( pRow->nParent<2 ) continue; /* Not a fork */
432
+ if( pRow->nNonCherrypick<2 ) continue; /* Not a fork */
414433
pParent = hashFind(p, pRow->aParent[0]);
415434
if( pParent==0 ) continue; /* Parent off-screen */
416435
if( pParent->zBranch==pRow->zBranch ) continue; /* Same branch */
417
- for(i=1; i<pRow->nParent; i++){
436
+ for(i=1; i<pRow->nNonCherrypick; i++){
418437
pParent = hashFind(p, pRow->aParent[i]);
419438
if( pParent && pParent->zBranch==pRow->zBranch ){
420439
int t = pRow->aParent[0];
421440
pRow->aParent[0] = pRow->aParent[i];
422441
pRow->aParent[i] = t;
@@ -565,18 +584,23 @@
565584
iMrail = findFreeRail(p, pRow->idx, p->nRow, 0);
566585
if( p->mxRail>=GR_MAX_RAIL ) return;
567586
mergeRiserFrom[iMrail] = parentRid;
568587
}
569588
mask = BIT(iMrail);
570
- pRow->mergeIn[iMrail] = 1;
571
- pRow->mergeDown |= mask;
589
+ if( i>=pRow->nNonCherrypick ){
590
+ pRow->mergeIn[iMrail] = 2;
591
+ pRow->cherrypickDown |= mask;
592
+ }else{
593
+ pRow->mergeIn[iMrail] = 1;
594
+ pRow->mergeDown |= mask;
595
+ }
572596
for(pLoop=pRow->pNext; pLoop; pLoop=pLoop->pNext){
573597
pLoop->railInUse |= mask;
574598
}
575599
}else{
576600
/* Merge from an on-screen node */
577
- createMergeRiser(p, pDesc, pRow);
601
+ createMergeRiser(p, pDesc, pRow, i>=pRow->nNonCherrypick);
578602
if( p->mxRail>=GR_MAX_RAIL ) return;
579603
}
580604
}
581605
}
582606
@@ -593,11 +617,11 @@
593617
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
594618
if( !pRow->isDup ) continue;
595619
pRow->iRail = dupRail;
596620
pDesc = hashFind(p, pRow->rid);
597621
assert( pDesc!=0 && pDesc!=pRow );
598
- createMergeRiser(p, pDesc, pRow);
622
+ createMergeRiser(p, pDesc, pRow, 0);
599623
if( pDesc->mergeOut>mxRail ) mxRail = pDesc->mergeOut;
600624
}
601625
if( dupRail<=mxRail ){
602626
dupRail = mxRail+1;
603627
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
604628
--- src/graph.c
+++ src/graph.c
@@ -34,10 +34,12 @@
34 ** but which are included just so that we can capture their background color.
35 */
36 struct GraphRow {
37 int rid; /* The rid for the check-in */
38 i8 nParent; /* Number of parents. -1 for technote lines */
 
 
39 int *aParent; /* Array of parents. 0 element is primary .*/
40 char *zBranch; /* Branch name */
41 char *zBgClr; /* Background Color */
42 char zUuid[HNAME_MAX+1]; /* Check-in for file ID */
43
@@ -55,10 +57,11 @@
55 i8 mergeOut; /* Merge out to this rail. -1 if no merge-out */
56 u8 mergeIn[GR_MAX_RAIL]; /* Merge in from non-zero rails */
57 int aiRiser[GR_MAX_RAIL]; /* Risers from this node to a higher row. */
58 int mergeUpto; /* Draw the mergeOut rail up to this level */
59 u64 mergeDown; /* Draw merge lines up from bottom of graph */
 
60
61 u64 railInUse; /* Mask of occupied rails at this row */
62 };
63
64 /* Context while building a graph
@@ -179,10 +182,11 @@
179 */
180 int graph_add_row(
181 GraphContext *p, /* The context to which the row is added */
182 int rid, /* RID for the check-in */
183 int nParent, /* Number of parents */
 
184 int *aParent, /* Array of parents */
185 const char *zBranch, /* Branch for this check-in */
186 const char *zBgClr, /* Background color. NULL or "" for white. */
187 const char *zUuid, /* hash name of the object being graphed */
188 int isLeaf /* True if this row is a leaf */
@@ -195,11 +199,16 @@
195 nByte = sizeof(GraphRow);
196 if( nParent>0 ) nByte += sizeof(pRow->aParent[0])*nParent;
197 pRow = (GraphRow*)safeMalloc( nByte );
198 pRow->aParent = nParent>0 ? (int*)&pRow[1] : 0;
199 pRow->rid = rid;
 
 
 
200 pRow->nParent = nParent;
 
 
201 pRow->zBranch = persistBranchName(p, zBranch);
202 if( zUuid==0 ) zUuid = "";
203 sqlite3_snprintf(sizeof(pRow->zUuid), pRow->zUuid, "%s", zUuid);
204 pRow->isLeaf = isLeaf;
205 memset(pRow->aiRiser, -1, sizeof(pRow->aiRiser));
@@ -284,11 +293,12 @@
284 ** Create a merge-arrow riser going from pParent up to pChild.
285 */
286 static void createMergeRiser(
287 GraphContext *p,
288 GraphRow *pParent,
289 GraphRow *pChild
 
290 ){
291 int u;
292 u64 mask;
293 GraphRow *pLoop;
294
@@ -311,11 +321,11 @@
311 pLoop=pLoop->pNext){
312 pLoop->railInUse |= mask;
313 }
314 }
315 }
316 pChild->mergeIn[pParent->mergeOut] = 1;
317 }
318
319 /*
320 ** Compute the maximum rail number.
321 */
@@ -323,11 +333,13 @@
323 GraphRow *pRow;
324 p->mxRail = 0;
325 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
326 if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail;
327 if( pRow->mergeOut>p->mxRail ) p->mxRail = pRow->mergeOut;
328 while( p->mxRail<GR_MAX_RAIL && pRow->mergeDown>(BIT(p->mxRail+1)-1) ){
 
 
329 p->mxRail++;
330 }
331 }
332 }
333
@@ -395,11 +407,18 @@
395 */
396 if( omitDescenders ){
397 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
398 for(i=1; i<pRow->nParent; i++){
399 if( hashFind(p, pRow->aParent[i])==0 ){
400 pRow->aParent[i] = pRow->aParent[--pRow->nParent];
 
 
 
 
 
 
 
401 i--;
402 }
403 }
404 }
405 }
@@ -408,15 +427,15 @@
408 ** other parents in the same branch, reorder the parents to make
409 ** the parent from the same branch the primary parent.
410 */
411 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
412 if( pRow->isDup ) continue;
413 if( pRow->nParent<2 ) continue; /* Not a fork */
414 pParent = hashFind(p, pRow->aParent[0]);
415 if( pParent==0 ) continue; /* Parent off-screen */
416 if( pParent->zBranch==pRow->zBranch ) continue; /* Same branch */
417 for(i=1; i<pRow->nParent; i++){
418 pParent = hashFind(p, pRow->aParent[i]);
419 if( pParent && pParent->zBranch==pRow->zBranch ){
420 int t = pRow->aParent[0];
421 pRow->aParent[0] = pRow->aParent[i];
422 pRow->aParent[i] = t;
@@ -565,18 +584,23 @@
565 iMrail = findFreeRail(p, pRow->idx, p->nRow, 0);
566 if( p->mxRail>=GR_MAX_RAIL ) return;
567 mergeRiserFrom[iMrail] = parentRid;
568 }
569 mask = BIT(iMrail);
570 pRow->mergeIn[iMrail] = 1;
571 pRow->mergeDown |= mask;
 
 
 
 
 
572 for(pLoop=pRow->pNext; pLoop; pLoop=pLoop->pNext){
573 pLoop->railInUse |= mask;
574 }
575 }else{
576 /* Merge from an on-screen node */
577 createMergeRiser(p, pDesc, pRow);
578 if( p->mxRail>=GR_MAX_RAIL ) return;
579 }
580 }
581 }
582
@@ -593,11 +617,11 @@
593 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
594 if( !pRow->isDup ) continue;
595 pRow->iRail = dupRail;
596 pDesc = hashFind(p, pRow->rid);
597 assert( pDesc!=0 && pDesc!=pRow );
598 createMergeRiser(p, pDesc, pRow);
599 if( pDesc->mergeOut>mxRail ) mxRail = pDesc->mergeOut;
600 }
601 if( dupRail<=mxRail ){
602 dupRail = mxRail+1;
603 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
604
--- src/graph.c
+++ src/graph.c
@@ -34,10 +34,12 @@
34 ** but which are included just so that we can capture their background color.
35 */
36 struct GraphRow {
37 int rid; /* The rid for the check-in */
38 i8 nParent; /* Number of parents. -1 for technote lines */
39 i8 nCherrypick; /* Subset of aParent that are cherrypicks */
40 i8 nNonCherrypick; /* Number of non-cherrypick parents */
41 int *aParent; /* Array of parents. 0 element is primary .*/
42 char *zBranch; /* Branch name */
43 char *zBgClr; /* Background Color */
44 char zUuid[HNAME_MAX+1]; /* Check-in for file ID */
45
@@ -55,10 +57,11 @@
57 i8 mergeOut; /* Merge out to this rail. -1 if no merge-out */
58 u8 mergeIn[GR_MAX_RAIL]; /* Merge in from non-zero rails */
59 int aiRiser[GR_MAX_RAIL]; /* Risers from this node to a higher row. */
60 int mergeUpto; /* Draw the mergeOut rail up to this level */
61 u64 mergeDown; /* Draw merge lines up from bottom of graph */
62 u64 cherrypickDown; /* Draw cherrypick lines up from bottom */
63
64 u64 railInUse; /* Mask of occupied rails at this row */
65 };
66
67 /* Context while building a graph
@@ -179,10 +182,11 @@
182 */
183 int graph_add_row(
184 GraphContext *p, /* The context to which the row is added */
185 int rid, /* RID for the check-in */
186 int nParent, /* Number of parents */
187 int nCherrypick, /* How many of aParent[] are actually cherrypicks */
188 int *aParent, /* Array of parents */
189 const char *zBranch, /* Branch for this check-in */
190 const char *zBgClr, /* Background color. NULL or "" for white. */
191 const char *zUuid, /* hash name of the object being graphed */
192 int isLeaf /* True if this row is a leaf */
@@ -195,11 +199,16 @@
199 nByte = sizeof(GraphRow);
200 if( nParent>0 ) nByte += sizeof(pRow->aParent[0])*nParent;
201 pRow = (GraphRow*)safeMalloc( nByte );
202 pRow->aParent = nParent>0 ? (int*)&pRow[1] : 0;
203 pRow->rid = rid;
204 if( nCherrypick>=nParent ){
205 nCherrypick = nParent-1; /* Safety. Should never happen. */
206 }
207 pRow->nParent = nParent;
208 pRow->nCherrypick = nCherrypick;
209 pRow->nNonCherrypick = nParent - nCherrypick;
210 pRow->zBranch = persistBranchName(p, zBranch);
211 if( zUuid==0 ) zUuid = "";
212 sqlite3_snprintf(sizeof(pRow->zUuid), pRow->zUuid, "%s", zUuid);
213 pRow->isLeaf = isLeaf;
214 memset(pRow->aiRiser, -1, sizeof(pRow->aiRiser));
@@ -284,11 +293,12 @@
293 ** Create a merge-arrow riser going from pParent up to pChild.
294 */
295 static void createMergeRiser(
296 GraphContext *p,
297 GraphRow *pParent,
298 GraphRow *pChild,
299 int isCherrypick
300 ){
301 int u;
302 u64 mask;
303 GraphRow *pLoop;
304
@@ -311,11 +321,11 @@
321 pLoop=pLoop->pNext){
322 pLoop->railInUse |= mask;
323 }
324 }
325 }
326 pChild->mergeIn[pParent->mergeOut] = isCherrypick ? 2 : 1;
327 }
328
329 /*
330 ** Compute the maximum rail number.
331 */
@@ -323,11 +333,13 @@
333 GraphRow *pRow;
334 p->mxRail = 0;
335 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
336 if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail;
337 if( pRow->mergeOut>p->mxRail ) p->mxRail = pRow->mergeOut;
338 while( p->mxRail<GR_MAX_RAIL
339 && (pRow->mergeDown|pRow->cherrypickDown)>(BIT(p->mxRail+1)-1)
340 ){
341 p->mxRail++;
342 }
343 }
344 }
345
@@ -395,11 +407,18 @@
407 */
408 if( omitDescenders ){
409 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
410 for(i=1; i<pRow->nParent; i++){
411 if( hashFind(p, pRow->aParent[i])==0 ){
412 memmove(pRow->aParent+i, pRow->aParent+i+1,
413 sizeof(pRow->aParent[0])*(pRow->nParent-i-1));
414 pRow->nParent--;
415 if( i<pRow->nNonCherrypick ){
416 pRow->nNonCherrypick--;
417 }else{
418 pRow->nCherrypick--;
419 }
420 i--;
421 }
422 }
423 }
424 }
@@ -408,15 +427,15 @@
427 ** other parents in the same branch, reorder the parents to make
428 ** the parent from the same branch the primary parent.
429 */
430 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
431 if( pRow->isDup ) continue;
432 if( pRow->nNonCherrypick<2 ) continue; /* Not a fork */
433 pParent = hashFind(p, pRow->aParent[0]);
434 if( pParent==0 ) continue; /* Parent off-screen */
435 if( pParent->zBranch==pRow->zBranch ) continue; /* Same branch */
436 for(i=1; i<pRow->nNonCherrypick; i++){
437 pParent = hashFind(p, pRow->aParent[i]);
438 if( pParent && pParent->zBranch==pRow->zBranch ){
439 int t = pRow->aParent[0];
440 pRow->aParent[0] = pRow->aParent[i];
441 pRow->aParent[i] = t;
@@ -565,18 +584,23 @@
584 iMrail = findFreeRail(p, pRow->idx, p->nRow, 0);
585 if( p->mxRail>=GR_MAX_RAIL ) return;
586 mergeRiserFrom[iMrail] = parentRid;
587 }
588 mask = BIT(iMrail);
589 if( i>=pRow->nNonCherrypick ){
590 pRow->mergeIn[iMrail] = 2;
591 pRow->cherrypickDown |= mask;
592 }else{
593 pRow->mergeIn[iMrail] = 1;
594 pRow->mergeDown |= mask;
595 }
596 for(pLoop=pRow->pNext; pLoop; pLoop=pLoop->pNext){
597 pLoop->railInUse |= mask;
598 }
599 }else{
600 /* Merge from an on-screen node */
601 createMergeRiser(p, pDesc, pRow, i>=pRow->nNonCherrypick);
602 if( p->mxRail>=GR_MAX_RAIL ) return;
603 }
604 }
605 }
606
@@ -593,11 +617,11 @@
617 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
618 if( !pRow->isDup ) continue;
619 pRow->iRail = dupRail;
620 pDesc = hashFind(p, pRow->rid);
621 assert( pDesc!=0 && pDesc!=pRow );
622 createMergeRiser(p, pDesc, pRow, 0);
623 if( pDesc->mergeOut>mxRail ) mxRail = pDesc->mergeOut;
624 }
625 if( dupRail<=mxRail ){
626 dupRail = mxRail+1;
627 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
628
+32 -6
--- src/graph.js
+++ src/graph.js
@@ -83,11 +83,12 @@
8383
parent.appendChild(canvasDiv);
8484
8585
var elems = {};
8686
var elemClasses = [
8787
"rail", "mergeoffset", "node", "arrow u", "arrow u sm", "line",
88
- "arrow merge r", "line merge", "arrow warp", "line warp"
88
+ "arrow merge r", "line merge", "arrow warp", "line warp",
89
+ "line cherrypick"
8990
];
9091
for( var i=0; i<elemClasses.length; i++ ){
9192
var cls = elemClasses[i];
9293
var elem = document.createElement("div");
9394
elem.className = "tl-" + cls;
@@ -103,10 +104,11 @@
103104
arrow = elems.arrow_u;
104105
arrowSmall = elems.arrow_u_sm;
105106
line = elems.line;
106107
mArrow = elems.arrow_merge_r;
107108
mLine = elems.line_merge;
109
+ cpLine = elems.line_cherrypick;
108110
wArrow = elems.arrow_warp;
109111
wLine = elems.line_warp;
110112
111113
var minRailPitch = Math.ceil((node.w+line.w)/2 + mArrow.w + 1);
112114
if( window.innerWidth<400 ){
@@ -186,14 +188,20 @@
186188
drawLine(line,color,x,y0,null,y1);
187189
x = to.x + (node.w-arw.w)/2;
188190
var n = drawBox(arw.cls,null,x,y);
189191
if(color) n.style.borderBottomColor = color;
190192
}
193
+ /* Draw thin horizontal or vertical lines representing merges */
191194
function drawMergeLine(x0,y0,x1,y1){
192195
drawLine(mLine,null,x0,y0,x1,y1);
193196
}
194
- function drawMergeArrow(p,rail){
197
+ function drawCherrypickLine(x0,y0,x1,y1){
198
+ drawLine(cpLine,null,x0,y0,x1,y1);
199
+ }
200
+ /* Draw an arrow representing an in-bound merge from the "rail"-th rail
201
+ ** over to the node of "p". Make is a checkpoint merge is "isCP" is true */
202
+ function drawMergeArrow(p,rail,isCP){
195203
var x0 = rail*railPitch + node.w/2;
196204
if( rail in mergeLines ){
197205
x0 += mergeLines[rail];
198206
if( p.r<rail ) x0 += mLine.w;
199207
}else{
@@ -200,13 +208,19 @@
200208
x0 += (p.r<rail ? -1 : 1)*line.w/2;
201209
}
202210
var x1 = mArrow.w ? mArrow.w/2 : -node.w/2;
203211
x1 = p.x + (p.r<rail ? node.w + Math.ceil(x1) : -x1);
204212
var y = miLineY(p);
205
- drawMergeLine(x0,y,x1,null);
206213
var x = p.x + (p.r<rail ? node.w : -mArrow.w);
207
- var cls = "arrow merge " + (p.r<rail ? "l" : "r");
214
+ var cls;
215
+ if( isCP ){
216
+ drawCherrypickLine(x0,y,x1,null);
217
+ cls = "arrow cherrypick " + (p.r<rail ? "l" : "r");
218
+ }else{
219
+ drawMergeLine(x0,y,x1,null);
220
+ cls = "arrow merge " + (p.r<rail ? "l" : "r");
221
+ }
208222
drawBox(cls,null,x,y+(mLine.w-mArrow.h)/2);
209223
}
210224
function drawNode(p, btm){
211225
if( p.bg ){
212226
var e = document.getElementById("mc"+p.id);
@@ -225,11 +239,11 @@
225239
n.style.zIndex = 10;
226240
if( !tx.omitDescenders ){
227241
if( p.u==0 ) drawUpArrow(p,{x: p.x, y: -node.h},p.fg);
228242
if( p.hasOwnProperty('d') ) drawUpArrow({x: p.x, y: btm-node.h/2},p,p.fg);
229243
}
230
- if( p.mo>=0 ){
244
+ if( p.hasOwnProperty('mo') ){
231245
var x0 = p.x + node.w/2;
232246
var x1 = p.mo*railPitch + node.w/2;
233247
var u = tx.rowinfo[p.mu-tx.iTopRow];
234248
var y1 = miLineY(u);
235249
if( p.u<0 || p.mo!=p.r ){
@@ -278,11 +292,23 @@
278292
rail = -rail;
279293
mergeLines[rail] = -mLine.w/2;
280294
var x = rail*railPitch + (node.w-mLine.w)/2;
281295
drawMergeLine(x,miLineY(p),null,btm);
282296
}
283
- drawMergeArrow(p,rail);
297
+ drawMergeArrow(p,rail,0);
298
+ }
299
+ }
300
+ if( p.hasOwnProperty('cpi') ){
301
+ for( var i=0; i<p.cpi.length; i++ ){
302
+ var rail = p.cpi[i];
303
+ if( rail<0 ){
304
+ rail = -rail;
305
+ mergeLines[rail] = -mLine.w/2;
306
+ var x = rail*railPitch + (node.w-mLine.w)/2;
307
+ drawMergeLine(x,miLineY(p),null,btm);
308
+ }
309
+ drawMergeArrow(p,rail,1);
284310
}
285311
}
286312
}
287313
var mergeLines;
288314
function renderGraph(){
289315
--- src/graph.js
+++ src/graph.js
@@ -83,11 +83,12 @@
83 parent.appendChild(canvasDiv);
84
85 var elems = {};
86 var elemClasses = [
87 "rail", "mergeoffset", "node", "arrow u", "arrow u sm", "line",
88 "arrow merge r", "line merge", "arrow warp", "line warp"
 
89 ];
90 for( var i=0; i<elemClasses.length; i++ ){
91 var cls = elemClasses[i];
92 var elem = document.createElement("div");
93 elem.className = "tl-" + cls;
@@ -103,10 +104,11 @@
103 arrow = elems.arrow_u;
104 arrowSmall = elems.arrow_u_sm;
105 line = elems.line;
106 mArrow = elems.arrow_merge_r;
107 mLine = elems.line_merge;
 
108 wArrow = elems.arrow_warp;
109 wLine = elems.line_warp;
110
111 var minRailPitch = Math.ceil((node.w+line.w)/2 + mArrow.w + 1);
112 if( window.innerWidth<400 ){
@@ -186,14 +188,20 @@
186 drawLine(line,color,x,y0,null,y1);
187 x = to.x + (node.w-arw.w)/2;
188 var n = drawBox(arw.cls,null,x,y);
189 if(color) n.style.borderBottomColor = color;
190 }
 
191 function drawMergeLine(x0,y0,x1,y1){
192 drawLine(mLine,null,x0,y0,x1,y1);
193 }
194 function drawMergeArrow(p,rail){
 
 
 
 
 
195 var x0 = rail*railPitch + node.w/2;
196 if( rail in mergeLines ){
197 x0 += mergeLines[rail];
198 if( p.r<rail ) x0 += mLine.w;
199 }else{
@@ -200,13 +208,19 @@
200 x0 += (p.r<rail ? -1 : 1)*line.w/2;
201 }
202 var x1 = mArrow.w ? mArrow.w/2 : -node.w/2;
203 x1 = p.x + (p.r<rail ? node.w + Math.ceil(x1) : -x1);
204 var y = miLineY(p);
205 drawMergeLine(x0,y,x1,null);
206 var x = p.x + (p.r<rail ? node.w : -mArrow.w);
207 var cls = "arrow merge " + (p.r<rail ? "l" : "r");
 
 
 
 
 
 
 
208 drawBox(cls,null,x,y+(mLine.w-mArrow.h)/2);
209 }
210 function drawNode(p, btm){
211 if( p.bg ){
212 var e = document.getElementById("mc"+p.id);
@@ -225,11 +239,11 @@
225 n.style.zIndex = 10;
226 if( !tx.omitDescenders ){
227 if( p.u==0 ) drawUpArrow(p,{x: p.x, y: -node.h},p.fg);
228 if( p.hasOwnProperty('d') ) drawUpArrow({x: p.x, y: btm-node.h/2},p,p.fg);
229 }
230 if( p.mo>=0 ){
231 var x0 = p.x + node.w/2;
232 var x1 = p.mo*railPitch + node.w/2;
233 var u = tx.rowinfo[p.mu-tx.iTopRow];
234 var y1 = miLineY(u);
235 if( p.u<0 || p.mo!=p.r ){
@@ -278,11 +292,23 @@
278 rail = -rail;
279 mergeLines[rail] = -mLine.w/2;
280 var x = rail*railPitch + (node.w-mLine.w)/2;
281 drawMergeLine(x,miLineY(p),null,btm);
282 }
283 drawMergeArrow(p,rail);
 
 
 
 
 
 
 
 
 
 
 
 
284 }
285 }
286 }
287 var mergeLines;
288 function renderGraph(){
289
--- src/graph.js
+++ src/graph.js
@@ -83,11 +83,12 @@
83 parent.appendChild(canvasDiv);
84
85 var elems = {};
86 var elemClasses = [
87 "rail", "mergeoffset", "node", "arrow u", "arrow u sm", "line",
88 "arrow merge r", "line merge", "arrow warp", "line warp",
89 "line cherrypick"
90 ];
91 for( var i=0; i<elemClasses.length; i++ ){
92 var cls = elemClasses[i];
93 var elem = document.createElement("div");
94 elem.className = "tl-" + cls;
@@ -103,10 +104,11 @@
104 arrow = elems.arrow_u;
105 arrowSmall = elems.arrow_u_sm;
106 line = elems.line;
107 mArrow = elems.arrow_merge_r;
108 mLine = elems.line_merge;
109 cpLine = elems.line_cherrypick;
110 wArrow = elems.arrow_warp;
111 wLine = elems.line_warp;
112
113 var minRailPitch = Math.ceil((node.w+line.w)/2 + mArrow.w + 1);
114 if( window.innerWidth<400 ){
@@ -186,14 +188,20 @@
188 drawLine(line,color,x,y0,null,y1);
189 x = to.x + (node.w-arw.w)/2;
190 var n = drawBox(arw.cls,null,x,y);
191 if(color) n.style.borderBottomColor = color;
192 }
193 /* Draw thin horizontal or vertical lines representing merges */
194 function drawMergeLine(x0,y0,x1,y1){
195 drawLine(mLine,null,x0,y0,x1,y1);
196 }
197 function drawCherrypickLine(x0,y0,x1,y1){
198 drawLine(cpLine,null,x0,y0,x1,y1);
199 }
200 /* Draw an arrow representing an in-bound merge from the "rail"-th rail
201 ** over to the node of "p". Make is a checkpoint merge is "isCP" is true */
202 function drawMergeArrow(p,rail,isCP){
203 var x0 = rail*railPitch + node.w/2;
204 if( rail in mergeLines ){
205 x0 += mergeLines[rail];
206 if( p.r<rail ) x0 += mLine.w;
207 }else{
@@ -200,13 +208,19 @@
208 x0 += (p.r<rail ? -1 : 1)*line.w/2;
209 }
210 var x1 = mArrow.w ? mArrow.w/2 : -node.w/2;
211 x1 = p.x + (p.r<rail ? node.w + Math.ceil(x1) : -x1);
212 var y = miLineY(p);
 
213 var x = p.x + (p.r<rail ? node.w : -mArrow.w);
214 var cls;
215 if( isCP ){
216 drawCherrypickLine(x0,y,x1,null);
217 cls = "arrow cherrypick " + (p.r<rail ? "l" : "r");
218 }else{
219 drawMergeLine(x0,y,x1,null);
220 cls = "arrow merge " + (p.r<rail ? "l" : "r");
221 }
222 drawBox(cls,null,x,y+(mLine.w-mArrow.h)/2);
223 }
224 function drawNode(p, btm){
225 if( p.bg ){
226 var e = document.getElementById("mc"+p.id);
@@ -225,11 +239,11 @@
239 n.style.zIndex = 10;
240 if( !tx.omitDescenders ){
241 if( p.u==0 ) drawUpArrow(p,{x: p.x, y: -node.h},p.fg);
242 if( p.hasOwnProperty('d') ) drawUpArrow({x: p.x, y: btm-node.h/2},p,p.fg);
243 }
244 if( p.hasOwnProperty('mo') ){
245 var x0 = p.x + node.w/2;
246 var x1 = p.mo*railPitch + node.w/2;
247 var u = tx.rowinfo[p.mu-tx.iTopRow];
248 var y1 = miLineY(u);
249 if( p.u<0 || p.mo!=p.r ){
@@ -278,11 +292,23 @@
292 rail = -rail;
293 mergeLines[rail] = -mLine.w/2;
294 var x = rail*railPitch + (node.w-mLine.w)/2;
295 drawMergeLine(x,miLineY(p),null,btm);
296 }
297 drawMergeArrow(p,rail,0);
298 }
299 }
300 if( p.hasOwnProperty('cpi') ){
301 for( var i=0; i<p.cpi.length; i++ ){
302 var rail = p.cpi[i];
303 if( rail<0 ){
304 rail = -rail;
305 mergeLines[rail] = -mLine.w/2;
306 var x = rail*railPitch + (node.w-mLine.w)/2;
307 drawMergeLine(x,miLineY(p),null,btm);
308 }
309 drawMergeArrow(p,rail,1);
310 }
311 }
312 }
313 var mergeLines;
314 function renderGraph(){
315
+44 -8
--- src/timeline.c
+++ src/timeline.c
@@ -429,10 +429,11 @@
429429
}
430430
}
431431
}
432432
if( zType[0]=='c' && pGraph ){
433433
int nParent = 0;
434
+ int nCherrypick = 0;
434435
int aParent[GR_MAX_RAIL];
435436
static Stmt qparent;
436437
db_static_prepare(&qparent,
437438
"SELECT pid FROM plink"
438439
" WHERE cid=:rid AND pid NOT IN phantom"
@@ -450,22 +451,23 @@
450451
" WHERE childid=:rid AND parentid NOT IN phantom"
451452
);
452453
db_bind_int(&qcherrypick, ":rid", rid);
453454
while( db_step(&qcherrypick)==SQLITE_ROW && nParent<count(aParent) ){
454455
aParent[nParent++] = db_column_int(&qcherrypick, 0);
456
+ nCherrypick++;
455457
}
456458
db_reset(&qcherrypick);
457459
}
458
- gidx = graph_add_row(pGraph, rid, nParent, aParent, zBr, zBgClr,
459
- zUuid, isLeaf);
460
+ gidx = graph_add_row(pGraph, rid, nParent, nCherrypick, aParent,
461
+ zBr, zBgClr, zUuid, isLeaf);
460462
db_reset(&qbranch);
461463
@ <div id="m%d(gidx)" class="tl-nodemark"></div>
462464
}else if( zType[0]=='e' && pGraph && zBgClr && zBgClr[0] ){
463465
/* For technotes, make a graph node with nParent==(-1). This will
464466
** not actually draw anything on the graph, but it will set the
465467
** background color of the timeline entry */
466
- gidx = graph_add_row(pGraph, rid, -1, 0, zBr, zBgClr, zUuid, 0);
468
+ gidx = graph_add_row(pGraph, rid, -1, 0, 0, zBr, zBgClr, zUuid, 0);
467469
@ <div id="m%d(gidx)" class="tl-nodemark"></div>
468470
}
469471
@</td>
470472
if( !isSelectedOrCurrent ){
471473
@ <td class="timeline%s(zStyle)Cell" id='mc%d(gidx)'>
@@ -884,10 +886,12 @@
884886
** merge arrows should be drawn into this node. If the value is
885887
** negative, then the rail position is the absolute value of mi[]
886888
** and a thin merge-arrow descender is drawn to the bottom of
887889
** the screen. This array is omitted if there are no inbound
888890
** merges.
891
+ ** cpi: "cherrypick-in". Like "mi" except for cherrypick merges.
892
+ ** omitted if there are no cherrypick merges.
889893
** h: The artifact hash of the object being graphed
890894
*/
891895
for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
892896
int k = 0;
893897
cgi_printf("{\"id\":%d,", pRow->idx);
@@ -920,17 +924,32 @@
920924
if( colorGraph && pRow->zBgClr[0]=='#' ){
921925
cgi_printf("\"fg\":\"%s\",", bg_to_fg(pRow->zBgClr));
922926
}
923927
/* mi */
924928
for(i=k=0; i<GR_MAX_RAIL; i++){
925
- if( pRow->mergeIn[i] ){
929
+ if( pRow->mergeIn[i]==1 ){
926930
int mi = i;
927931
if( (pRow->mergeDown >> i) & 1 ) mi = -mi;
928932
if( k==0 ){
929933
cgi_printf("\"mi\":");
930934
cSep = '[';
931935
}
936
+ k++;
937
+ cgi_printf("%c%d", cSep, mi);
938
+ cSep = ',';
939
+ }
940
+ }
941
+ if( k ) cgi_printf("],");
942
+ /* cpi */
943
+ for(i=k=0; i<GR_MAX_RAIL; i++){
944
+ if( pRow->mergeIn[i]==2 ){
945
+ int mi = i;
946
+ if( (pRow->cherrypickDown >> i) & 1 ) mi = -mi;
947
+ if( k==0 ){
948
+ cgi_printf("\"cpi\":");
949
+ cSep = '[';
950
+ }
932951
k++;
933952
cgi_printf("%c%d", cSep, mi);
934953
cSep = ',';
935954
}
936955
}
@@ -1401,10 +1420,11 @@
14011420
** u=USER Only show items associated with USER
14021421
** y=TYPE 'ci', 'w', 't', 'e', 'f', or 'all'.
14031422
** ss=VIEWSTYLE c: "Compact" v: "Verbose" m: "Modern" j: "Columnar"
14041423
** advm Use the "Advanced" or "Busy" menu design.
14051424
** ng No Graph.
1425
+** ncp Omit cherrypick merges
14061426
** nd Do not highlight the focus check-in
14071427
** v Show details of files changed
14081428
** f=CHECKIN Show family (immediate parents and children) of CHECKIN
14091429
** from=CHECKIN Path from...
14101430
** to=CHECKIN ... to this
@@ -1585,16 +1605,19 @@
15851605
){
15861606
nEntry = -1;
15871607
zCirca = 0;
15881608
}
15891609
if( zType[0]=='a' ){
1590
- tmFlags |= TIMELINE_BRIEF | TIMELINE_GRAPH;
1610
+ tmFlags |= TIMELINE_BRIEF | TIMELINE_GRAPH | TIMELINE_CHPICK;
15911611
}else{
1592
- tmFlags |= TIMELINE_GRAPH;
1612
+ tmFlags |= TIMELINE_GRAPH | TIMELINE_CHPICK;
1613
+ }
1614
+ if( PB("ncp") ){
1615
+ tmFlags &= ~TIMELINE_CHPICK;
15931616
}
15941617
if( PB("ng") || zSearch!=0 ){
1595
- tmFlags &= ~TIMELINE_GRAPH;
1618
+ tmFlags &= ~(TIMELINE_GRAPH|TIMELINE_CHPICK);
15961619
}
15971620
if( PB("brbg") ){
15981621
tmFlags |= TIMELINE_BRCOLOR;
15991622
}
16001623
if( PB("unhide") ){
@@ -1856,11 +1879,11 @@
18561879
" WHERE %s AND tagtype>0 AND rid=blob.rid)\n", zTagSql/*safe-for-%s*/);
18571880
18581881
if( related ){
18591882
/* The next two blob_appendf() calls add SQL that causes check-ins that
18601883
** are not part of the branch which are parents or children of the
1861
- ** branch to be included in the report. This related check-ins are
1884
+ ** branch to be included in the report. These related check-ins are
18621885
** useful in helping to visualize what has happened on a quiescent
18631886
** branch that is infrequently merged with a much more activate branch.
18641887
*/
18651888
blob_append_sql(&cond,
18661889
" OR EXISTS(SELECT 1 FROM plink CROSS JOIN tagxref ON rid=cid"
@@ -1885,10 +1908,23 @@
18851908
" AND NOT EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=pid"
18861909
" WHERE tagid=%d AND tagtype>0 AND cid=blob.rid)\n",
18871910
TAG_HIDDEN
18881911
);
18891912
}
1913
+ }
1914
+ if( !PB("ncp") && db_table_exists("repository","cherrypick") ){
1915
+ tmFlags |= TIMELINE_CHPICK;
1916
+ blob_append_sql(&cond,
1917
+ " OR EXISTS(SELECT 1 FROM cherrypick CROSS JOIN tagxref"
1918
+ " ON rid=childid NATURAL JOIN tag WHERE %s AND tagtype>0"
1919
+ " AND parentid=blob.rid)\n", zTagSql/*safe-for-%s*/
1920
+ );
1921
+ blob_append_sql(&cond,
1922
+ " OR EXISTS(SELECT 1 FROM cherrypick CROSS JOIN tagxref"
1923
+ " ON rid=parentid NATURAL JOIN tag WHERE %s AND tagtype>0"
1924
+ " AND childid=blob.rid)\n", zTagSql/*safe-for-%s*/
1925
+ );
18901926
}
18911927
}
18921928
blob_append_sql(&cond, ")");
18931929
}
18941930
if( (zType[0]=='w' && !g.perm.RdWiki)
18951931
--- src/timeline.c
+++ src/timeline.c
@@ -429,10 +429,11 @@
429 }
430 }
431 }
432 if( zType[0]=='c' && pGraph ){
433 int nParent = 0;
 
434 int aParent[GR_MAX_RAIL];
435 static Stmt qparent;
436 db_static_prepare(&qparent,
437 "SELECT pid FROM plink"
438 " WHERE cid=:rid AND pid NOT IN phantom"
@@ -450,22 +451,23 @@
450 " WHERE childid=:rid AND parentid NOT IN phantom"
451 );
452 db_bind_int(&qcherrypick, ":rid", rid);
453 while( db_step(&qcherrypick)==SQLITE_ROW && nParent<count(aParent) ){
454 aParent[nParent++] = db_column_int(&qcherrypick, 0);
 
455 }
456 db_reset(&qcherrypick);
457 }
458 gidx = graph_add_row(pGraph, rid, nParent, aParent, zBr, zBgClr,
459 zUuid, isLeaf);
460 db_reset(&qbranch);
461 @ <div id="m%d(gidx)" class="tl-nodemark"></div>
462 }else if( zType[0]=='e' && pGraph && zBgClr && zBgClr[0] ){
463 /* For technotes, make a graph node with nParent==(-1). This will
464 ** not actually draw anything on the graph, but it will set the
465 ** background color of the timeline entry */
466 gidx = graph_add_row(pGraph, rid, -1, 0, zBr, zBgClr, zUuid, 0);
467 @ <div id="m%d(gidx)" class="tl-nodemark"></div>
468 }
469 @</td>
470 if( !isSelectedOrCurrent ){
471 @ <td class="timeline%s(zStyle)Cell" id='mc%d(gidx)'>
@@ -884,10 +886,12 @@
884 ** merge arrows should be drawn into this node. If the value is
885 ** negative, then the rail position is the absolute value of mi[]
886 ** and a thin merge-arrow descender is drawn to the bottom of
887 ** the screen. This array is omitted if there are no inbound
888 ** merges.
 
 
889 ** h: The artifact hash of the object being graphed
890 */
891 for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
892 int k = 0;
893 cgi_printf("{\"id\":%d,", pRow->idx);
@@ -920,17 +924,32 @@
920 if( colorGraph && pRow->zBgClr[0]=='#' ){
921 cgi_printf("\"fg\":\"%s\",", bg_to_fg(pRow->zBgClr));
922 }
923 /* mi */
924 for(i=k=0; i<GR_MAX_RAIL; i++){
925 if( pRow->mergeIn[i] ){
926 int mi = i;
927 if( (pRow->mergeDown >> i) & 1 ) mi = -mi;
928 if( k==0 ){
929 cgi_printf("\"mi\":");
930 cSep = '[';
931 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
932 k++;
933 cgi_printf("%c%d", cSep, mi);
934 cSep = ',';
935 }
936 }
@@ -1401,10 +1420,11 @@
1401 ** u=USER Only show items associated with USER
1402 ** y=TYPE 'ci', 'w', 't', 'e', 'f', or 'all'.
1403 ** ss=VIEWSTYLE c: "Compact" v: "Verbose" m: "Modern" j: "Columnar"
1404 ** advm Use the "Advanced" or "Busy" menu design.
1405 ** ng No Graph.
 
1406 ** nd Do not highlight the focus check-in
1407 ** v Show details of files changed
1408 ** f=CHECKIN Show family (immediate parents and children) of CHECKIN
1409 ** from=CHECKIN Path from...
1410 ** to=CHECKIN ... to this
@@ -1585,16 +1605,19 @@
1585 ){
1586 nEntry = -1;
1587 zCirca = 0;
1588 }
1589 if( zType[0]=='a' ){
1590 tmFlags |= TIMELINE_BRIEF | TIMELINE_GRAPH;
1591 }else{
1592 tmFlags |= TIMELINE_GRAPH;
 
 
 
1593 }
1594 if( PB("ng") || zSearch!=0 ){
1595 tmFlags &= ~TIMELINE_GRAPH;
1596 }
1597 if( PB("brbg") ){
1598 tmFlags |= TIMELINE_BRCOLOR;
1599 }
1600 if( PB("unhide") ){
@@ -1856,11 +1879,11 @@
1856 " WHERE %s AND tagtype>0 AND rid=blob.rid)\n", zTagSql/*safe-for-%s*/);
1857
1858 if( related ){
1859 /* The next two blob_appendf() calls add SQL that causes check-ins that
1860 ** are not part of the branch which are parents or children of the
1861 ** branch to be included in the report. This related check-ins are
1862 ** useful in helping to visualize what has happened on a quiescent
1863 ** branch that is infrequently merged with a much more activate branch.
1864 */
1865 blob_append_sql(&cond,
1866 " OR EXISTS(SELECT 1 FROM plink CROSS JOIN tagxref ON rid=cid"
@@ -1885,10 +1908,23 @@
1885 " AND NOT EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=pid"
1886 " WHERE tagid=%d AND tagtype>0 AND cid=blob.rid)\n",
1887 TAG_HIDDEN
1888 );
1889 }
 
 
 
 
 
 
 
 
 
 
 
 
 
1890 }
1891 }
1892 blob_append_sql(&cond, ")");
1893 }
1894 if( (zType[0]=='w' && !g.perm.RdWiki)
1895
--- src/timeline.c
+++ src/timeline.c
@@ -429,10 +429,11 @@
429 }
430 }
431 }
432 if( zType[0]=='c' && pGraph ){
433 int nParent = 0;
434 int nCherrypick = 0;
435 int aParent[GR_MAX_RAIL];
436 static Stmt qparent;
437 db_static_prepare(&qparent,
438 "SELECT pid FROM plink"
439 " WHERE cid=:rid AND pid NOT IN phantom"
@@ -450,22 +451,23 @@
451 " WHERE childid=:rid AND parentid NOT IN phantom"
452 );
453 db_bind_int(&qcherrypick, ":rid", rid);
454 while( db_step(&qcherrypick)==SQLITE_ROW && nParent<count(aParent) ){
455 aParent[nParent++] = db_column_int(&qcherrypick, 0);
456 nCherrypick++;
457 }
458 db_reset(&qcherrypick);
459 }
460 gidx = graph_add_row(pGraph, rid, nParent, nCherrypick, aParent,
461 zBr, zBgClr, zUuid, isLeaf);
462 db_reset(&qbranch);
463 @ <div id="m%d(gidx)" class="tl-nodemark"></div>
464 }else if( zType[0]=='e' && pGraph && zBgClr && zBgClr[0] ){
465 /* For technotes, make a graph node with nParent==(-1). This will
466 ** not actually draw anything on the graph, but it will set the
467 ** background color of the timeline entry */
468 gidx = graph_add_row(pGraph, rid, -1, 0, 0, zBr, zBgClr, zUuid, 0);
469 @ <div id="m%d(gidx)" class="tl-nodemark"></div>
470 }
471 @</td>
472 if( !isSelectedOrCurrent ){
473 @ <td class="timeline%s(zStyle)Cell" id='mc%d(gidx)'>
@@ -884,10 +886,12 @@
886 ** merge arrows should be drawn into this node. If the value is
887 ** negative, then the rail position is the absolute value of mi[]
888 ** and a thin merge-arrow descender is drawn to the bottom of
889 ** the screen. This array is omitted if there are no inbound
890 ** merges.
891 ** cpi: "cherrypick-in". Like "mi" except for cherrypick merges.
892 ** omitted if there are no cherrypick merges.
893 ** h: The artifact hash of the object being graphed
894 */
895 for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
896 int k = 0;
897 cgi_printf("{\"id\":%d,", pRow->idx);
@@ -920,17 +924,32 @@
924 if( colorGraph && pRow->zBgClr[0]=='#' ){
925 cgi_printf("\"fg\":\"%s\",", bg_to_fg(pRow->zBgClr));
926 }
927 /* mi */
928 for(i=k=0; i<GR_MAX_RAIL; i++){
929 if( pRow->mergeIn[i]==1 ){
930 int mi = i;
931 if( (pRow->mergeDown >> i) & 1 ) mi = -mi;
932 if( k==0 ){
933 cgi_printf("\"mi\":");
934 cSep = '[';
935 }
936 k++;
937 cgi_printf("%c%d", cSep, mi);
938 cSep = ',';
939 }
940 }
941 if( k ) cgi_printf("],");
942 /* cpi */
943 for(i=k=0; i<GR_MAX_RAIL; i++){
944 if( pRow->mergeIn[i]==2 ){
945 int mi = i;
946 if( (pRow->cherrypickDown >> i) & 1 ) mi = -mi;
947 if( k==0 ){
948 cgi_printf("\"cpi\":");
949 cSep = '[';
950 }
951 k++;
952 cgi_printf("%c%d", cSep, mi);
953 cSep = ',';
954 }
955 }
@@ -1401,10 +1420,11 @@
1420 ** u=USER Only show items associated with USER
1421 ** y=TYPE 'ci', 'w', 't', 'e', 'f', or 'all'.
1422 ** ss=VIEWSTYLE c: "Compact" v: "Verbose" m: "Modern" j: "Columnar"
1423 ** advm Use the "Advanced" or "Busy" menu design.
1424 ** ng No Graph.
1425 ** ncp Omit cherrypick merges
1426 ** nd Do not highlight the focus check-in
1427 ** v Show details of files changed
1428 ** f=CHECKIN Show family (immediate parents and children) of CHECKIN
1429 ** from=CHECKIN Path from...
1430 ** to=CHECKIN ... to this
@@ -1585,16 +1605,19 @@
1605 ){
1606 nEntry = -1;
1607 zCirca = 0;
1608 }
1609 if( zType[0]=='a' ){
1610 tmFlags |= TIMELINE_BRIEF | TIMELINE_GRAPH | TIMELINE_CHPICK;
1611 }else{
1612 tmFlags |= TIMELINE_GRAPH | TIMELINE_CHPICK;
1613 }
1614 if( PB("ncp") ){
1615 tmFlags &= ~TIMELINE_CHPICK;
1616 }
1617 if( PB("ng") || zSearch!=0 ){
1618 tmFlags &= ~(TIMELINE_GRAPH|TIMELINE_CHPICK);
1619 }
1620 if( PB("brbg") ){
1621 tmFlags |= TIMELINE_BRCOLOR;
1622 }
1623 if( PB("unhide") ){
@@ -1856,11 +1879,11 @@
1879 " WHERE %s AND tagtype>0 AND rid=blob.rid)\n", zTagSql/*safe-for-%s*/);
1880
1881 if( related ){
1882 /* The next two blob_appendf() calls add SQL that causes check-ins that
1883 ** are not part of the branch which are parents or children of the
1884 ** branch to be included in the report. These related check-ins are
1885 ** useful in helping to visualize what has happened on a quiescent
1886 ** branch that is infrequently merged with a much more activate branch.
1887 */
1888 blob_append_sql(&cond,
1889 " OR EXISTS(SELECT 1 FROM plink CROSS JOIN tagxref ON rid=cid"
@@ -1885,10 +1908,23 @@
1908 " AND NOT EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=pid"
1909 " WHERE tagid=%d AND tagtype>0 AND cid=blob.rid)\n",
1910 TAG_HIDDEN
1911 );
1912 }
1913 }
1914 if( !PB("ncp") && db_table_exists("repository","cherrypick") ){
1915 tmFlags |= TIMELINE_CHPICK;
1916 blob_append_sql(&cond,
1917 " OR EXISTS(SELECT 1 FROM cherrypick CROSS JOIN tagxref"
1918 " ON rid=childid NATURAL JOIN tag WHERE %s AND tagtype>0"
1919 " AND parentid=blob.rid)\n", zTagSql/*safe-for-%s*/
1920 );
1921 blob_append_sql(&cond,
1922 " OR EXISTS(SELECT 1 FROM cherrypick CROSS JOIN tagxref"
1923 " ON rid=parentid NATURAL JOIN tag WHERE %s AND tagtype>0"
1924 " AND childid=blob.rid)\n", zTagSql/*safe-for-%s*/
1925 );
1926 }
1927 }
1928 blob_append_sql(&cond, ")");
1929 }
1930 if( (zType[0]=='w' && !g.perm.RdWiki)
1931

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button