Fossil

Diff
Login

Diff

Differences From Artifact [e5be74d23c]:

To Artifact [88e68feb3c]:


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
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
                     0, 0, 0, rid, 0, 0);
  db_finalize(&q);
  style_finish_page();
}

/*
** WEBPAGE: ckout
**
** Show information about the current checkout.  This page only functions
** if the web server is run on a loopback interface (in other words, was
** started using "fossil ui" or similar) from with on open check-out.
**
** See the help screen for the /vdiff web page for a list of available
** keyboard shortcuts.
*/
void ckout_page(void){
  int vid;
  char *zHostname;
  char *zCwd;
  int diffType;               /* 0: no diff,  1: unified,  2: side-by-side */
  DiffConfig DCfg,*pCfg;      /* Diff details */
  const char *zHome;          /* Home directory */
  const char *zW;             /* The "w" query parameter */
  int nChng;                  /* Number of changes */
  Stmt q;

  if( !db_open_local(0) || !cgi_is_loopback(g.zIpAddr) ){
    cgi_redirectf("%R/home");
    return;
  }
  file_chdir(g.zLocalRoot, 0);
  diffType = preferred_diff_type();
  pCfg = construct_diff_flags(diffType, &DCfg);
  vid = db_lget_int("checkout", 0);
  db_unprotect(PROTECT_ALL);
  vfile_check_signature(vid, CKSIG_ENOTFILE);
  db_protect_pop();
  style_set_current_feature("vinfo");
  zHostname = fossil_hostname();
  zCwd = file_getcwd(0,0);
  zHome = fossil_getenv("HOME");
  if( zHome ){
    int nHome = (int)strlen(zHome);
    if( strncmp(zCwd, zHome, nHome)==0 && zCwd[nHome]=='/' ){
      zCwd = mprintf("~%s", zCwd+nHome);
    }
  }
  if( zHostname ){
    style_header("Checkout Status: %h on %h", zCwd, zHostname);
  }else{
    style_header("Checkout Status: %h", zCwd);
  }
  render_checkin_context(vid, 0, 0, 0);
  nChng = db_int(0, "SELECT count(*) FROM vfile"
                    " WHERE vid=%d AND (deleted OR chnged OR rid==0)", vid);
  if( nChng==0 ){
    @ <p>No uncommitted changes</p>
    style_finish_page();
    return;
  }
  db_prepare(&q,
       /*   0         1        2        3       4    5       6 */
    "SELECT pathname, deleted, chnged , rid==0, rid, islink, uuid"
    "  FROM vfile LEFT JOIN blob USING(rid)"
    " WHERE vid=%d"
    "   AND (deleted OR chnged OR rid==0)"
    " ORDER BY pathname /*scan*/",
    vid
  );
  if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
    DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
  }else{
    DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
  }
  @ <hr>
  @ <div class="sectionmenu info-changes-menu">
  zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
  if( diffType!=0 ){
    /* Class "smb-hide-diff" required by the fossil.diff.js script. */
    const char *zBtnClass = "button smb-hide-diff";
    @ %z(chref(zBtnClass,"%R?diff=0"))Hide&nbsp;Diff</a>
  }







|
<
<
<
<
<
<
<

|
<
<
<


<




<
<
<
<
<


<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




<
















<







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
632
633
634
635
636
637
638
639
640
641
642
643
644

645
646
647
648
649
650
651
  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
                     0, 0, 0, rid, 0, 0);
  db_finalize(&q);
  style_finish_page();
}

/*
** Render a web-page diff of the changes in the working check-out







*/
static void ckout_normal_diff(int vid){



  int diffType;               /* 0: no diff,  1: unified,  2: side-by-side */
  DiffConfig DCfg,*pCfg;      /* Diff details */

  const char *zW;             /* The "w" query parameter */
  int nChng;                  /* Number of changes */
  Stmt q;






  diffType = preferred_diff_type();
  pCfg = construct_diff_flags(diffType, &DCfg);




















  nChng = db_int(0, "SELECT count(*) FROM vfile"
                    " WHERE vid=%d AND (deleted OR chnged OR rid==0)", vid);
  if( nChng==0 ){
    @ <p>No uncommitted changes</p>

    return;
  }
  db_prepare(&q,
       /*   0         1        2        3       4    5       6 */
    "SELECT pathname, deleted, chnged , rid==0, rid, islink, uuid"
    "  FROM vfile LEFT JOIN blob USING(rid)"
    " WHERE vid=%d"
    "   AND (deleted OR chnged OR rid==0)"
    " ORDER BY pathname /*scan*/",
    vid
  );
  if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
    DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
  }else{
    DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
  }

  @ <div class="sectionmenu info-changes-menu">
  zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
  if( diffType!=0 ){
    /* Class "smb-hide-diff" required by the fossil.diff.js script. */
    const char *zBtnClass = "button smb-hide-diff";
    @ %z(chref(zBtnClass,"%R?diff=0"))Hide&nbsp;Diff</a>
  }
760
761
762
763
764
765
766










































767















































768





































































769
770
771
772
773
774
775
      blob_read_from_file(&new, zTreename, ExtFILE);
      text_diff(&old, &new, cgi_output_blob(), pCfg);
      blob_reset(&old);
      blob_reset(&new);
    }
  }
  db_finalize(&q);










































  // @ </div> <!-- ap-002 -->















































  append_diff_javascript(diffType);





































































  style_finish_page();
}

/*
** WEBPAGE: vinfo
** WEBPAGE: ci
** URL:  /ci/ARTIFACTID







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

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







722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
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
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
      blob_read_from_file(&new, zTreename, ExtFILE);
      text_diff(&old, &new, cgi_output_blob(), pCfg);
      blob_reset(&old);
      blob_reset(&new);
    }
  }
  db_finalize(&q);
  append_diff_javascript(diffType);
}

/*
** Render a web-page diff of the changes in the working check-out to
** an external reference.
*/
static void ckout_external_base_diff(int vid, const char *zExBase){
  int diffType;               /* 0: no diff,  1: unified,  2: side-by-side */
  DiffConfig DCfg,*pCfg;      /* Diff details */
  const char *zW;             /* The "w" query parameter */
  Stmt q;

  diffType = preferred_diff_type();
  pCfg = construct_diff_flags(diffType, &DCfg);
  db_prepare(&q,
    "SELECT pathname FROM vfile WHERE vid=%d ORDER BY pathname", vid
  );
  if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
    DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
  }else{
    DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
  }
  @ <div class="sectionmenu info-changes-menu">
  zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
  if( diffType!=1 ){
    @ %z(chref("button","%R?diff=1&exbase=%h%s",zExBase,zW))\
    @ Unified&nbsp;Diff</a>
  }
  if( diffType!=2 ){
    @ %z(chref("button","%R?diff=2&exbase=%h%s",zExBase,zW))\
    @ Side-by-Side&nbsp;Diff</a>
  }
  if( diffType!=0 ){
    if( *zW ){
      @ %z(chref("button","%R?diff=%d&exbase=%h",diffType,zExBase))\
      @ Show&nbsp;Whitespace&nbsp;Changes</a>
    }else{
      @ %z(chref("button","%R?diff=%d&exbase=%h&w",diffType,zExBase))\
      @ Ignore&nbsp;Whitespace</a>
    }
  }
  @ </div>
  while( db_step(&q)==SQLITE_ROW ){
    const char *zFile;  /* Name of file in the repository */
    char *zLhs;         /* Full name of left-hand side file */
    char *zRhs;         /* Full name of right-hand side file */
    Blob rhs;           /* Full text of RHS */
    Blob lhs;           /* Full text of LHS */

    zFile = db_column_text(&q,0);
    zLhs = mprintf("%s/%s", zExBase, zFile);
    zRhs = mprintf("%s%s", g.zLocalRoot, zFile);
    if( file_size(zLhs, ExtFILE)<0 ){
      @ <div class='file-change-line'><span>
      @ Missing from external baseline: %h(zFile)
      @ </span></div>
    }else{
      blob_read_from_file(&lhs, zLhs, ExtFILE);
      blob_read_from_file(&rhs, zRhs, ExtFILE);
      if( blob_size(&lhs)!=blob_size(&rhs)
       || memcmp(blob_buffer(&lhs), blob_buffer(&rhs), blob_size(&lhs))!=0
      ){
        @ <div class='file-change-line'><span>
        @ Changes to %h(zFile)
        @ </span></div>
        if( pCfg ){
          char *zFullFN;
          char *zHexFN;
          int nFullFN;
          zFullFN = file_canonical_name_dup(zLhs);
          nFullFN = (int)strlen(zFullFN);
          zHexFN = fossil_malloc( nFullFN*2 + 5 );
          zHexFN[0] = 'x';
          encode16((const u8*)zFullFN, (u8*)(zHexFN+1), nFullFN);
          zHexFN[1+nFullFN*2] = 0;
          fossil_free(zFullFN);
          pCfg->zLeftHash = zHexFN;
          text_diff(&lhs, &rhs, cgi_output_blob(), pCfg);
          pCfg->zLeftHash = 0;
          fossil_free(zHexFN);
        }
      }
      blob_reset(&lhs);
      blob_reset(&rhs);
    }
    fossil_free(zLhs);
    fossil_free(zRhs);
  }
  db_finalize(&q);
  append_diff_javascript(diffType);
}

/*
** WEBPAGE: ckout
**
** Show information about the current checkout.  This page only functions
** if the web server is run on a loopback interface (in other words, was
** started using "fossil ui" or similar) from within an open check-out.
**
** If the "exbase=PATH" query parameter is provided, then the diff shown
** uses the files in PATH as the baseline.  This is the same as using
** the "--from PATH" argument to the "fossil diff" command-line.  In fact,
** when using "fossil ui --from PATH", the --from argument becomes the value
** of the exbase query parameter for the start page.
**
** Other query parameters related to diffs are also accepted.
**
** See the help screen for the /vdiff web page for a list of available
** keyboard shortcuts.
*/
void ckout_page(void){
  int vid;
  const char *zHome;          /* Home directory */
  int nHome;
  const char *zExBase;
  char *zHostname;
  char *zCwd;

  if( !db_open_local(0) || !cgi_is_loopback(g.zIpAddr) ){
    cgi_redirectf("%R/home");
    return;
  }
  file_chdir(g.zLocalRoot, 0);
  vid = db_lget_int("checkout", 0);
  db_unprotect(PROTECT_ALL);
  vfile_check_signature(vid, CKSIG_ENOTFILE);
  db_protect_pop();
  style_set_current_feature("vinfo");
  zHostname = fossil_hostname();
  zCwd = file_getcwd(0,0);
  zHome = fossil_getenv("HOME");
  if( zHome ){
    nHome = (int)strlen(zHome);
    if( strncmp(zCwd, zHome, nHome)==0 && zCwd[nHome]=='/' ){
      zCwd = mprintf("~%s", zCwd+nHome);
    }
  }else{
    nHome = 0;
  }
  if( zHostname ){
    style_header("Checkout Status: %h on %h", zCwd, zHostname);
  }else{
    style_header("Checkout Status: %h", zCwd);
  }
  render_checkin_context(vid, 0, 0, 0);
  @ <hr>
  zExBase = P("exbase");
  if( zExBase && zExBase[0] ){
    char *zCBase = file_canonical_name_dup(zExBase);
    if( nHome && strncmp(zCBase, zHome, nHome)==0 && zCBase[nHome]=='/' ){
      @ <p>Using external baseline: ~%h(zCBase+nHome)</p>
    }else{
      @ <p>Using external baseline: %h(zCBase)</p>
    }
    ckout_external_base_diff(vid, zCBase);
    fossil_free(zCBase);
  }else{
    ckout_normal_diff(vid);
  }
  style_finish_page();
}

/*
** WEBPAGE: vinfo
** WEBPAGE: ci
** URL:  /ci/ARTIFACTID
2121
2122
2123
2124
2125
2126
2127






2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142

2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162

























2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195

/*
** WEBPAGE: jchunk hidden
** URL: /jchunk/HASH?from=N&to=M
**
** Return lines of text from a file as a JSON array - one entry in the
** array for each line of text.






**
** **Warning:**  This is an internal-use-only interface that is subject to
** change at any moment.  External application should not use this interface
** since the application will break when this interface changes, and this
** interface will undoubtedly change.
**
** This page is intended to be used in an XHR from javascript on a
** diff page, to return unseen context to fill in additional context
** when the user clicks on the appropriate button. The response is
** always in JSON form and errors are reported as documented for
** ajax_route_error().
*/
void jchunk_page(void){
  int rid = 0;
  const char *zName = PD("name", "");

  int iFrom = atoi(PD("from","0"));
  int iTo = atoi(PD("to","0"));
  int ln;
  int go = 1;
  const char *zSep;
  Blob content;
  Blob line;
  Blob *pOut;

  if(0){
    ajax_route_error(400, "Just testing client-side error handling.");
    return;
  }

  login_check_credentials();
  cgi_check_for_malice();
  if( !g.perm.Read ){
    ajax_route_error(403, "Access requires Read permissions.");
    return;
  }

























#if 1
  /* Re-enable this block once this code is integrated somewhere into
     the UI. */
  rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName);
  if( rid==0 ){
    ajax_route_error(404, "Unknown artifact: %h", zName);
    return;
  }
#else
  /* This impl is only to simplify "manual" testing via the JS
     console. */
  rid = symbolic_name_to_rid(zName, "*");
  if( rid==0 ){
    ajax_route_error(404, "Unknown artifact: %h", zName);
    return;
  }else if( rid<0 ){
    ajax_route_error(418, "Ambiguous artifact name: %h", zName);
    return;
  }
#endif
  if( iFrom<1 || iTo<iFrom ){
    ajax_route_error(500, "Invalid line range from=%d, to=%d.",
                     iFrom, iTo);
    return;
  }
  content_get(rid, &content);
  g.isConst = 1;
  cgi_set_content_type("application/json");
  ln = 0;
  while( go && ln<iFrom ){
    go = blob_line(&content, &line);
    ln++;
  }







>
>
>
>
>
>















>




















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

|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|

<
<
<
|

<







2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334



2335
2336

2337
2338
2339
2340
2341
2342
2343

/*
** WEBPAGE: jchunk hidden
** URL: /jchunk/HASH?from=N&to=M
**
** Return lines of text from a file as a JSON array - one entry in the
** array for each line of text.
**
** The HASH is normally a sha1 or sha3 hash that identifies an artifact
** in the BLOB table of the database.  However, if HASH starts with an "x"
** and is followed by valid hexadecimal, and if we are running in a
** "fossil ui" situation (locally and with privilege), then decode the hex
** into a filename and read the file content from that name.
**
** **Warning:**  This is an internal-use-only interface that is subject to
** change at any moment.  External application should not use this interface
** since the application will break when this interface changes, and this
** interface will undoubtedly change.
**
** This page is intended to be used in an XHR from javascript on a
** diff page, to return unseen context to fill in additional context
** when the user clicks on the appropriate button. The response is
** always in JSON form and errors are reported as documented for
** ajax_route_error().
*/
void jchunk_page(void){
  int rid = 0;
  const char *zName = PD("name", "");
  int nName = (int)(strlen(zName)&0x7fffffff);
  int iFrom = atoi(PD("from","0"));
  int iTo = atoi(PD("to","0"));
  int ln;
  int go = 1;
  const char *zSep;
  Blob content;
  Blob line;
  Blob *pOut;

  if(0){
    ajax_route_error(400, "Just testing client-side error handling.");
    return;
  }

  login_check_credentials();
  cgi_check_for_malice();
  if( !g.perm.Read ){
    ajax_route_error(403, "Access requires Read permissions.");
    return;
  }
  if( iFrom<1 || iTo<iFrom ){
    ajax_route_error(500, "Invalid line range from=%d, to=%d.",
                     iFrom, iTo);
    return;
  }
  if( zName[0]=='x'
   && ((nName-1)&1)==0
   && validate16(&zName[1],nName-1)
   && g.perm.Admin
   && db_open_local(0)
   && cgi_is_loopback(g.zIpAddr)
  ){
    /* Treat the HASH as a hex-encoded filename */
    int n = (nName-1)/2;
    char *zFN = fossil_malloc(n+1);
    decode16((const u8*)&zName[1], (u8*)zFN, nName-1);
    zFN[n] = 0;
    if( file_size(zFN, ExtFILE)<0 ){
      blob_zero(&content);
    }else{
      blob_read_from_file(&content, zFN, ExtFILE);
    }
    fossil_free(zFN);
  }else{
    /* Treat the HASH as an artifact hash matching BLOB.UUID */
#if 1
    /* Re-enable this block once this code is integrated somewhere into
       the UI. */
    rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName);
    if( rid==0 ){
      ajax_route_error(404, "Unknown artifact: %h", zName);
      return;
    }
#else
    /* This impl is only to simplify "manual" testing via the JS
       console. */
    rid = symbolic_name_to_rid(zName, "*");
    if( rid==0 ){
      ajax_route_error(404, "Unknown artifact: %h", zName);
      return;
    }else if( rid<0 ){
      ajax_route_error(418, "Ambiguous artifact name: %h", zName);
      return;
    }
#endif



    content_get(rid, &content);
  }

  g.isConst = 1;
  cgi_set_content_type("application/json");
  ln = 0;
  while( go && ln<iFrom ){
    go = blob_line(&content, &line);
    ln++;
  }