Fossil

Changes On Branch vdiff-improvements
Login

Changes On Branch vdiff-improvements

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch vdiff-improvements Excluding Merge-Ins

This is equivalent to a diff from c6a033ce to 8e175a67

2019-09-03
20:17
Improvements to /vdiff. Provide the branch= query parameter. In the name decoder, all "merge-in:BRANCH" to mean the most recent merge-in to BRANCH from its parent. Provide "Diff" links for branches that show all changes since the most recent merge-in. (check-in: e632a092 user: drh tags: trunk)
20:08
Fix a possible UNIQUE constraint violation in the new /vdiff logic. (Closed-Leaf check-in: 8e175a67 user: drh tags: vdiff-improvements)
2019-08-28
16:08
Added 'or' help for checkin/ci alias, per forum request. (check-in: f616380d user: stephan tags: trunk)
2019-08-27
12:36
Add hyperlinks to the branch diff page from branch timeline and from /info. (check-in: 833b220a user: drh tags: vdiff-improvements)
04:15
Make it possible to disable JSON auto-detection in the CGI subsystem. (check-in: a7754353 user: mistachkin tags: noJsonCgiFlag)
00:29
Merge in documentation enhancements from trunk. (check-in: c1b62c32 user: drh tags: vdiff-improvements)
00:07
Changed all of the [anycap jor] TH1 calls in the stock skins wrapping the generation of that skin's /timeline and /timeline.rss links to [anycap ijr2] to match the user caps the timeline HTTP hit handler actually checks for in the C code. This is a branch in part because it needs review, but also it's the start of a broader effort to check the other cap checks in the skins to make sure they a) match what the C code checks for; and b) match each other. (check-in: 9cee8cf5 user: wyoung tags: skin-cap-matching)
2019-08-25
13:24
Added HTTP proxying info to Debian nginx server setup guide. (check-in: c6a033ce user: wyoung tags: trunk)
12:39
Replaced the content of "Running Fossil in SCGI Mode" within www/server/debian/nginx.md with references to our other Fossil server docs. This also reduces the prior focus of this section on fslsrv to a single sentence, since we now prefer the systemd option, now that we have it. (check-in: a4bb92f7 user: wyoung tags: trunk)

Changes to skins/ardoise/css.txt.

945
946
947
948
949
950
951




























952
953
954
955
956
957
958
.timelineSelected > .timelineVerboseCell {
  padding: .75em;
  border-radius: 5px;
  border: solid #ff8000;
  vertical-align: top;
  text-align: left;
  background: #442800




























}
.timelineCurrent > .timelineColumnarCell,
.timelineCurrent > .timelineCompactCell,
.timelineCurrent > .timelineDetailCell,
.timelineCurrent > .timelineModernCell,
.timelineCurrent > .timelineVerboseCell {
  vertical-align: top;







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
.timelineSelected > .timelineVerboseCell {
  padding: .75em;
  border-radius: 5px;
  border: solid #ff8000;
  vertical-align: top;
  text-align: left;
  background: #442800
}
span.timelineSelected {
  border-radius: 5px;
  border: solid #ff8000;
  vertical-align: top;
  text-align: left;
  background: #442800
}
.timelineSelected {}
.timelineSecondary {}
.timelineSecondary > .timelineColumnarCell,
.timelineSecondary > .timelineCompactCell,
.timelineSecondary > .timelineDetailCell,
.timelineSecondary > .timelineModernCell,
.timelineSecondary > .timelineVerboseCell {
  padding: .75em;
  border-radius: 5px;
  border: solid #0080ff;
  vertical-align: top;
  text-align: left;
  background: #002844
}
span.timelineSecondary {
  border-radius: 5px;
  border: solid #0080ff;
  vertical-align: top;
  text-align: left;
  background: #002844
}
.timelineCurrent > .timelineColumnarCell,
.timelineCurrent > .timelineCompactCell,
.timelineCurrent > .timelineDetailCell,
.timelineCurrent > .timelineModernCell,
.timelineCurrent > .timelineVerboseCell {
  vertical-align: top;

Changes to skins/blitz/css.txt.

1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070



1071
1072
1073
1074
1075
1076
1077
tr.timelineCurrent {
  border-left: 2px solid orange;
  background-color: #ffc;
  border-bottom: 1px solid #ddd;
  border-right: 1px solid #ddd;
}

tr.timelineSelected {
  border-left: 2px solid orange;
  background-color: #ffffe8;
  border-bottom: 1px solid #ddd;
  border-right: 1px solid #ddd;
}




tr.timelineCurrent td.timelineTableCell {
}

tr.timelineBottom td {
  border-bottom: 0;
}







|





>
>
>







1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
tr.timelineCurrent {
  border-left: 2px solid orange;
  background-color: #ffc;
  border-bottom: 1px solid #ddd;
  border-right: 1px solid #ddd;
}

.timelineSelected {
  border-left: 2px solid orange;
  background-color: #ffffe8;
  border-bottom: 1px solid #ddd;
  border-right: 1px solid #ddd;
}
.timelineSecondary {
  background-color: #e8ffff;
}

tr.timelineCurrent td.timelineTableCell {
}

tr.timelineBottom td {
  border-bottom: 0;
}

Changes to skins/eagle/css.txt.

166
167
168
169
170
171
172
173
174
175



176
177
178
179
180
181
182
  border: 0;
  cellpadding: 0;
  font-family: "courier new";
  border-spacing: 0px 2px;
  // border-collapse: collapse;
}

tr.timelineSelected {
  background-color: #7EA2D9;
}




/* commit node */
.tl-node {
  width: 10px;
  height: 10px;
  border: 1px solid #fff;
  background: #485D7B;







|


>
>
>







166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
  border: 0;
  cellpadding: 0;
  font-family: "courier new";
  border-spacing: 0px 2px;
  // border-collapse: collapse;
}

.timelineSelected {
  background-color: #7EA2D9;
}
.timelineSecondary {
  background-color: #7EA27E;
}

/* commit node */
.tl-node {
  width: 10px;
  height: 10px;
  border: 1px solid #fff;
  background: #485D7B;

Changes to skins/xekri/css.txt.

801
802
803
804
805
806
807


808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827







828
829
830
831
832
833
834
}

.timelineTable .timelineSelected {
  background: #222;
  border:     0;
  box-shadow: none;
}



.timelineTable .timelineSelected .timelineTime {
  background:    #333;
  border-radius: 1rem 0 0 1rem;
  box-shadow:    2px 2px 1px #000;
}

.timelineTable .timelineSelected .timelineColumnarCell {
  background: #333;
  box-shadow: 2px 2px 1px #000;
}

.timelineTable .timelineSelected .timelineModernCell  ,
.timelineTable .timelineSelected .timelineCompactCell ,
.timelineTable .timelineSelected .timelineVerboseCell ,
.timelineTable .timelineSelected .timelineDetailCell  {
  background:    #333;
  border-radius: 0 1rem 1rem 0;
  box-shadow:    2px 2px 1px #000;
}








.timelineTable .timelineModernCell  .timelineModernComment  ,
.timelineTable .timelineModernCell  .timelineModernDetail   ,
.timelineTable .timelineCompactCell .timelineCompactComment ,
.timelineTable .timelineCompactCell .timelineCompactDetail  ,
.timelineTable .timelineVerboseCell .timelineVerboseComment ,
.timelineTable .timelineVerboseCell .timelineVerboseDetail  {







>
>




















>
>
>
>
>
>
>







801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
}

.timelineTable .timelineSelected {
  background: #222;
  border:     0;
  box-shadow: none;
}
.timelineSelected {}
.timelineSecondary {}

.timelineTable .timelineSelected .timelineTime {
  background:    #333;
  border-radius: 1rem 0 0 1rem;
  box-shadow:    2px 2px 1px #000;
}

.timelineTable .timelineSelected .timelineColumnarCell {
  background: #333;
  box-shadow: 2px 2px 1px #000;
}

.timelineTable .timelineSelected .timelineModernCell  ,
.timelineTable .timelineSelected .timelineCompactCell ,
.timelineTable .timelineSelected .timelineVerboseCell ,
.timelineTable .timelineSelected .timelineDetailCell  {
  background:    #333;
  border-radius: 0 1rem 1rem 0;
  box-shadow:    2px 2px 1px #000;
}

span.timelineSelected {
  padding: 0 1em 0 1em;
  border-radius: 1rem;
  background: #333;
  box-shadow:    2px 2px 1px #000;
}

.timelineTable .timelineModernCell  .timelineModernComment  ,
.timelineTable .timelineModernCell  .timelineModernDetail   ,
.timelineTable .timelineCompactCell .timelineCompactComment ,
.timelineTable .timelineCompactCell .timelineCompactDetail  ,
.timelineTable .timelineVerboseCell .timelineVerboseComment ,
.timelineTable .timelineVerboseCell .timelineVerboseDetail  {

Changes to src/branch.c.

16
17
18
19
20
21
22



























23
24
25
26
27
28
29
*******************************************************************************
**
** This file contains code used to create new branches within a repository.
*/
#include "config.h"
#include "branch.h"
#include <assert.h>




























/*
**  fossil branch new    NAME  BASIS ?OPTIONS?
**  argv0  argv1  argv2  argv3 argv4
*/
void branch_new(void){
  int rootid;            /* RID of the root check-in - what we branch off of */







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
*******************************************************************************
**
** This file contains code used to create new branches within a repository.
*/
#include "config.h"
#include "branch.h"
#include <assert.h>

/*
** If RID refers to a check-in, return the name of the branch for that
** check-in.
**
** Space to hold the returned value is obtained from fossil_malloc()
** and should be freed by the caller.
*/
char *branch_of_rid(int rid){
  char *zBr = 0;
  static Stmt q;
  db_static_prepare(&q,
      "SELECT value FROM tagxref"
      " WHERE rid=$rid AND tagid=%d"
      " AND tagtype>0", TAG_BRANCH);
  db_bind_int(&q, "$rid", rid);
  if( db_step(&q)==SQLITE_ROW ){
    zBr = fossil_strdup(db_column_text(&q,0));
  }
  db_reset(&q);
  if( zBr==0 ){
    static char *zMain = 0;
    if( zMain==0 ) zMain = db_get("main-branch","trunk");
    zBr = fossil_strdup(zMain);
  }
  return zBr;
}

/*
**  fossil branch new    NAME  BASIS ?OPTIONS?
**  argv0  argv1  argv2  argv3 argv4
*/
void branch_new(void){
  int rootid;            /* RID of the root check-in - what we branch off of */
659
660
661
662
663
664
665
666
667
668
669
  blob_reset(&sql);
  /* Always specify TIMELINE_DISJOINT, or graph_finish() may fail because of too
  ** many descenders to (off-screen) parents. */
  tmFlags = TIMELINE_DISJOINT | TIMELINE_NOSCROLL;
  if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH;
  if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR;
  if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR;
  www_print_timeline(&q, tmFlags, 0, 0, 0, 0, brtimeline_extra);
  db_finalize(&q);
  style_footer();
}







|



686
687
688
689
690
691
692
693
694
695
696
  blob_reset(&sql);
  /* Always specify TIMELINE_DISJOINT, or graph_finish() may fail because of too
  ** many descenders to (off-screen) parents. */
  tmFlags = TIMELINE_DISJOINT | TIMELINE_NOSCROLL;
  if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH;
  if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR;
  if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR;
  www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, brtimeline_extra);
  db_finalize(&q);
  style_footer();
}

Changes to src/default_css.txt.

36
37
38
39
40
41
42
43
44
45
46
47
48



49
50
51
52
53
54
55
  font-size: small;
}
tr.timelineCurrent {
  padding: .1em .2em;
  border: 1px dashed #446979;
  box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.5);
}
tr.timelineSelected {
  padding: .1em .2em;
  border: 2px solid lightgray;
  background-color: #ffc;
  box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.5);
}



tr.timelineSelected td {
  border-radius: 0;
  border-width: 0;
}
tr.timelineCurrent td {
  border-radius: 0;
  border-width: 0;







|





>
>
>







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
  font-size: small;
}
tr.timelineCurrent {
  padding: .1em .2em;
  border: 1px dashed #446979;
  box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.5);
}
.timelineSelected {
  padding: .1em .2em;
  border: 2px solid lightgray;
  background-color: #ffc;
  box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.5);
}
.timelineSecondary {
  background-color: #cff;
}
tr.timelineSelected td {
  border-radius: 0;
  border-width: 0;
}
tr.timelineCurrent td {
  border-radius: 0;
  border-width: 0;

Changes to src/descendants.c.

181
182
183
184
185
186
187

























188
189
190
191
192
193
194
    "     ORDER BY mtime DESC LIMIT %d"
    "  )"
    "INSERT INTO ok"
    "  SELECT rid FROM ancestor;",
    rid, rid, directOnly ? "AND plink.isPrim" : "", N
  );
}


























/*
** Compute all direct ancestors (merge ancestors do not count)
** for the check-in rid and put them in a table named "ancestor".
** Label each generation with consecutive integers going backwards
** in time such that rid has the smallest generation number and the oldest
** direct ancestor as the largest generation number.







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
    "     ORDER BY mtime DESC LIMIT %d"
    "  )"
    "INSERT INTO ok"
    "  SELECT rid FROM ancestor;",
    rid, rid, directOnly ? "AND plink.isPrim" : "", N
  );
}

/*
** Compute the youngest ancestor of record ID rid that is a member of
** branch zBranch.
*/
int compute_youngest_ancestor_in_branch(int rid, const char *zBranch){
  return db_int(0,
    "WITH RECURSIVE "
    "  ancestor(rid, mtime) AS ("
    "    SELECT %d, mtime FROM event WHERE objid=%d "
    "    UNION "
    "    SELECT plink.pid, event.mtime"
    "      FROM ancestor, plink, event"
    "     WHERE plink.cid=ancestor.rid"
    "       AND event.objid=plink.pid"
    "     ORDER BY mtime DESC"
    "  )"
    "  SELECT ancestor.rid FROM ancestor"
    "   WHERE EXISTS(SELECT 1 FROM tagxref"
                    " WHERE tagid=%d AND tagxref.rid=ancestor.rid"
                    "   AND value=%Q AND tagtype>0)"
    "  LIMIT 1",
    rid, rid, TAG_BRANCH, zBranch
  );
}

/*
** Compute all direct ancestors (merge ancestors do not count)
** for the check-in rid and put them in a table named "ancestor".
** Label each generation with consecutive integers going backwards
** in time such that rid has the smallest generation number and the oldest
** direct ancestor as the largest generation number.
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
  blob_reset(&sql);
  /* Always specify TIMELINE_DISJOINT, or graph_finish() may fail because of too
  ** many descenders to (off-screen) parents. */
  tmFlags = TIMELINE_LEAFONLY | TIMELINE_DISJOINT | TIMELINE_NOSCROLL;
  if( fNg==0 ) tmFlags |= TIMELINE_GRAPH;
  if( fBrBg ) tmFlags |= TIMELINE_BRCOLOR;
  if( fUBg ) tmFlags |= TIMELINE_UCOLOR;
  www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0);
  db_finalize(&q);
  @ <br />
  style_footer();
}

#if INTERFACE
/* Flag parameters to compute_uses_file() */







|







563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
  blob_reset(&sql);
  /* Always specify TIMELINE_DISJOINT, or graph_finish() may fail because of too
  ** many descenders to (off-screen) parents. */
  tmFlags = TIMELINE_LEAFONLY | TIMELINE_DISJOINT | TIMELINE_NOSCROLL;
  if( fNg==0 ) tmFlags |= TIMELINE_GRAPH;
  if( fBrBg ) tmFlags |= TIMELINE_BRCOLOR;
  if( fUBg ) tmFlags |= TIMELINE_UCOLOR;
  www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0);
  db_finalize(&q);
  @ <br />
  style_footer();
}

#if INTERFACE
/* Flag parameters to compute_uses_file() */

Changes to src/finfo.c.

778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
       /* 5 */ "  (SELECT uuid FROM blob WHERE rid=mlink.pmid),"
       /* 6 */ "  mperm,"
       /* 7 */ "  isaux"
       "  FROM mlink WHERE mid=%d ORDER BY 1",
       mid
    );
    @ <h1>MLINK table for check-in %h(zCI)</h1>
    render_checkin_context(mid, 1);
    style_table_sorter();
    @ <hr />
    @ <div class='brlist'>
    @ <table class='sortable' data-column-types='ttxtttt' data-init-sort='1'>
    @ <thead><tr>
    @ <th>File</th>
    @ <th>Parent<br>Check-in</th>







|







778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
       /* 5 */ "  (SELECT uuid FROM blob WHERE rid=mlink.pmid),"
       /* 6 */ "  mperm,"
       /* 7 */ "  isaux"
       "  FROM mlink WHERE mid=%d ORDER BY 1",
       mid
    );
    @ <h1>MLINK table for check-in %h(zCI)</h1>
    render_checkin_context(mid, 0, 1);
    style_table_sorter();
    @ <hr />
    @ <div class='brlist'>
    @ <table class='sortable' data-column-types='ttxtttt' data-init-sort='1'>
    @ <thead><tr>
    @ <th>File</th>
    @ <th>Parent<br>Check-in</th>

Changes to src/info.c.

250
251
252
253
254
255
256
257
258
259





260
261

262
263
264



265
266
267
268

269

270
271
272
273
274
275
276
277
278
279
280

281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
  }
}

/*
** Show the context graph (immediate parents and children) for
** check-in rid.
*/
void render_checkin_context(int rid, int parentsOnly){
  Blob sql;
  Stmt q;





  blob_zero(&sql);
  blob_append(&sql, timeline_query_for_www(), -1);

  db_multi_exec(
     "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);"
     "DELETE FROM ok;"



     "INSERT INTO ok VALUES(%d);"
     "INSERT OR IGNORE INTO ok SELECT pid FROM plink WHERE cid=%d;",
     rid, rid
  );

  if( !parentsOnly ){

    db_multi_exec(
      "INSERT OR IGNORE INTO ok SELECT cid FROM plink WHERE pid=%d;", rid
    );
    if( db_table_exists("repository","cherrypick") ){
      db_multi_exec(
        "INSERT OR IGNORE INTO ok "
        "  SELECT parentid FROM cherrypick WHERE childid=%d;"
        "INSERT OR IGNORE INTO ok "
        "  SELECT childid FROM cherrypick WHERE parentid=%d;",
        rid, rid
      );

    }
  }
  blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
  db_prepare(&q, "%s", blob_sql_text(&sql));
  www_print_timeline(&q,
          TIMELINE_GRAPH
         |TIMELINE_FILLGAPS
         |TIMELINE_NOSCROLL
         |TIMELINE_XMERGE
         |TIMELINE_CHPICK,
       0, 0, 0, rid, 0);
  db_finalize(&q);
}

/*
** Show a graph all wiki, tickets, and check-ins that refer to object zUuid.
**
** If zLabel is not NULL and the graph is not empty, then output zLabel as







|


>
>
>
>
>


>

|
|
>
>
>
|
|
|
|
>

>
|
|
|
|
|
|
|
|
|
|
|
>










|







250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
  }
}

/*
** Show the context graph (immediate parents and children) for
** check-in rid.
*/
void render_checkin_context(int rid, int rid2, int parentsOnly){
  Blob sql;
  Stmt q;
  int rx[2];
  int i, n;
  rx[0] = rid;
  rx[1] = rid2;
  n = rid2 ? 2 : 1;
  blob_zero(&sql);
  blob_append(&sql, timeline_query_for_www(), -1);

  db_multi_exec(
    "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);"
    "DELETE FROM ok;"
  );
  for(i=0; i<n; i++){
    db_multi_exec(
      "INSERT OR IGNORE INTO ok VALUES(%d);"
      "INSERT OR IGNORE INTO ok SELECT pid FROM plink WHERE cid=%d;",
      rx[i], rx[i]
    );
  }
  if( !parentsOnly ){
    for(i=0; i<n; i++){
      db_multi_exec(
        "INSERT OR IGNORE INTO ok SELECT cid FROM plink WHERE pid=%d;", rx[i]
      );
      if( db_table_exists("repository","cherrypick") ){
        db_multi_exec(
          "INSERT OR IGNORE INTO ok "
          "  SELECT parentid FROM cherrypick WHERE childid=%d;"
          "INSERT OR IGNORE INTO ok "
          "  SELECT childid FROM cherrypick WHERE parentid=%d;",
          rx[i], rx[i]
        );
      }
    }
  }
  blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
  db_prepare(&q, "%s", blob_sql_text(&sql));
  www_print_timeline(&q,
          TIMELINE_GRAPH
         |TIMELINE_FILLGAPS
         |TIMELINE_NOSCROLL
         |TIMELINE_XMERGE
         |TIMELINE_CHPICK,
       0, 0, 0, rid, rid2, 0);
  db_finalize(&q);
}

/*
** Show a graph all wiki, tickets, and check-ins that refer to object zUuid.
**
** If zLabel is not NULL and the graph is not empty, then output zLabel as
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
  if( !db_exists("SELECT 1 FROM ok") ) return;
  if( zLabel ) cgi_printf("%s", zLabel);
  blob_zero(&sql);
  blob_append(&sql, timeline_query_for_www(), -1);
  blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
  db_prepare(&q, "%s", blob_sql_text(&sql));
  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
                     0, 0, 0, 0, 0);
  db_finalize(&q);
}

/*
** WEBPAGE: test-backlinks
**
** Show a timeline of all check-ins and other events that have entries







|







327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
  if( !db_exists("SELECT 1 FROM ok") ) return;
  if( zLabel ) cgi_printf("%s", zLabel);
  blob_zero(&sql);
  blob_append(&sql, timeline_query_for_www(), -1);
  blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
  db_prepare(&q, "%s", blob_sql_text(&sql));
  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
                     0, 0, 0, 0, 0, 0);
  db_finalize(&q);
}

/*
** WEBPAGE: test-backlinks
**
** Show a timeline of all check-ins and other events that have entries
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
     "  WHERE blob.uuid BETWEEN backlink.target AND (backlink.target||'x')"
  );
  blob_zero(&sql);
  blob_append(&sql, timeline_query_for_www(), -1);
  blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
  db_prepare(&q, "%s", blob_sql_text(&sql));
  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
                     0, 0, 0, 0, 0);
  db_finalize(&q);
  style_footer();
}


/*
** Append the difference between artifacts to the output







|







360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
     "  WHERE blob.uuid BETWEEN backlink.target AND (backlink.target||'x')"
  );
  blob_zero(&sql);
  blob_append(&sql, timeline_query_for_www(), -1);
  blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
  db_prepare(&q, "%s", blob_sql_text(&sql));
  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
                     0, 0, 0, 0, 0, 0);
  db_finalize(&q);
  style_footer();
}


/*
** Append the difference between artifacts to the output
603
604
605
606
607
608
609

610
611
612
613
614
615
616
617
618

619
620
621
622
623
624
625
626
627
628
629
630
631
     "  WHERE tagxref.rid=%d;"
     "INSERT OR IGNORE INTO ok "
     " SELECT tagxref.origid"
     "   FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid"
     "  WHERE tagxref.rid=%d;",
     rid, rid, rid
  );

  db_multi_exec(
    "SELECT tag.tagid, tagname, "
    "       (SELECT uuid FROM blob WHERE rid=tagxref.srcid AND rid!=%d),"
    "       value, datetime(tagxref.mtime,toLocal()), tagtype,"
    "       (SELECT uuid FROM blob WHERE rid=tagxref.origid AND rid!=%d)"
    "  FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid"
    " WHERE tagxref.rid=%d"
    " ORDER BY tagname /*sort*/", rid, rid, rid
  );

  blob_zero(&sql);
  blob_append(&sql, timeline_query_for_www(), -1);
  blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
  db_prepare(&q, "%s", blob_sql_text(&sql));
  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
                     0, 0, 0, rid, 0);
  db_finalize(&q);
  style_footer();
}

/*
** WEBPAGE: vinfo
** WEBPAGE: ci







>









>





|







615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
     "  WHERE tagxref.rid=%d;"
     "INSERT OR IGNORE INTO ok "
     " SELECT tagxref.origid"
     "   FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid"
     "  WHERE tagxref.rid=%d;",
     rid, rid, rid
  );
#if 0
  db_multi_exec(
    "SELECT tag.tagid, tagname, "
    "       (SELECT uuid FROM blob WHERE rid=tagxref.srcid AND rid!=%d),"
    "       value, datetime(tagxref.mtime,toLocal()), tagtype,"
    "       (SELECT uuid FROM blob WHERE rid=tagxref.origid AND rid!=%d)"
    "  FROM tagxref JOIN tag ON tagxref.tagid=tag.tagid"
    " WHERE tagxref.rid=%d"
    " ORDER BY tagname /*sort*/", rid, rid, rid
  );
#endif
  blob_zero(&sql);
  blob_append(&sql, timeline_query_for_www(), -1);
  blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
  db_prepare(&q, "%s", blob_sql_text(&sql));
  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
                     0, 0, 0, rid, 0, 0);
  db_finalize(&q);
  style_footer();
}

/*
** WEBPAGE: vinfo
** WEBPAGE: ci
649
650
651
652
653
654
655

656
657
658
659
660
661
662
  const char *zUuid;   /* UUID of zName */
  const char *zParent; /* UUID of the parent check-in (if any) */
  const char *zRe;     /* regex parameter */
  ReCompiled *pRe = 0; /* regex */
  const char *zW;      /* URL param for ignoring whitespace */
  const char *zPage = "vinfo";  /* Page that shows diffs */
  const char *zPageHide = "ci"; /* Page that hides diffs */


  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  zName = P("name");
  rid = name_to_rid_www("name");
  if( rid==0 ){
    style_header("Check-in Information Error");







>







663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
  const char *zUuid;   /* UUID of zName */
  const char *zParent; /* UUID of the parent check-in (if any) */
  const char *zRe;     /* regex parameter */
  ReCompiled *pRe = 0; /* regex */
  const char *zW;      /* URL param for ignoring whitespace */
  const char *zPage = "vinfo";  /* Page that shows diffs */
  const char *zPageHide = "ci"; /* Page that hides diffs */
  const char *zBrName; /* Branch name */

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  zName = P("name");
  rid = name_to_rid_www("name");
  if( rid==0 ){
    style_header("Check-in Information Error");
677
678
679
680
681
682
683

684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
     "SELECT uuid, datetime(mtime,toLocal()), user, comment,"
     "       datetime(omtime,toLocal()), mtime"
     "  FROM blob, event"
     " WHERE blob.rid=%d"
     "   AND event.objid=%d",
     rid, rid
  );

  
  cookie_link_parameter("diff","diff","2");
  diffType = atoi(PD("diff","2"));
  if( db_step(&q1)==SQLITE_ROW ){
    const char *zUuid = db_column_text(&q1, 0);
    int nUuid = db_column_bytes(&q1, 0);
    char *zEUser, *zEComment;
    const char *zUser;
    const char *zOrigUser;
    const char *zComment;
    const char *zDate;
    const char *zOrigDate;
    const char *zBrName;
    int okWiki = 0;
    Blob wiki_read_links = BLOB_INITIALIZER;
    Blob wiki_add_links = BLOB_INITIALIZER;

    style_header("Check-in [%S]", zUuid);
    login_anonymous_available();
    zEUser = db_text(0,
                   "SELECT value FROM tagxref"
                   " WHERE tagid=%d AND rid=%d AND tagtype>0",
                    TAG_USER, rid);
    zEComment = db_text(0,
                   "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
                   TAG_COMMENT, rid);
    zBrName = db_text(0,
                   "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
                   TAG_BRANCH, rid);
    zOrigUser = db_column_text(&q1, 2);
    zUser = zEUser ? zEUser : zOrigUser;
    zComment = db_column_text(&q1, 3);
    zDate = db_column_text(&q1,1);
    zOrigDate = db_column_text(&q1, 4);
    if( zOrigDate==0 ) zOrigDate = zDate;
    @ <div class="section">Overview</div>







>












<













<
<
<







692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711

712
713
714
715
716
717
718
719
720
721
722
723
724



725
726
727
728
729
730
731
     "SELECT uuid, datetime(mtime,toLocal()), user, comment,"
     "       datetime(omtime,toLocal()), mtime"
     "  FROM blob, event"
     " WHERE blob.rid=%d"
     "   AND event.objid=%d",
     rid, rid
  );
  zBrName = branch_of_rid(rid);
  
  cookie_link_parameter("diff","diff","2");
  diffType = atoi(PD("diff","2"));
  if( db_step(&q1)==SQLITE_ROW ){
    const char *zUuid = db_column_text(&q1, 0);
    int nUuid = db_column_bytes(&q1, 0);
    char *zEUser, *zEComment;
    const char *zUser;
    const char *zOrigUser;
    const char *zComment;
    const char *zDate;
    const char *zOrigDate;

    int okWiki = 0;
    Blob wiki_read_links = BLOB_INITIALIZER;
    Blob wiki_add_links = BLOB_INITIALIZER;

    style_header("Check-in [%S]", zUuid);
    login_anonymous_available();
    zEUser = db_text(0,
                   "SELECT value FROM tagxref"
                   " WHERE tagid=%d AND rid=%d AND tagtype>0",
                    TAG_USER, rid);
    zEComment = db_text(0,
                   "SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
                   TAG_COMMENT, rid);



    zOrigUser = db_column_text(&q1, 2);
    zUser = zEUser ? zEUser : zOrigUser;
    zComment = db_column_text(&q1, 3);
    zDate = db_column_text(&q1,1);
    zOrigDate = db_column_text(&q1, 4);
    if( zOrigDate==0 ) zOrigDate = zDate;
    @ <div class="section">Overview</div>
870
871
872
873
874
875
876



877
878
879
880
881
882
883
884
      }
      @ %s(zLinks)</td></tr>
    }

    if( g.perm.Hyperlink ){
      @ <tr><th>Other&nbsp;Links:</th>
      @   <td>



      @   %z(href("%R/artifact/%!S",zUuid))manifest</a>
      @ | %z(href("%R/ci_tags/%!S",zUuid))tags</a>
      if( g.perm.Admin ){
        @   | %z(href("%R/mlink?ci=%!S",zUuid))mlink table</a>
      }
      if( g.anon.Write ){
        @   | %z(href("%R/ci_edit?r=%!S",zUuid))edit</a>
      }







>
>
>
|







882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
      }
      @ %s(zLinks)</td></tr>
    }

    if( g.perm.Hyperlink ){
      @ <tr><th>Other&nbsp;Links:</th>
      @   <td>
      if( fossil_strcmp(zBrName, db_get("main-branch","trunk"))!=0 ){
        @ %z(href("%R/vdiff?branch=%!S", zUuid))branch diff</a> |
      }
      @ %z(href("%R/artifact/%!S",zUuid))manifest</a>
      @ | %z(href("%R/ci_tags/%!S",zUuid))tags</a>
      if( g.perm.Admin ){
        @   | %z(href("%R/mlink?ci=%!S",zUuid))mlink table</a>
      }
      if( g.anon.Write ){
        @   | %z(href("%R/ci_edit?r=%!S",zUuid))edit</a>
      }
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
  }
  db_finalize(&q1);
  if( !PB("nowiki") ){
    wiki_render_associated("checkin", zUuid, 0);
  }
  render_backlink_graph(zUuid, "<div class=\"section\">References</div>\n");
  @ <div class="section">Context</div>
  render_checkin_context(rid, 0);
  @ <div class="section">Changes</div>
  @ <div class="sectionmenu">
  diffFlags = construct_diff_flags(diffType);
  zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
  if( diffType!=0 ){
    @ %z(chref("button","%R/%s/%T?diff=0",zPageHide,zName))\
    @ Hide&nbsp;Diffs</a>







|







909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
  }
  db_finalize(&q1);
  if( !PB("nowiki") ){
    wiki_render_associated("checkin", zUuid, 0);
  }
  render_backlink_graph(zUuid, "<div class=\"section\">References</div>\n");
  @ <div class="section">Context</div>
  render_checkin_context(rid, 0, 0);
  @ <div class="section">Changes</div>
  @ <div class="sectionmenu">
  diffFlags = construct_diff_flags(diffType);
  zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
  if( diffType!=0 ){
    @ %z(chref("button","%R/%s/%T?diff=0",zPageHide,zName))\
    @ Hide&nbsp;Diffs</a>
1095
1096
1097
1098
1099
1100
1101

1102
1103
1104
1105
1106
1107
1108
  if( !is_a_version(rid) ){
    webpage_error("Artifact %s is not a check-in.", P(zParam));
    return 0;
  }
  return manifest_get(rid, CFTYPE_MANIFEST, 0);
}


/*
** Output a description of a check-in
*/
static void checkin_description(int rid){
  Stmt q;
  db_prepare(&q,
    "SELECT datetime(mtime), coalesce(euser,user),"







>







1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
  if( !is_a_version(rid) ){
    webpage_error("Artifact %s is not a check-in.", P(zParam));
    return 0;
  }
  return manifest_get(rid, CFTYPE_MANIFEST, 0);
}

#if 0 /* not used */
/*
** Output a description of a check-in
*/
static void checkin_description(int rid){
  Stmt q;
  db_prepare(&q,
    "SELECT datetime(mtime), coalesce(euser,user),"
1153
1154
1155
1156
1157
1158
1159

1160
1161
1162
1163
1164
1165
1166
    }
    @ date:
    hyperlink_to_date(zDate, ")");
    tag_private_status(rid);
  }
  db_finalize(&q);
}



/*
** WEBPAGE: vdiff
** URL: /vdiff?from=TAG&to=TAG
**
** Show the difference between two check-ins identified by the from= and







>







1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
    }
    @ date:
    hyperlink_to_date(zDate, ")");
    tag_private_status(rid);
  }
  db_finalize(&q);
}
#endif /* not used */


/*
** WEBPAGE: vdiff
** URL: /vdiff?from=TAG&to=TAG
**
** Show the difference between two check-ins identified by the from= and
1188
1189
1190
1191
1192
1193
1194


1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206



1207
1208


1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221

1222

1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239

1240
1241
1242

1243
1244
1245
1246
1247
1248
1249
1250
1251



1252

1253
















1254
1255

1256

1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267

1268
1269
1270
1271
1272
1273
1274
  ManifestFile *pFileFrom, *pFileTo;
  const char *zBranch;
  const char *zFrom;
  const char *zTo;
  const char *zRe;
  const char *zW;
  const char *zGlob;


  ReCompiled *pRe = 0;
  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  login_anonymous_available();
  load_control();
  cookie_link_parameter("diff","diff","2");
  diffType = atoi(PD("diff","2"));
  cookie_render();
  zRe = P("regex");
  if( zRe ) re_compile(&pRe, zRe, 0);
  zBranch = P("branch");
  if( zBranch && zBranch[0] ){



    cgi_replace_parameter("from", mprintf("root:%s", zBranch));
    cgi_replace_parameter("to", zBranch);


  }
  pTo = vdiff_parse_manifest("to", &ridTo);
  if( pTo==0 ) return;
  pFrom = vdiff_parse_manifest("from", &ridFrom);
  if( pFrom==0 ) return;
  zGlob = P("glob");
  zFrom = P("from");
  zTo = P("to");
  if(zGlob && !*zGlob){
    zGlob = NULL;
  }
  diffFlags = construct_diff_flags(diffType);
  zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";

  style_submenu_element("Path", "%R/timeline?me=%T&you=%T", zFrom, zTo);

  if( diffType!=0 ){
    style_submenu_element("Hide Diff", "%R/vdiff?from=%T&to=%T&diff=0%s%T%s",
                          zFrom, zTo,
                          zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
  }
  if( diffType!=2 ){
    style_submenu_element("Side-by-Side Diff",
                          "%R/vdiff?from=%T&to=%T&diff=2%s%T%s",
                          zFrom, zTo,
                          zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
  }
  if( diffType!=1 ) {
    style_submenu_element("Unified Diff",
                          "%R/vdiff?from=%T&to=%T&diff=1%s%T%s",
                          zFrom, zTo,
                          zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
  }

  style_submenu_element("Invert",
                        "%R/vdiff?from=%T&to=%T&%s%T%s", zTo, zFrom,
                        zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);

  if( zGlob ){
    style_submenu_element("Clear glob",
                          "%R/vdiff?from=%T&to=%T&%s", zFrom, zTo, zW);
  }else{
    style_submenu_element("Patch", "%R/vpatch?from=%T&to=%T%s", zFrom, zTo, zW);
  }
  if( diffType!=0 ){
    style_submenu_checkbox("w", "Ignore Whitespace", 0, 0);
  }



  style_header("Check-in Differences");

  if( P("nohdr")==0 ){
















    @ <h2>Difference From:</h2><blockquote>
    checkin_description(ridFrom);

    @ </blockquote><h2>To:</h2><blockquote>

    checkin_description(ridTo);
    @ </blockquote>
    if( pRe ){
      @ <p><b>Only differences that match regular expression "%h(zRe)"
      @ are shown.</b></p>
    }
    if( zGlob ){
      @ <p><b>Only files matching the glob "%h(zGlob)" are shown.</b></p>
    }
    @<hr /><p>
  }


  manifest_file_rewind(pFrom);
  pFileFrom = manifest_file_next(pFrom, 0);
  manifest_file_rewind(pTo);
  pFileTo = manifest_file_next(pTo, 0);
  while( pFileFrom || pFileTo ){
    int cmp;







>
>











|
>
>
>
|

>
>













>
|
>

|
|




|
|




|
|


>
|
|
|
>


|






>
>
>
|
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
>
|
>
|
<









>







1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307

1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
  ManifestFile *pFileFrom, *pFileTo;
  const char *zBranch;
  const char *zFrom;
  const char *zTo;
  const char *zRe;
  const char *zW;
  const char *zGlob;
  char *zQuery;
  char *zMergeOrigin = 0;
  ReCompiled *pRe = 0;
  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  login_anonymous_available();
  load_control();
  cookie_link_parameter("diff","diff","2");
  diffType = atoi(PD("diff","2"));
  cookie_render();
  zRe = P("regex");
  if( zRe ) re_compile(&pRe, zRe, 0);
  zBranch = P("branch");
  if( zBranch && zBranch[0]==0 ) zBranch = 0;
  if( zBranch ){
    zQuery = mprintf("branch=%T", zBranch);
    zMergeOrigin = mprintf("merge-in:%s", zBranch);
    cgi_replace_parameter("from", zMergeOrigin);
    cgi_replace_parameter("to", zBranch);
  }else{
    zQuery = mprintf("from=%T&to=%T",PD("from",""),PD("to",""));
  }
  pTo = vdiff_parse_manifest("to", &ridTo);
  if( pTo==0 ) return;
  pFrom = vdiff_parse_manifest("from", &ridFrom);
  if( pFrom==0 ) return;
  zGlob = P("glob");
  zFrom = P("from");
  zTo = P("to");
  if(zGlob && !*zGlob){
    zGlob = NULL;
  }
  diffFlags = construct_diff_flags(diffType);
  zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
  if( zBranch==0 ){
    style_submenu_element("Path", "%R/timeline?me=%T&you=%T", zFrom, zTo);
  }
  if( diffType!=0 ){
    style_submenu_element("Hide Diff", "%R/vdiff?%s&diff=0%s%T%s",
                          zQuery,
                          zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
  }
  if( diffType!=2 ){
    style_submenu_element("Side-by-Side Diff",
                          "%R/vdiff?%s&diff=2%s%T%s",
                          zQuery,
                          zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
  }
  if( diffType!=1 ) {
    style_submenu_element("Unified Diff",
                          "%R/vdiff?%s&diff=1%s%T%s",
                          zQuery,
                          zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
  }
  if( zBranch==0 ){
    style_submenu_element("Invert",
                          "%R/vdiff?from=%T&to=%T&%s%T%s", zTo, zFrom,
                          zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
  }
  if( zGlob ){
    style_submenu_element("Clear glob",
                          "%R/vdiff?%s&%s", zQuery, zW);
  }else{
    style_submenu_element("Patch", "%R/vpatch?from=%T&to=%T%s", zFrom, zTo, zW);
  }
  if( diffType!=0 ){
    style_submenu_checkbox("w", "Ignore Whitespace", 0, 0);
  }
  if( zBranch ){
    style_header("Changes On Branch %h", zBranch);
  }else{
    style_header("Check-in Differences");
  }
  if( P("nohdr")==0 ){
    if( zBranch ){
      char *zRealBranch = branch_of_rid(ridTo);
      char *zToUuid = rid_to_uuid(ridTo);
      char *zFromUuid = rid_to_uuid(ridFrom);
      @ <h2>Changes In Branch \
      @ %z(href("%R/timeline?r=%T",zRealBranch))%h(zRealBranch)</a>
      if( ridTo != symbolic_name_to_rid(zRealBranch,"ci") ){
        @ Through %z(href("%R/info/%!S",zToUuid))[%S(zToUuid)]</a>
      }
      @ Excluding Merge-Ins</h2>
      @ <p>This is equivalent to a diff from
      @ <span class='timelineSelected'>\
      @ %z(href("%R/info/%!S",zFromUuid))%S(zFromUuid)</a></span>
      @ to <span class='timelineSelected timelineSecondary'>\
      @ %z(href("%R/info/%!S",zToUuid))%S(zToUuid)</a></span></p>
    }else{
      @ <h2>Difference From <span class='timelineSelected'>\
      @ %z(href("%R/info/%h",zFrom))%h(zFrom)</a></span>
      @ To <span class='timelineSelected timelineSecondary'>\
      @ %z(href("%R/info/%h",zTo))%h(zTo)</a></span></h2>
    }
    render_checkin_context(ridFrom, ridTo, 0);

    if( pRe ){
      @ <p><b>Only differences that match regular expression "%h(zRe)"
      @ are shown.</b></p>
    }
    if( zGlob ){
      @ <p><b>Only files matching the glob "%h(zGlob)" are shown.</b></p>
    }
    @<hr /><p>
  }
  fossil_free(zQuery);

  manifest_file_rewind(pFrom);
  pFileFrom = manifest_file_next(pFrom, 0);
  manifest_file_rewind(pTo);
  pFileTo = manifest_file_next(pTo, 0);
  while( pFileFrom || pFileTo ){
    int cmp;

Changes to src/moderate.c.

182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
  if( moderation_table_exists() ){
    blob_init(&sql, timeline_query_for_www(), -1);
    blob_append_sql(&sql,
        " AND event.objid IN (SELECT objid FROM modreq)"
        " ORDER BY event.mtime DESC"
    );
    db_prepare(&q, "%s", blob_sql_text(&sql));
    www_print_timeline(&q, 0, 0, 0, 0, 0, 0);
    db_finalize(&q);
  }
  style_footer();
}

/*
** Disapproves any entries in the modreq table which belong to any







|







182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
  if( moderation_table_exists() ){
    blob_init(&sql, timeline_query_for_www(), -1);
    blob_append_sql(&sql,
        " AND event.objid IN (SELECT objid FROM modreq)"
        " ORDER BY event.mtime DESC"
    );
    db_prepare(&q, "%s", blob_sql_text(&sql));
    www_print_timeline(&q, 0, 0, 0, 0, 0, 0, 0);
    db_finalize(&q);
  }
  style_footer();
}

/*
** Disapproves any entries in the modreq table which belong to any

Changes to src/name.c.

106
107
108
109
110
111
112
113
114







115
116
117
118

119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141





142
143
144
145
146
147
148
149

  /* It looks like this may be a date.  Return it with punctuation added. */
  return zEDate;
}

/*
** Return the RID that is the "root" of the branch that contains
** check-in "rid" if inBranch==0 or the first check-in in the branch
** if inBranch==1.







*/
int start_of_branch(int rid, int inBranch){
  Stmt q;
  int rc;

  char *zBr;
  zBr = db_text("trunk","SELECT value FROM tagxref"
                        " WHERE rid=%d AND tagid=%d"
                        " AND tagtype>0",
                        rid, TAG_BRANCH);
  db_prepare(&q,
    "SELECT pid, EXISTS(SELECT 1 FROM tagxref"
                       " WHERE tagid=%d AND tagtype>0"
                       "   AND value=%Q AND rid=plink.pid)"
    "  FROM plink"
    " WHERE cid=:cid AND isprim",
    TAG_BRANCH, zBr
  );
  fossil_free(zBr);
  do{
    db_reset(&q);
    db_bind_int(&q, ":cid", rid);
    rc = db_step(&q);
    if( rc!=SQLITE_ROW ) break;
    if( inBranch && db_column_int(&q,1)==0 ) break;
    rid = db_column_int(&q, 0);
  }while( db_column_int(&q, 1)==1 && rid>0 );
  db_finalize(&q);





  return rid;
}

/*
** Convert a symbolic name into a RID.  Acceptable forms:
**
**   *  artifact hash (optionally enclosed in [...])
**   *  4-character or larger prefix of a artifact







|
|
>
>
>
>
>
>
>

|


>
|
<
<
<
<











|


|
|
|

>
>
>
>
>
|







106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127




128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158

  /* It looks like this may be a date.  Return it with punctuation added. */
  return zEDate;
}

/*
** Return the RID that is the "root" of the branch that contains
** check-in "rid".  Details depending on eType:
**
**    eType==0    The check-in of the parent branch off of which
**                the branch containing RID originally diverged.
**
**    eType==1    The first check-in of the branch that contains RID.
**
**    eType==2    The youngest ancestor of RID that is on the branch
**                from which the branch containing RID diverged.
*/
int start_of_branch(int rid, int eType){
  Stmt q;
  int rc;
  int ans = rid;
  char *zBr = branch_of_rid(rid);




  db_prepare(&q,
    "SELECT pid, EXISTS(SELECT 1 FROM tagxref"
                       " WHERE tagid=%d AND tagtype>0"
                       "   AND value=%Q AND rid=plink.pid)"
    "  FROM plink"
    " WHERE cid=:cid AND isprim",
    TAG_BRANCH, zBr
  );
  fossil_free(zBr);
  do{
    db_reset(&q);
    db_bind_int(&q, ":cid", ans);
    rc = db_step(&q);
    if( rc!=SQLITE_ROW ) break;
    if( eType==1 && db_column_int(&q,1)==0 ) break;
    ans = db_column_int(&q, 0);
  }while( db_column_int(&q, 1)==1 && ans>0 );
  db_finalize(&q);
  if( eType==2 && ans>0 ){
    zBr = branch_of_rid(ans);
    ans = compute_youngest_ancestor_in_branch(rid, zBr);
    fossil_free(zBr);
  }
  return ans;
}

/*
** Convert a symbolic name into a RID.  Acceptable forms:
**
**   *  artifact hash (optionally enclosed in [...])
**   *  4-character or larger prefix of a artifact
163
164
165
166
167
168
169

170
171
172
173
174
175
176
** Return the RID of the matching artifact.  Or return 0 if the name does not
** match any known object.  Or return -1 if the name is ambiguous.
**
** The zType parameter specifies the type of artifact: ci, t, w, e, g, f.
** If zType is NULL or "" or "*" then any type of artifact will serve.
** If zType is "br" then find the first check-in of the named branch
** rather than the last.

** zType is "ci" in most use cases since we are usually searching for
** a check-in.
**
** Note that the input zTag for types "t" and "e" is the artifact hash of
** the ticket-change or technote-change artifact, not the randomly generated
** hexadecimal identifier assigned to tickets and events.  Those identifiers
** live in a separate namespace.







>







172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
** Return the RID of the matching artifact.  Or return 0 if the name does not
** match any known object.  Or return -1 if the name is ambiguous.
**
** The zType parameter specifies the type of artifact: ci, t, w, e, g, f.
** If zType is NULL or "" or "*" then any type of artifact will serve.
** If zType is "br" then find the first check-in of the named branch
** rather than the last.
**
** zType is "ci" in most use cases since we are usually searching for
** a check-in.
**
** Note that the input zTag for types "t" and "e" is the artifact hash of
** the ticket-change or technote-change artifact, not the randomly generated
** hexadecimal identifier assigned to tickets and events.  Those identifiers
** live in a separate namespace.
268
269
270
271
272
273
274
275
276
277
278





279
280
281
282
283
284
285
       "   AND event.type GLOB '%q'",
       &zTag[4], zType
    );
    if( startOfBranch ) rid = start_of_branch(rid,1);
    return rid;
  }

  /* root:TAG -> The origin of the branch */
  if( memcmp(zTag, "root:", 5)==0 ){
    rid = symbolic_name_to_rid(zTag+5, zType);
    return start_of_branch(rid, 0);





  }

  /* symbolic-name ":" date-time */
  nTag = strlen(zTag);
  for(i=0; i<nTag-10 && zTag[i]!=':'; i++){}
  if( zTag[i]==':' && fossil_isdate(&zTag[i+1]) ){
    char *zDate = mprintf("%s", &zTag[i+1]);







|
|


>
>
>
>
>







278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
       "   AND event.type GLOB '%q'",
       &zTag[4], zType
    );
    if( startOfBranch ) rid = start_of_branch(rid,1);
    return rid;
  }

  /* root:BR -> The origin of the branch named BR */
  if( strncmp(zTag, "root:", 5)==0 ){
    rid = symbolic_name_to_rid(zTag+5, zType);
    return start_of_branch(rid, 0);
  }
  /* rootx:BR -> Most recent merge-in for the branch name BR */
  if( strncmp(zTag, "merge-in:", 9)==0 ){
    rid = symbolic_name_to_rid(zTag+9, zType);
    return start_of_branch(rid, 2);
  }

  /* symbolic-name ":" date-time */
  nTag = strlen(zTag);
  for(i=0; i<nTag-10 && zTag[i]!=':'; i++){}
  if( zTag[i]==':' && fossil_isdate(&zTag[i+1]) ){
    char *zDate = mprintf("%s", &zTag[i+1]);
463
464
465
466
467
468
469


470

471
472
473

474
475
476






477
478
479
480
481
482
483
484
485

486
487
488
489
490
491
492
  }
  return c;
}

/*
** COMMAND: test-name-to-id
**


** Convert a name to a full artifact ID.

*/
void test_name_to_id(void){
  int i;

  Blob name;
  db_must_be_within_tree();
  for(i=2; i<g.argc; i++){






    blob_init(&name, g.argv[i], -1);
    fossil_print("%s -> ", g.argv[i]);
    if( name_to_uuid(&name, 1, "*") ){
      fossil_print("ERROR: %s\n", g.zErrMsg);
      fossil_error_reset();
    }else{
      fossil_print("%s\n", blob_buffer(&name));
    }
    blob_reset(&name);

  }
}

/*
** Convert a name to a rid.  If the name can be any of the various forms
** accepted:
**







>
>
|
>



>



>
>
>
>
>
>
|
|
|
|
|
|
|
|
|
>







478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
  }
  return c;
}

/*
** COMMAND: test-name-to-id
**
** Usage:  %fossil test-name-to-id [--count N] NAME
**
** Convert a NAME to a full artifact ID.  Repeat the conversion N
** times (for timing purposes) if the --count option is given.
*/
void test_name_to_id(void){
  int i;
  int n = 0;
  Blob name;
  db_must_be_within_tree();
  for(i=2; i<g.argc; i++){
    if( strcmp(g.argv[i],"--count")==0 && i+1<g.argc ){
      i++;
      n = atoi(g.argv[i]);
      continue;
    }
    do{
      blob_init(&name, g.argv[i], -1);
      fossil_print("%s -> ", g.argv[i]);
      if( name_to_uuid(&name, 1, "*") ){
        fossil_print("ERROR: %s\n", g.zErrMsg);
        fossil_error_reset();
      }else{
        fossil_print("%s\n", blob_buffer(&name));
      }
      blob_reset(&name);
    }while( n-- > 0 );
  }
}

/*
** Convert a name to a rid.  If the name can be any of the various forms
** accepted:
**

Changes to src/tag.c.

774
775
776
777
778
779
780
781
782
783
784
785
  blob_reset(&sql);
  /* Always specify TIMELINE_DISJOINT, or graph_finish() may fail because of too
  ** many descenders to (off-screen) parents. */
  tmFlags = TIMELINE_XMERGE | TIMELINE_FILLGAPS | TIMELINE_NOSCROLL;
  if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH;
  if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR;
  if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR;
  www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0);
  db_finalize(&q);
  @ <br />
  style_footer();
}







|




774
775
776
777
778
779
780
781
782
783
784
785
  blob_reset(&sql);
  /* Always specify TIMELINE_DISJOINT, or graph_finish() may fail because of too
  ** many descenders to (off-screen) parents. */
  tmFlags = TIMELINE_XMERGE | TIMELINE_FILLGAPS | TIMELINE_NOSCROLL;
  if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH;
  if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR;
  if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR;
  www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0);
  db_finalize(&q);
  @ <br />
  style_footer();
}

Changes to src/timeline.c.

241
242
243
244
245
246
247
248

249
250
251
252
253
254
255
*/
void www_print_timeline(
  Stmt *pQuery,            /* Query to implement the timeline */
  int tmFlags,             /* Flags controlling display behavior */
  const char *zThisUser,   /* Suppress links to this user */
  const char *zThisTag,    /* Suppress links to this tag */
  const char *zLeftBranch, /* Strive to put this branch on the left margin */
  int selectedRid,         /* Highlight the line with this RID value */

  void (*xExtra)(int)      /* Routine to call on each line of display */
){
  int mxWikiLen;
  Blob comment;
  int prevTagid = 0;
  int suppressCnt = 0;
  char zPrevDate[20];







|
>







241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
*/
void www_print_timeline(
  Stmt *pQuery,            /* Query to implement the timeline */
  int tmFlags,             /* Flags controlling display behavior */
  const char *zThisUser,   /* Suppress links to this user */
  const char *zThisTag,    /* Suppress links to this tag */
  const char *zLeftBranch, /* Strive to put this branch on the left margin */
  int selectedRid,         /* Highlight the line with this RID value or zero */
  int secondRid,           /* Secondary highlight (or zero) */
  void (*xExtra)(int)      /* Routine to call on each line of display */
){
  int mxWikiLen;
  Blob comment;
  int prevTagid = 0;
  int suppressCnt = 0;
  char zPrevDate[20];
393
394
395
396
397
398
399



400
401
402
403
404
405
406
    }else{
      zTime[0] = 0;
    }
    pendingEndTr = 1;
    if( rid==selectedRid ){
      @ <tr class="timelineSelected">
      isSelectedOrCurrent = 1;



    }else if( rid==vid ){
      @ <tr class="timelineCurrent">
      isSelectedOrCurrent = 1;
    }else {
      @ <tr>
    }
    if( zType[0]=='e' && tagid ){







>
>
>







394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
    }else{
      zTime[0] = 0;
    }
    pendingEndTr = 1;
    if( rid==selectedRid ){
      @ <tr class="timelineSelected">
      isSelectedOrCurrent = 1;
    }else if( rid==secondRid ){
      @ <tr class="timelineSelected timelineSecondary">
      isSelectedOrCurrent = 1;
    }else if( rid==vid ){
      @ <tr class="timelineCurrent">
      isSelectedOrCurrent = 1;
    }else {
      @ <tr>
    }
    if( zType[0]=='e' && tagid ){
1599
1600
1601
1602
1603
1604
1605
1606

1607
1608
1609
1610
1611
1612
1613
  int me_rid = name_to_typed_rid(P("me"),"ci");  /* me= for common ancestory */
  int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */
  int pd_rid;
  double rBefore, rAfter, rCirca;     /* Boundary times */
  const char *z;
  char *zOlderButton = 0;             /* URL for Older button at the bottom */
  char *zNewerButton = 0;             /* URL for Newer button at the top */
  int selectedRid = -9999999;         /* Show a highlight on this RID */

  int disableY = 0;                   /* Disable type selector on submenu */
  int advancedMenu = 0;               /* Use the advanced menu design */
  char *zPlural;                      /* Ending for plural forms */
  int showCherrypicks = 1;            /* True to show cherrypick merges */

  /* Set number of rows to display */
  cookie_read_parameter("n","n");







|
>







1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
  int me_rid = name_to_typed_rid(P("me"),"ci");  /* me= for common ancestory */
  int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */
  int pd_rid;
  double rBefore, rAfter, rCirca;     /* Boundary times */
  const char *z;
  char *zOlderButton = 0;             /* URL for Older button at the bottom */
  char *zNewerButton = 0;             /* URL for Newer button at the top */
  int selectedRid = 0;                /* Show a highlight on this RID */
  int secondaryRid = 0;               /* Show secondary highlight */
  int disableY = 0;                   /* Disable type selector on submenu */
  int advancedMenu = 0;               /* Use the advanced menu design */
  char *zPlural;                      /* Ending for plural forms */
  int showCherrypicks = 1;            /* True to show cherrypick merges */

  /* Set number of rows to display */
  cookie_read_parameter("n","n");
1623
1624
1625
1626
1627
1628
1629


1630
1631
1632
1633
1634
1635
1636
        nEntry = 10;
      }
    }
  }else{
    z = "50";
    nEntry = 50;
  }


  cgi_replace_query_parameter("n",z);
  cookie_write_parameter("n","n",0);
  tmFlags |= timeline_ss_submenu();
  cookie_link_parameter("advm","advm","0");
  advancedMenu = atoi(PD("advm","0"));

  /* Omit all cherry-pick merge lines if the "ncp" query parameter is







>
>







1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
        nEntry = 10;
      }
    }
  }else{
    z = "50";
    nEntry = 50;
  }
  secondaryRid = name_to_typed_rid(P("sel2"),"ci");
  selectedRid = name_to_typed_rid(P("sel1"),"ci");
  cgi_replace_query_parameter("n",z);
  cookie_write_parameter("n","n",0);
  tmFlags |= timeline_ss_submenu();
  cookie_link_parameter("advm","advm","0");
  advancedMenu = atoi(PD("advm","0"));

  /* Omit all cherry-pick merge lines if the "ncp" query parameter is
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396


2397
2398
2399
2400
2401
2402
2403
    if( r>0.0 ) selectedRid = timeline_add_divider(r);
  }
  blob_zero(&sql);
  db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/");
  if( fossil_islower(desc.aData[0]) ){
    desc.aData[0] = fossil_toupper(desc.aData[0]);
  }
  if( zBrName
   && !PB("nowiki")
   && wiki_render_associated("branch", zBrName, WIKIASSOC_ALL)
  ){
    @ <div class="section">%b(&desc)</div>


  }else
  if( zTagName
   && matchStyle==MS_EXACT
   && zBrName==0
   && !PB("nowiki")
   && wiki_render_associated("tag", zTagName, WIKIASSOC_ALL)
  ){







|
|
|
|
|
>
>







2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
    if( r>0.0 ) selectedRid = timeline_add_divider(r);
  }
  blob_zero(&sql);
  db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/");
  if( fossil_islower(desc.aData[0]) ){
    desc.aData[0] = fossil_toupper(desc.aData[0]);
  }
  if( zBrName ){
    if( !PB("nowiki")
     && wiki_render_associated("branch", zBrName, WIKIASSOC_ALL)
    ){
      @ <div class="section">%b(&desc)</div>
    }
    style_submenu_element("Diff", "%R/vdiff?branch=%T", zBrName);
  }else
  if( zTagName
   && matchStyle==MS_EXACT
   && zBrName==0
   && !PB("nowiki")
   && wiki_render_associated("tag", zTagName, WIKIASSOC_ALL)
  ){
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
    @ <p class="generalError">%h(zError)</p>
  }

  if( zNewerButton ){
    @ %z(chref("button","%z",zNewerButton))More&nbsp;&uarr;</a>
  }
  www_print_timeline(&q, tmFlags, zThisUser, zThisTag, zBrName,
                     selectedRid, 0);
  db_finalize(&q);
  if( zOlderButton ){
    @ %z(chref("button","%z",zOlderButton))More&nbsp;&darr;</a>
  }
  style_footer();
}








|







2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
    @ <p class="generalError">%h(zError)</p>
  }

  if( zNewerButton ){
    @ %z(chref("button","%z",zNewerButton))More&nbsp;&uarr;</a>
  }
  www_print_timeline(&q, tmFlags, zThisUser, zThisTag, zBrName,
                     selectedRid, secondaryRid, 0);
  db_finalize(&q);
  if( zOlderButton ){
    @ %z(chref("button","%z",zOlderButton))More&nbsp;&darr;</a>
  }
  style_footer();
}

2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
      continue;
    }
    zId = db_text(0, "SELECT timestamp FROM timeline"
                     " ORDER BY sortby DESC LIMIT 1");
    @ <h2>%d(iAgo) Year%s(iAgo>1?"s":"") Ago
    @ <small>%z(href("%R/timeline?c=%t",zId))(more context)</a>\
    @ </small></h2>
    www_print_timeline(&q, TIMELINE_GRAPH, 0, 0, 0, 0, 0);
  }
  db_finalize(&q);
  style_footer();
}


/*







|







2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
      continue;
    }
    zId = db_text(0, "SELECT timestamp FROM timeline"
                     " ORDER BY sortby DESC LIMIT 1");
    @ <h2>%d(iAgo) Year%s(iAgo>1?"s":"") Ago
    @ <small>%z(href("%R/timeline?c=%t",zId))(more context)</a>\
    @ </small></h2>
    www_print_timeline(&q, TIMELINE_GRAPH, 0, 0, 0, 0, 0, 0);
  }
  db_finalize(&q);
  style_footer();
}


/*

Changes to src/tkt.c.

894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
                  " WHERE target=%Q) "
         "ORDER BY mtime DESC",
         timeline_query_for_www(), tagid, zFullUuid, zFullUuid, zFullUuid
    );
  }
  db_prepare(&q, "%z", zSQL/*safe-for-%s*/);
  www_print_timeline(&q, TIMELINE_ARTID|TIMELINE_DISJOINT|TIMELINE_GRAPH,
                     0, 0, 0, 0, 0);
  db_finalize(&q);
  style_footer();
}

/*
** WEBPAGE: tkthistory
** URL: /tkthistory?name=TICKETUUID







|







894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
                  " WHERE target=%Q) "
         "ORDER BY mtime DESC",
         timeline_query_for_www(), tagid, zFullUuid, zFullUuid, zFullUuid
    );
  }
  db_prepare(&q, "%z", zSQL/*safe-for-%s*/);
  www_print_timeline(&q, TIMELINE_ARTID|TIMELINE_DISJOINT|TIMELINE_GRAPH,
                     0, 0, 0, 0, 0, 0);
  db_finalize(&q);
  style_footer();
}

/*
** WEBPAGE: tkthistory
** URL: /tkthistory?name=TICKETUUID