Fossil SCM

fossil-scm / src / diffcmd.c
Source Blame History 1533 lines
dbda8d6… drh 1 /*
c19f34c… drh 2 ** Copyright (c) 2007 D. Richard Hipp
dbda8d6… drh 3 **
dbda8d6… drh 4 ** This program is free software; you can redistribute it and/or
c06edd2… drh 5 ** modify it under the terms of the Simplified BSD License (also
c06edd2… drh 6 ** known as the "2-Clause License" or "FreeBSD License".)
c06edd2… drh 7
dbda8d6… drh 8 ** This program is distributed in the hope that it will be useful,
c06edd2… drh 9 ** but without any warranty; without even the implied warranty of
c06edd2… drh 10 ** merchantability or fitness for a particular purpose.
dbda8d6… drh 11 **
dbda8d6… drh 12 ** Author contact information:
dbda8d6… drh 13 ** [email protected]
dbda8d6… drh 14 ** http://www.hwaci.com/drh/
dbda8d6… drh 15 **
dbda8d6… drh 16 *******************************************************************************
dbda8d6… drh 17 **
dbda8d6… drh 18 ** This file contains code used to implement the "diff" command
dbda8d6… drh 19 */
dbda8d6… drh 20 #include "config.h"
dbda8d6… drh 21 #include "diffcmd.h"
dbda8d6… drh 22 #include <assert.h>
dbda8d6… drh 23
00c8622… drh 24 /* includes needed to catch interrupts */
00c8622… drh 25 #ifdef _WIN32
00c8622… drh 26 # include <windows.h>
00c8622… drh 27 #else
f3961f4… drh 28 # include <signal.h>
f3961f4… drh 29 #endif
f3961f4… drh 30
135ed93… drh 31 /*
135ed93… drh 32 ** Use the right null device for the platform.
135ed93… drh 33 */
135ed93… drh 34 #if defined(_WIN32)
135ed93… drh 35 # define NULL_DEVICE "NUL"
135ed93… drh 36 #else
135ed93… drh 37 # define NULL_DEVICE "/dev/null"
135ed93… drh 38 #endif
135ed93… drh 39
135ed93… drh 40 /*
7807ec4… mistachkin 41 ** Used when the name for the diff is unknown.
7807ec4… mistachkin 42 */
7807ec4… mistachkin 43 #define DIFF_NO_NAME "(unknown)"
7807ec4… mistachkin 44
7807ec4… mistachkin 45 /*
825d78b… mistachkin 46 ** Use the "exec-rel-paths" setting and the --exec-abs-paths and
825d78b… mistachkin 47 ** --exec-rel-paths command line options to determine whether
825d78b… mistachkin 48 ** certain external commands are executed using relative paths.
825d78b… mistachkin 49 */
c46f980… drh 50 static int determine_exec_relative_option(int force){
825d78b… mistachkin 51 static int relativePaths = -1;
825d78b… mistachkin 52 if( force || relativePaths==-1 ){
825d78b… mistachkin 53 int relPathOption = find_option("exec-rel-paths", 0, 0)!=0;
825d78b… mistachkin 54 int absPathOption = find_option("exec-abs-paths", 0, 0)!=0;
825d78b… mistachkin 55 #if defined(FOSSIL_ENABLE_EXEC_REL_PATHS)
825d78b… mistachkin 56 relativePaths = db_get_boolean("exec-rel-paths", 1);
825d78b… mistachkin 57 #else
825d78b… mistachkin 58 relativePaths = db_get_boolean("exec-rel-paths", 0);
825d78b… mistachkin 59 #endif
825d78b… mistachkin 60 if( relPathOption ){ relativePaths = 1; }
825d78b… mistachkin 61 if( absPathOption ){ relativePaths = 0; }
825d78b… mistachkin 62 }
825d78b… mistachkin 63 return relativePaths;
825d78b… mistachkin 64 }
825d78b… mistachkin 65
c46f980… drh 66 #if INTERFACE
c46f980… drh 67 /*
c46f980… drh 68 ** An array of FileDirList objects describe the files and directories listed
c46f980… drh 69 ** on the command line of a "diff" command. Only those objects listed are
c46f980… drh 70 ** actually diffed.
c46f980… drh 71 */
c46f980… drh 72 struct FileDirList {
c46f980… drh 73 int nUsed; /* Number of times each entry is used */
c46f980… drh 74 int nName; /* Length of the entry */
c46f980… drh 75 char *zName; /* Text of the entry */
c46f980… drh 76 };
c46f980… drh 77 #endif
c46f980… drh 78
c46f980… drh 79 /*
c46f980… drh 80 ** Return true if zFile is a file named on the azInclude[] list or is
c46f980… drh 81 ** a file in a directory named on the azInclude[] list.
c46f980… drh 82 **
c46f980… drh 83 ** if azInclude is NULL, then always include zFile.
c46f980… drh 84 */
c46f980… drh 85 static int file_dir_match(FileDirList *p, const char *zFile){
c46f980… drh 86 if( p==0 || strcmp(p->zName,".")==0 ) return 1;
c46f980… drh 87 if( filenames_are_case_sensitive() ){
c46f980… drh 88 while( p->zName ){
c46f980… drh 89 if( strcmp(zFile, p->zName)==0
c46f980… drh 90 || (strncmp(zFile, p->zName, p->nName)==0
c46f980… drh 91 && zFile[p->nName]=='/')
c46f980… drh 92 ){
c46f980… drh 93 break;
c46f980… drh 94 }
c46f980… drh 95 p++;
c46f980… drh 96 }
c46f980… drh 97 }else{
c46f980… drh 98 while( p->zName ){
c46f980… drh 99 if( fossil_stricmp(zFile, p->zName)==0
c46f980… drh 100 || (fossil_strnicmp(zFile, p->zName, p->nName)==0
c46f980… drh 101 && zFile[p->nName]=='/')
c46f980… drh 102 ){
c46f980… drh 103 break;
c46f980… drh 104 }
c46f980… drh 105 p++;
c46f980… drh 106 }
c46f980… drh 107 }
c46f980… drh 108 if( p->zName ){
c46f980… drh 109 p->nUsed++;
c46f980… drh 110 return 1;
c46f980… drh 111 }
c46f980… drh 112 return 0;
c46f980… drh 113 }
c46f980… drh 114
c46f980… drh 115 /*
41f6a45… danield 116 ** Print details about the compared versions - possibly the working directory
41f6a45… danield 117 ** or the undo buffer. For check-ins, show hash and commit time.
275da70… danield 118 **
41f6a45… danield 119 ** This is intended primarily to go into the "header garbage" that is ignored
41f6a45… danield 120 ** by patch(1).
41f6a45… danield 121 **
41f6a45… danield 122 ** zFrom and zTo are interpreted as symbolic version names, unless they
41f6a45… danield 123 ** start with '(', in which case they are printed directly.
41f6a45… danield 124 */
41f6a45… danield 125 void diff_print_versions(const char *zFrom, const char *zTo, DiffConfig *pCfg){
41f6a45… danield 126 if( (pCfg->diffFlags & (DIFF_SIDEBYSIDE|DIFF_BRIEF|DIFF_NUMSTAT|
41f6a45… danield 127 DIFF_HTML|DIFF_WEBPAGE|DIFF_BROWSER|DIFF_JSON|DIFF_TCL))==0 ){
41f6a45… danield 128 fossil_print("Fossil-Diff-From: %s\n",
41f6a45… danield 129 zFrom[0]=='(' ? zFrom : mprintf("%S %s",
41f6a45… danield 130 rid_to_uuid(symbolic_name_to_rid(zFrom, "ci")),
41f6a45… danield 131 db_text("","SELECT datetime(%f)||' UTC'",
633ea73… drh 132 symbolic_name_to_mtime(zFrom, 0, 0))));
41f6a45… danield 133 fossil_print("Fossil-Diff-To: %s\n",
41f6a45… danield 134 zTo[0]=='(' ? zTo : mprintf("%S %s",
41f6a45… danield 135 rid_to_uuid(symbolic_name_to_rid(zTo, "ci")),
41f6a45… danield 136 db_text("","SELECT datetime(%f)||' UTC'",
633ea73… drh 137 symbolic_name_to_mtime(zTo, 0, 1))));
41f6a45… danield 138 fossil_print("%.66c\n", '-');
41f6a45… danield 139 }
41f6a45… danield 140 }
41f6a45… danield 141
41f6a45… danield 142 /*
ab47cc7… drh 143 ** Print the "Index:" message that patches wants to see at the top of a diff.
ab47cc7… drh 144 */
590e01d… drh 145 void diff_print_index(const char *zFile, DiffConfig *pCfg, Blob *pOut){
1347a1d… drh 146 if( (pCfg->diffFlags &
1347a1d… drh 147 (DIFF_SIDEBYSIDE|DIFF_BRIEF|DIFF_NUMSTAT|DIFF_JSON|
1347a1d… drh 148 DIFF_WEBPAGE|DIFF_TCL))==0
1347a1d… drh 149 ){
590e01d… drh 150 blob_appendf(pOut, "Index: %s\n%.66c\n", zFile, '=');
ab47cc7… drh 151 }
ab47cc7… drh 152 }
ab47cc7… drh 153
ab47cc7… drh 154 /*
1347a1d… drh 155 ** Print the +++/--- filename lines or whatever filename information
1347a1d… drh 156 ** is appropriate for the output format.
41f6a45… danield 157 **
ab47cc7… drh 158 */
9e33074… drh 159 void diff_print_filenames(
1347a1d… drh 160 const char *zLeft, /* Name of the left file */
1347a1d… drh 161 const char *zRight, /* Name of the right file */
1347a1d… drh 162 DiffConfig *pCfg, /* Diff configuration */
590e01d… drh 163 Blob *pOut /* Write to this blob, or stdout of this is NULL */
9e33074… drh 164 ){
1347a1d… drh 165 u64 diffFlags = pCfg->diffFlags;
caa6ad3… drh 166 /* Standardize on /dev/null, regardless of platform. */
caa6ad3… drh 167 if( pCfg->diffFlags & DIFF_FILE_ADDED ) zLeft = "/dev/null";
caa6ad3… drh 168 if( pCfg->diffFlags & DIFF_FILE_DELETED ) zRight = "/dev/null";
f5cb12d… drh 169 if( pCfg->azLabel[0] ) zLeft = pCfg->azLabel[0];
f5cb12d… drh 170 if( pCfg->azLabel[1] ) zRight = pCfg->azLabel[1];
1347a1d… drh 171 if( diffFlags & (DIFF_BRIEF|DIFF_RAW) ){
e0565d4… drh 172 /* no-op */
9e33074… drh 173 }else if( diffFlags & DIFF_DEBUG ){
590e01d… drh 174 blob_appendf(pOut, "FILE-LEFT %s\nFILE-RIGHT %s\n", zLeft, zRight);
ea52b7d… drh 175 }else if( diffFlags & DIFF_WEBPAGE ){
ea52b7d… drh 176 if( fossil_strcmp(zLeft,zRight)==0 ){
590e01d… drh 177 blob_appendf(pOut,"<h1>%h</h1>\n", zLeft);
590e01d… drh 178 }else{
590e01d… drh 179 blob_appendf(pOut,"<h1>%h &lrarr; %h</h1>\n", zLeft, zRight);
590e01d… drh 180 }
590e01d… drh 181 }else if( diffFlags & (DIFF_TCL|DIFF_JSON) ){
1347a1d… drh 182 if( diffFlags & DIFF_TCL ){
1347a1d… drh 183 blob_append(pOut, "FILE ", 5);
1347a1d… drh 184 blob_append_tcl_literal(pOut, zLeft, (int)strlen(zLeft));
1347a1d… drh 185 blob_append_char(pOut, ' ');
1347a1d… drh 186 blob_append_tcl_literal(pOut, zRight, (int)strlen(zRight));
1347a1d… drh 187 blob_append_char(pOut, '\n');
1347a1d… drh 188 }else{
590e01d… drh 189 if( pOut ) blob_trim(pOut);
1347a1d… drh 190 blob_append(pOut, (pCfg->nFile==0 ? "[{" : ",\n{"), -1);
1347a1d… drh 191 pCfg->nFile++;
1347a1d… drh 192 blob_append(pOut, "\n \"leftname\":", -1);
1347a1d… drh 193 blob_append_json_literal(pOut, zLeft, (int)strlen(zLeft));
1347a1d… drh 194 blob_append(pOut, ",\n \"rightname\":", -1);
1347a1d… drh 195 blob_append_json_literal(pOut, zRight, (int)strlen(zRight));
1347a1d… drh 196 blob_append(pOut, ",\n \"diff\":\n", -1);
1347a1d… drh 197 }
e0565d4… drh 198 }else if( diffFlags & DIFF_SIDEBYSIDE ){
1347a1d… drh 199 int w = diff_width(pCfg);
ab47cc7… drh 200 int n1 = strlen(zLeft);
1e0e075… drh 201 int n2 = strlen(zRight);
fade055… mistachkin 202 int x;
1e0e075… drh 203 if( n1==n2 && fossil_strcmp(zLeft,zRight)==0 ){
1e0e075… drh 204 if( n1>w*2 ) n1 = w*2;
1e0e075… drh 205 x = w*2+17 - (n1+2);
590e01d… drh 206 blob_appendf(pOut, "%.*c %.*s %.*c\n",
590e01d… drh 207 x/2, '=', n1, zLeft, (x+1)/2, '=');
1e0e075… drh 208 }else{
1e0e075… drh 209 if( w<20 ) w = 20;
1e0e075… drh 210 if( n1>w-10 ) n1 = w - 10;
1e0e075… drh 211 if( n2>w-10 ) n2 = w - 10;
590e01d… drh 212 blob_appendf(pOut, "%.*c %.*s %.*c versus %.*c %.*s %.*c\n",
590e01d… drh 213 (w-n1+10)/2, '=', n1, zLeft, (w-n1+1)/2, '=',
590e01d… drh 214 (w-n2)/2, '=', n2, zRight, (w-n2+1)/2, '=');
590e01d… drh 215 }
590e01d… drh 216 }else{
c6715ca… andybradford 217 blob_appendf(pOut, "--- %s\t\n+++ %s\t\n", zLeft, zRight);
590e01d… drh 218 }
590e01d… drh 219 }
590e01d… drh 220
590e01d… drh 221
590e01d… drh 222 /*
5a8516d… drh 223 ** Default header texts for diff with --webpage
590e01d… drh 224 */
275da70… danield 225 static const char zWebpageHdr[] =
9e33074… drh 226 @ <!DOCTYPE html>
9e33074… drh 227 @ <html>
9e33074… drh 228 @ <head>
9e33074… drh 229 @ <meta charset="UTF-8">
9e33074… drh 230 @ <style>
432ff8d… stephan 231 @ body {
432ff8d… stephan 232 @ background-color: white;
432ff8d… stephan 233 @ }
9e33074… drh 234 @ h1 {
9e33074… drh 235 @ font-size: 150%;
9e33074… drh 236 @ }
9e33074… drh 237 @
9e33074… drh 238 @ table.diff {
51c1efd… stephan 239 @ width: 100%;
eb6611c… drh 240 @ border-spacing: 0;
9e33074… drh 241 @ border: 1px solid black;
432ff8d… stephan 242 @ line-height: inherit;
432ff8d… stephan 243 @ font-size: inherit;
eb6611c… drh 244 @ }
9e33074… drh 245 @ table.diff td {
eb6611c… drh 246 @ vertical-align: top;
432ff8d… stephan 247 @ line-height: inherit;
432ff8d… stephan 248 @ font-size: inherit;
9e33074… drh 249 @ }
9e33074… drh 250 @ table.diff pre {
9e33074… drh 251 @ margin: 0 0 0 0;
432ff8d… stephan 252 @ line-height: inherit;
432ff8d… stephan 253 @ font-size: inherit;
9e33074… drh 254 @ }
9e33074… drh 255 @ td.diffln {
8c6dddc… stephan 256 @ width: fit-content;
eb6611c… drh 257 @ text-align: right;
9e33074… drh 258 @ padding: 0 1em 0 0;
9e33074… drh 259 @ }
9e33074… drh 260 @ td.difflne {
9e33074… drh 261 @ padding-bottom: 0.4em;
9e33074… drh 262 @ }
9e33074… drh 263 @ td.diffsep {
8c6dddc… stephan 264 @ width: fit-content;
9e33074… drh 265 @ padding: 0 0.3em 0 1em;
432ff8d… stephan 266 @ line-height: inherit;
432ff8d… stephan 267 @ font-size: inherit;
432ff8d… stephan 268 @ }
432ff8d… stephan 269 @ td.diffsep pre {
432ff8d… stephan 270 @ line-height: inherit;
432ff8d… stephan 271 @ font-size: inherit;
eb6611c… drh 272 @ }
9e33074… drh 273 @ td.difftxt pre {
eb6611c… drh 274 @ overflow-x: auto;
eb6611c… drh 275 @ }
9e33074… drh 276 @ td.diffln ins {
9e33074… drh 277 @ background-color: #a0e4b2;
9e33074… drh 278 @ text-decoration: none;
432ff8d… stephan 279 @ line-height: inherit;
432ff8d… stephan 280 @ font-size: inherit;
9e33074… drh 281 @ }
9e33074… drh 282 @ td.diffln del {
9e33074… drh 283 @ background-color: #ffc0c0;
9e33074… drh 284 @ text-decoration: none;
432ff8d… stephan 285 @ line-height: inherit;
432ff8d… stephan 286 @ font-size: inherit;
9e33074… drh 287 @ }
9e33074… drh 288 @ td.difftxt del {
9e33074… drh 289 @ background-color: #ffe8e8;
9e33074… drh 290 @ text-decoration: none;
432ff8d… stephan 291 @ line-height: inherit;
432ff8d… stephan 292 @ font-size: inherit;
9e33074… drh 293 @ }
9e33074… drh 294 @ td.difftxt del > del {
9e33074… drh 295 @ background-color: #ffc0c0;
9e33074… drh 296 @ text-decoration: none;
9e33074… drh 297 @ font-weight: bold;
9e33074… drh 298 @ }
9e33074… drh 299 @ td.difftxt del > del.edit {
9e33074… drh 300 @ background-color: #c0c0ff;
9e33074… drh 301 @ text-decoration: none;
9e33074… drh 302 @ font-weight: bold;
9e33074… drh 303 @ }
9e33074… drh 304 @ td.difftxt ins {
9e33074… drh 305 @ background-color: #dafbe1;
9e33074… drh 306 @ text-decoration: none;
432ff8d… stephan 307 @ line-height: inherit;
432ff8d… stephan 308 @ font-size: inherit;
9e33074… drh 309 @ }
9e33074… drh 310 @ td.difftxt ins > ins {
9e33074… drh 311 @ background-color: #a0e4b2;
9e33074… drh 312 @ text-decoration: none;
9e33074… drh 313 @ font-weight: bold;
9e33074… drh 314 @ }
9e33074… drh 315 @ td.difftxt ins > ins.edit {
9e33074… drh 316 @ background-color: #c0c0ff;
9e33074… drh 317 @ text-decoration: none;
9e33074… drh 318 @ font-weight: bold;
9e33074… drh 319 @ }
a2e6b31… danield 320 @ @media (prefers-color-scheme: dark) {
a2e6b31… danield 321 @ body {
a2e6b31… danield 322 @ background-color: #353535;
a2e6b31… danield 323 @ color: #ffffff;
a2e6b31… danield 324 @ }
a2e6b31… danield 325 @ td.diffln ins {
a2e6b31… danield 326 @ background-color: #559855;
a2e6b31… danield 327 @ color: #000000;
a2e6b31… danield 328 @ }
a2e6b31… danield 329 @ td.diffln del {
a2e6b31… danield 330 @ background-color: #cc5555;
a2e6b31… danield 331 @ color: #000000;
a2e6b31… danield 332 @ }
a2e6b31… danield 333 @ td.difftxt del {
a2e6b31… danield 334 @ background-color: #f9cfcf;
a2e6b31… danield 335 @ color: #000000;
a2e6b31… danield 336 @ }
a2e6b31… danield 337 @ td.difftxt del > del {
a2e6b31… danield 338 @ background-color: #cc5555;
a2e6b31… danield 339 @ color: #000000;
a2e6b31… danield 340 @ }
a2e6b31… danield 341 @ td.difftxt ins {
a2e6b31… danield 342 @ background-color: #a2dbb2;
a2e6b31… danield 343 @ color: #000000;
a2e6b31… danield 344 @ }
a2e6b31… danield 345 @ td.difftxt ins > ins {
a2e6b31… danield 346 @ background-color: #559855;
a2e6b31… danield 347 @ }
a2e6b31… danield 348 @ }
275da70… danield 349 @
5a8516d… drh 350 @ </style>
5a8516d… drh 351 @ </head>
5a8516d… drh 352 @ <body>
5a8516d… drh 353 ;
275da70… danield 354 static const char zWebpageHdrDark[] =
5a8516d… drh 355 @ <!DOCTYPE html>
5a8516d… drh 356 @ <html>
5a8516d… drh 357 @ <head>
5a8516d… drh 358 @ <meta charset="UTF-8">
5a8516d… drh 359 @ <style>
5a8516d… drh 360 @ body {
5a8516d… drh 361 @ background-color: #353535;
5a8516d… drh 362 @ color: #ffffff;
5a8516d… drh 363 @ }
5a8516d… drh 364 @ h1 {
5a8516d… drh 365 @ font-size: 150%;
5a8516d… drh 366 @ }
275da70… danield 367 @
5a8516d… drh 368 @ table.diff {
5a8516d… drh 369 @ width: 100%;
5a8516d… drh 370 @ border-spacing: 0;
5a8516d… drh 371 @ border: 1px solid black;
5a8516d… drh 372 @ line-height: inherit;
5a8516d… drh 373 @ font-size: inherit;
5a8516d… drh 374 @ }
5a8516d… drh 375 @ table.diff td {
5a8516d… drh 376 @ vertical-align: top;
5a8516d… drh 377 @ line-height: inherit;
5a8516d… drh 378 @ font-size: inherit;
5a8516d… drh 379 @ }
5a8516d… drh 380 @ table.diff pre {
5a8516d… drh 381 @ margin: 0 0 0 0;
5a8516d… drh 382 @ line-height: inherit;
5a8516d… drh 383 @ font-size: inherit;
5a8516d… drh 384 @ }
5a8516d… drh 385 @ td.diffln {
8c6dddc… stephan 386 @ width: fit-content;
5a8516d… drh 387 @ text-align: right;
5a8516d… drh 388 @ padding: 0 1em 0 0;
5a8516d… drh 389 @ }
5a8516d… drh 390 @ td.difflne {
5a8516d… drh 391 @ padding-bottom: 0.4em;
5a8516d… drh 392 @ }
5a8516d… drh 393 @ td.diffsep {
8c6dddc… stephan 394 @ width: fit-content;
5a8516d… drh 395 @ padding: 0 0.3em 0 1em;
5a8516d… drh 396 @ line-height: inherit;
5a8516d… drh 397 @ font-size: inherit;
5a8516d… drh 398 @ }
5a8516d… drh 399 @ td.diffsep pre {
5a8516d… drh 400 @ line-height: inherit;
5a8516d… drh 401 @ font-size: inherit;
5a8516d… drh 402 @ }
5a8516d… drh 403 @ td.difftxt pre {
5a8516d… drh 404 @ overflow-x: auto;
5a8516d… drh 405 @ }
5a8516d… drh 406 @ td.diffln ins {
5a8516d… drh 407 @ background-color: #559855;
5a8516d… drh 408 @ color: #000000;
5a8516d… drh 409 @ text-decoration: none;
5a8516d… drh 410 @ line-height: inherit;
5a8516d… drh 411 @ font-size: inherit;
5a8516d… drh 412 @ }
5a8516d… drh 413 @ td.diffln del {
5a8516d… drh 414 @ background-color: #cc5555;
5a8516d… drh 415 @ color: #000000;
5a8516d… drh 416 @ text-decoration: none;
5a8516d… drh 417 @ line-height: inherit;
5a8516d… drh 418 @ font-size: inherit;
5a8516d… drh 419 @ }
5a8516d… drh 420 @ td.difftxt del {
5a8516d… drh 421 @ background-color: #f9cfcf;
5a8516d… drh 422 @ color: #000000;
5a8516d… drh 423 @ text-decoration: none;
5a8516d… drh 424 @ line-height: inherit;
5a8516d… drh 425 @ font-size: inherit;
5a8516d… drh 426 @ }
5a8516d… drh 427 @ td.difftxt del > del {
5a8516d… drh 428 @ background-color: #cc5555;
5a8516d… drh 429 @ color: #000000;
5a8516d… drh 430 @ text-decoration: none;
5a8516d… drh 431 @ font-weight: bold;
5a8516d… drh 432 @ }
5a8516d… drh 433 @ td.difftxt del > del.edit {
5a8516d… drh 434 @ background-color: #c0c0ff;
5a8516d… drh 435 @ text-decoration: none;
5a8516d… drh 436 @ font-weight: bold;
5a8516d… drh 437 @ }
5a8516d… drh 438 @ td.difftxt ins {
5a8516d… drh 439 @ background-color: #a2dbb2;
5a8516d… drh 440 @ color: #000000;
5a8516d… drh 441 @ text-decoration: none;
5a8516d… drh 442 @ line-height: inherit;
5a8516d… drh 443 @ font-size: inherit;
5a8516d… drh 444 @ }
5a8516d… drh 445 @ td.difftxt ins > ins {
5a8516d… drh 446 @ background-color: #559855;
5a8516d… drh 447 @ text-decoration: none;
5a8516d… drh 448 @ font-weight: bold;
5a8516d… drh 449 @ }
5a8516d… drh 450 @ td.difftxt ins > ins.edit {
5a8516d… drh 451 @ background-color: #c0c0ff;
5a8516d… drh 452 @ text-decoration: none;
5a8516d… drh 453 @ font-weight: bold;
5a8516d… drh 454 @ }
275da70… danield 455 @
ea52b7d… drh 456 @ </style>
ea52b7d… drh 457 @ </head>
ea52b7d… drh 458 @ <body>
ea52b7d… drh 459 ;
275da70… danield 460 const char zWebpageEnd[] =
7e37ae9… drh 461 @ </body>
7e37ae9… drh 462 @ </html>
7e37ae9… drh 463 ;
7e37ae9… drh 464
7e37ae9… drh 465 /*
1347a1d… drh 466 ** State variables used by the --browser option for diff. These must
1347a1d… drh 467 ** be static variables, not elements of DiffConfig, since they are
1347a1d… drh 468 ** used by the interrupt handler.
f3961f4… drh 469 */
f3961f4… drh 470 static char *tempDiffFilename; /* File holding the diff HTML */
f3961f4… drh 471 static FILE *diffOut; /* Open to write into tempDiffFilename */
f3961f4… drh 472
f3961f4… drh 473 /* Amount of delay (in milliseconds) between launching the
9a3372e… drh 474 ** web browser and deleting the temporary file used by --browser
f3961f4… drh 475 */
9a3372e… drh 476 #ifndef FOSSIL_BROWSER_DIFF_DELAY
9a3372e… drh 477 # define FOSSIL_BROWSER_DIFF_DELAY 5000 /* 5 seconds by default */
f3961f4… drh 478 #endif
f3961f4… drh 479
f3961f4… drh 480 /*
9a3372e… drh 481 ** If we catch a single while writing the temporary file for the --browser
f3961f4… drh 482 ** diff output, then delete the temporary file and exit.
f3961f4… drh 483 */
f3961f4… drh 484 static void diff_www_interrupt(int NotUsed){
f3961f4… drh 485 (void)NotUsed;
f3961f4… drh 486 if( diffOut ) fclose(diffOut);
f3961f4… drh 487 if( tempDiffFilename ) file_delete(tempDiffFilename);
f3961f4… drh 488 exit(1);
f3961f4… drh 489 }
f3961f4… drh 490 #ifdef _WIN32
f3961f4… drh 491 static BOOL WINAPI diff_console_ctrl_handler(DWORD dwCtrlType){
f3961f4… drh 492 if( dwCtrlType==CTRL_C_EVENT ) diff_www_interrupt(0);
f3961f4… drh 493 return FALSE;
f3961f4… drh 494 }
f3961f4… drh 495 #endif
f3961f4… drh 496
f3961f4… drh 497
f3961f4… drh 498 /*
f3961f4… drh 499 ** Do preliminary setup and output before computing a diff.
f3961f4… drh 500 **
9a3372e… drh 501 ** For --browser, redirect stdout to a temporary file that will
f3961f4… drh 502 ** hold the result. Make arrangements to delete that temporary
f3961f4… drh 503 ** file if the diff is interrupted.
f3961f4… drh 504 **
9a3372e… drh 505 ** For --browser and --webpage, output the HTML header.
eb6611c… drh 506 */
1347a1d… drh 507 void diff_begin(DiffConfig *pCfg){
1347a1d… drh 508 if( (pCfg->diffFlags & DIFF_BROWSER)!=0 ){
f3961f4… drh 509 tempDiffFilename = fossil_temp_filename();
f3961f4… drh 510 tempDiffFilename = sqlite3_mprintf("%z.html", tempDiffFilename);
f48e48f… drh 511 diffOut = fossil_freopen(tempDiffFilename,"wb",stdout);
f3961f4… drh 512 if( diffOut==0 ){
16d3d8b… stephan 513 fossil_fatal("unable to create temporary file \"%s\"",
f3961f4… drh 514 tempDiffFilename);
f3961f4… drh 515 }
f3961f4… drh 516 #ifndef _WIN32
f3961f4… drh 517 signal(SIGINT, diff_www_interrupt);
f3961f4… drh 518 #else
f3961f4… drh 519 SetConsoleCtrlHandler(diff_console_ctrl_handler, TRUE);
f3961f4… drh 520 #endif
f3961f4… drh 521 }
1347a1d… drh 522 if( (pCfg->diffFlags & DIFF_WEBPAGE)!=0 ){
275da70… danield 523 fossil_print("%s",(pCfg->diffFlags & DIFF_DARKMODE)!=0 ? zWebpageHdrDark :
5a8516d… drh 524 zWebpageHdr);
83feccc… drh 525 fflush(stdout);
eb6611c… drh 526 }
eb6611c… drh 527 }
eb6611c… drh 528
eb6611c… drh 529 /* Do any final output required by a diff and complete the diff
eb6611c… drh 530 ** process.
f3961f4… drh 531 **
275da70… danield 532 ** For --browser and --webpage, output any javascript required by
f3961f4… drh 533 ** the diff. (Currently JS is only needed for side-by-side diffs).
f3961f4… drh 534 **
9a3372e… drh 535 ** For --browser, close the connection to the temporary file, then
f3961f4… drh 536 ** launch a web browser to view the file. After a delay
9a3372e… drh 537 ** of FOSSIL_BROWSER_DIFF_DELAY milliseconds, delete the temp file.
eb6611c… drh 538 */
1347a1d… drh 539 void diff_end(DiffConfig *pCfg, int nErr){
1347a1d… drh 540 if( (pCfg->diffFlags & DIFF_WEBPAGE)!=0 ){
1347a1d… drh 541 if( pCfg->diffFlags & DIFF_SIDEBYSIDE ){
9e33074… drh 542 const unsigned char *zJs = builtin_file("diff.js", 0);
eb6611c… drh 543 fossil_print("<script>\n%s</script>\n", zJs);
eb6611c… drh 544 }
eb6611c… drh 545 fossil_print("%s", zWebpageEnd);
f3961f4… drh 546 }
1347a1d… drh 547 if( (pCfg->diffFlags & DIFF_BROWSER)!=0 && nErr==0 ){
c72c6df… drh 548 char *zCmd = mprintf("%s %$", fossil_web_browser(), tempDiffFilename);
f3961f4… drh 549 fclose(diffOut);
f48e48f… drh 550 diffOut = fossil_freopen(NULL_DEVICE, "wb", stdout);
f3961f4… drh 551 fossil_system(zCmd);
f3961f4… drh 552 fossil_free(zCmd);
4291b9c… drh 553 diffOut = 0;
9a3372e… drh 554 sqlite3_sleep(FOSSIL_BROWSER_DIFF_DELAY);
f3961f4… drh 555 file_delete(tempDiffFilename);
f3961f4… drh 556 sqlite3_free(tempDiffFilename);
f3961f4… drh 557 tempDiffFilename = 0;
f48e48f… drh 558 }
1347a1d… drh 559 if( (pCfg->diffFlags & DIFF_JSON)!=0 && pCfg->nFile>0 ){
1347a1d… drh 560 fossil_print("]\n");
1347a1d… drh 561 }
12a2a5e… drh 562 }
a51808c… drh 563
a51808c… drh 564 /*
a51808c… drh 565 ** Show the difference between two files, one in memory and one on disk.
a51808c… drh 566 **
a51808c… drh 567 ** The difference is the set of edits needed to transform pFile1 into
a51808c… drh 568 ** zFile2. The content of pFile1 is in memory. zFile2 exists on disk.
a51808c… drh 569 */
12a2a5e… drh 570 void diff_file(
a51808c… drh 571 Blob *pFile1, /* In memory content to compare from */
a51808c… drh 572 const char *zFile2, /* On disk content to compare to */
a51808c… drh 573 const char *zName, /* Display name of the file */
1347a1d… drh 574 DiffConfig *pCfg, /* Flags to control the diff */
590e01d… drh 575 Blob *pOut /* Blob to store diff output */
a51808c… drh 576 ){
346de5d… drh 577 if( pCfg->zDiffCmd==0 ){
585360b… drh 578 Blob out; /* Diff output text */
585360b… drh 579 Blob file2; /* Content of zFile2 */
585360b… drh 580 const char *zName2; /* Name of zFile2 for display */
a51808c… drh 581
a51808c… drh 582 /* Read content of zFile2 into memory */
a51808c… drh 583 blob_zero(&file2);
caa6ad3… drh 584 if( pCfg->diffFlags & DIFF_FILE_DELETED || file_size(zFile2, ExtFILE)<0 ){
135ed93… drh 585 zName2 = NULL_DEVICE;
135ed93… drh 586 }else{
1772357… drh 587 blob_read_from_file(&file2, zFile2, ExtFILE);
585360b… drh 588 zName2 = zName;
585360b… drh 589 }
a51808c… drh 590
a51808c… drh 591 /* Compute and output the differences */
3732790… danield 592 if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){
fbaa7ca… drh 593 if( blob_compare(pFile1, &file2) ){
fbaa7ca… drh 594 fossil_print("CHANGED %s\n", zName);
fbaa7ca… drh 595 }
fbaa7ca… drh 596 }else{
fbaa7ca… drh 597 blob_zero(&out);
db034a5… drh 598 text_diff(pFile1, &file2, &out, pCfg);
1347a1d… drh 599 if( blob_size(&out) ){
1347a1d… drh 600 if( pCfg->diffFlags & DIFF_NUMSTAT ){
7bcd662… drh 601 if( !(pCfg->diffFlags & DIFF_BRIEF) ){
3732790… danield 602 blob_appendf(pOut, "%s %s\n", blob_str(&out), zName);
7bcd662… drh 603 }
1347a1d… drh 604 }else{
590e01d… drh 605 diff_print_filenames(zName, zName2, pCfg, pOut);
590e01d… drh 606 blob_appendf(pOut, "%s\n", blob_str(&out));
3d6cf6a… drh 607 }
fbaa7ca… drh 608 }
fbaa7ca… drh 609 blob_reset(&out);
d5729c3… drh 610 }
a51808c… drh 611
a51808c… drh 612 /* Release memory resources */
a51808c… drh 613 blob_reset(&file2);
a51808c… drh 614 }else{
a51808c… drh 615 Blob nameFile1; /* Name of temporary file to old pFile1 content */
a51808c… drh 616 Blob cmd; /* Text of command to run */
caa6ad3… drh 617 int useTempfile = 1;
f8339c2… drh 618
346de5d… drh 619 if( (pCfg->diffFlags & DIFF_INCBINARY)==0 ){
f8339c2… drh 620 Blob file2;
346de5d… drh 621 if( looks_like_binary(pFile1) ){
49b0ff1… drh 622 fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
f8339c2… drh 623 return;
f8339c2… drh 624 }
346de5d… drh 625 if( pCfg->zBinGlob ){
346de5d… drh 626 Glob *pBinary = glob_create(pCfg->zBinGlob);
f8339c2… drh 627 if( glob_match(pBinary, zName) ){
49b0ff1… drh 628 fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
f8339c2… drh 629 glob_free(pBinary);
f8339c2… drh 630 return;
f8339c2… drh 631 }
f8339c2… drh 632 glob_free(pBinary);
f8339c2… drh 633 }
f8339c2… drh 634 blob_zero(&file2);
1772357… drh 635 if( file_size(zFile2, ExtFILE)>=0 ){
1772357… drh 636 blob_read_from_file(&file2, zFile2, ExtFILE);
4e0e69f… drh 637 }
4e0e69f… drh 638 if( looks_like_binary(&file2) ){
49b0ff1… drh 639 fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
f8339c2… drh 640 blob_reset(&file2);
f8339c2… drh 641 return;
f8339c2… drh 642 }
f8339c2… drh 643 blob_reset(&file2);
f8339c2… drh 644 }
a51808c… drh 645
a51808c… drh 646 /* Construct a temporary file to hold pFile1 based on the name of
a51808c… drh 647 ** zFile2 */
1dd2527… drh 648 file_tempname(&nameFile1, zFile2, "orig");
caa6ad3… drh 649 #if !defined(_WIN32)
caa6ad3… drh 650 /* On Unix, use /dev/null for added or deleted files. */
caa6ad3… drh 651 if( pCfg->diffFlags & DIFF_FILE_ADDED ){
caa6ad3… drh 652 blob_init(&nameFile1, NULL_DEVICE, -1);
caa6ad3… drh 653 useTempfile = 0;
caa6ad3… drh 654 }else if( pCfg->diffFlags & DIFF_FILE_DELETED ){
caa6ad3… drh 655 zFile2 = NULL_DEVICE;
caa6ad3… drh 656 }
caa6ad3… drh 657 #endif
caa6ad3… drh 658 if( useTempfile ) blob_write_to_file(pFile1, blob_str(&nameFile1));
a51808c… drh 659
a51808c… drh 660 /* Construct the external diff command */
a51808c… drh 661 blob_zero(&cmd);
346de5d… drh 662 blob_append(&cmd, pCfg->zDiffCmd, -1);
db034a5… drh 663 if( pCfg->diffFlags & DIFF_INVERT ){
4f83d06… drh 664 blob_append_escaped_arg(&cmd, zFile2, 1);
4f83d06… drh 665 blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1);
2a47673… mgagnon 666 }else{
4f83d06… drh 667 blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1);
4f83d06… drh 668 blob_append_escaped_arg(&cmd, zFile2, 1);
2a47673… mgagnon 669 }
a51808c… drh 670
a51808c… drh 671 /* Run the external diff command */
9b04150… mgagnon 672 fossil_system(blob_str(&cmd));
a51808c… drh 673
a51808c… drh 674 /* Delete the temporary file and clean up memory used */
caa6ad3… drh 675 if( useTempfile ) file_delete(blob_str(&nameFile1));
a51808c… drh 676 blob_reset(&nameFile1);
a51808c… drh 677 blob_reset(&cmd);
a51808c… drh 678 }
a51808c… drh 679 }
a51808c… drh 680
a51808c… drh 681 /*
fe8bb01… drh 682 ** Show the difference between two files, both in memory.
fe8bb01… drh 683 **
fe8bb01… drh 684 ** The difference is the set of edits needed to transform pFile1 into
fe8bb01… drh 685 ** pFile2.
fe8bb01… drh 686 **
fe8bb01… drh 687 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
fe8bb01… drh 688 ** command zDiffCmd to do the diffing.
f8339c2… drh 689 **
f8339c2… drh 690 ** When using an external diff program, zBinGlob contains the GLOB patterns
f8339c2… drh 691 ** for file names to treat as binary. If fIncludeBinary is zero, these files
f8339c2… drh 692 ** will be skipped in addition to files that may contain binary content.
fe8bb01… drh 693 */
12a2a5e… drh 694 void diff_file_mem(
fe8bb01… drh 695 Blob *pFile1, /* In memory content to compare from */
fe8bb01… drh 696 Blob *pFile2, /* In memory content to compare to */
fe8bb01… drh 697 const char *zName, /* Display name of the file */
1347a1d… drh 698 DiffConfig *pCfg /* Diff flags */
fe8bb01… drh 699 ){
3732790… danield 700 if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){
3732790… danield 701 return;
3732790… danield 702 }
346de5d… drh 703 if( pCfg->zDiffCmd==0 ){
fe8bb01… drh 704 Blob out; /* Diff output text */
fe8bb01… drh 705
fe8bb01… drh 706 blob_zero(&out);
1347a1d… drh 707 text_diff(pFile1, pFile2, &out, pCfg);
1347a1d… drh 708 if( pCfg->diffFlags & DIFF_NUMSTAT ){
7bcd662… drh 709 if( !(pCfg->diffFlags & DIFF_BRIEF) ){
3732790… danield 710 fossil_print("%s %s\n", blob_str(&out), zName);
7bcd662… drh 711 }
3d6cf6a… drh 712 }else{
1347a1d… drh 713 diff_print_filenames(zName, zName, pCfg, 0);
3d6cf6a… drh 714 fossil_print("%s\n", blob_str(&out));
3d6cf6a… drh 715 }
86d4754… jan.nijtmans 716
fe8bb01… drh 717 /* Release memory resources */
fe8bb01… drh 718 blob_reset(&out);
fe8bb01… drh 719 }else{
fe8bb01… drh 720 Blob cmd;
051d0ab… drh 721 Blob temp1;
051d0ab… drh 722 Blob temp2;
caa6ad3… drh 723 int useTempfile1 = 1;
caa6ad3… drh 724 int useTempfile2 = 1;
f8339c2… drh 725
346de5d… drh 726 if( (pCfg->diffFlags & DIFF_INCBINARY)==0 ){
346de5d… drh 727 if( looks_like_binary(pFile1) || looks_like_binary(pFile2) ){
49b0ff1… drh 728 fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
f8339c2… drh 729 return;
f8339c2… drh 730 }
346de5d… drh 731 if( pCfg->zBinGlob ){
346de5d… drh 732 Glob *pBinary = glob_create(pCfg->zBinGlob);
f8339c2… drh 733 if( glob_match(pBinary, zName) ){
49b0ff1… drh 734 fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
f8339c2… drh 735 glob_free(pBinary);
f8339c2… drh 736 return;
f8339c2… drh 737 }
f8339c2… drh 738 glob_free(pBinary);
f8339c2… drh 739 }
f8339c2… drh 740 }
b66b99c… jan.nijtmans 741
caa6ad3… drh 742 /* Construct temporary file names */
1dd2527… drh 743 file_tempname(&temp1, zName, "before");
1dd2527… drh 744 file_tempname(&temp2, zName, "after");
caa6ad3… drh 745 #if !defined(_WIN32)
caa6ad3… drh 746 /* On Unix, use /dev/null for added or deleted files. */
caa6ad3… drh 747 if( pCfg->diffFlags & DIFF_FILE_ADDED ){
caa6ad3… drh 748 useTempfile1 = 0;
caa6ad3… drh 749 blob_init(&temp1, NULL_DEVICE, -1);
caa6ad3… drh 750 }else if( pCfg->diffFlags & DIFF_FILE_DELETED ){
caa6ad3… drh 751 useTempfile2 = 0;
caa6ad3… drh 752 blob_init(&temp2, NULL_DEVICE, -1);
caa6ad3… drh 753 }
caa6ad3… drh 754 #endif
caa6ad3… drh 755 if( useTempfile1 ) blob_write_to_file(pFile1, blob_str(&temp1));
caa6ad3… drh 756 if( useTempfile2 ) blob_write_to_file(pFile2, blob_str(&temp2));
fe8bb01… drh 757
fe8bb01… drh 758 /* Construct the external diff command */
fe8bb01… drh 759 blob_zero(&cmd);
346de5d… drh 760 blob_append(&cmd, pCfg->zDiffCmd, -1);
4f83d06… drh 761 blob_append_escaped_arg(&cmd, blob_str(&temp1), 1);
4f83d06… drh 762 blob_append_escaped_arg(&cmd, blob_str(&temp2), 1);
fe8bb01… drh 763
fe8bb01… drh 764 /* Run the external diff command */
d9880a8… drh 765 fossil_system(blob_str(&cmd));
fe8bb01… drh 766
fe8bb01… drh 767 /* Delete the temporary file and clean up memory used */
caa6ad3… drh 768 if( useTempfile1 ) file_delete(blob_str(&temp1));
caa6ad3… drh 769 if( useTempfile2 ) file_delete(blob_str(&temp2));
051d0ab… drh 770
051d0ab… drh 771 blob_reset(&temp1);
051d0ab… drh 772 blob_reset(&temp2);
fe8bb01… drh 773 blob_reset(&cmd);
fe8bb01… drh 774 }
fe8bb01… drh 775 }
fe8bb01… drh 776
fe8bb01… drh 777 /*
fb2d637… js 778 ** Return true if the disk file is identical to the Blob. Return zero
960c9e8… drh 779 ** if the files differ in any way.
960c9e8… drh 780 */
960c9e8… drh 781 static int file_same_as_blob(Blob *blob, const char *zDiskFile){
960c9e8… drh 782 Blob file;
960c9e8… drh 783 int rc = 0;
960c9e8… drh 784 if( blob_size(blob)!=file_size(zDiskFile, ExtFILE) ) return 0;
960c9e8… drh 785 blob_zero(&file);
960c9e8… drh 786 blob_read_from_file(&file, zDiskFile, ExtFILE);
960c9e8… drh 787 if( blob_size(&file)!=blob_size(blob) ){
960c9e8… drh 788 rc = 0;
960c9e8… drh 789 }else{
960c9e8… drh 790 rc = memcmp(blob_buffer(&file), blob_buffer(blob), blob_size(&file))==0;
960c9e8… drh 791 }
960c9e8… drh 792 blob_reset(&file);
960c9e8… drh 793 return rc;
960c9e8… drh 794 }
960c9e8… drh 795
960c9e8… drh 796 /*
99d0baa… drh 797 ** Run a diff between the version zFrom and files on disk in the current
99d0baa… drh 798 ** working checkout. zFrom might be NULL which means to simply show the
99d0baa… drh 799 ** difference between the edited files on disk and the check-out on which
99d0baa… drh 800 ** they are based.
f8339c2… drh 801 **
f8339c2… drh 802 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
f8339c2… drh 803 ** command zDiffCmd to do the diffing.
f8339c2… drh 804 **
f8339c2… drh 805 ** When using an external diff program, zBinGlob contains the GLOB patterns
f8339c2… drh 806 ** for file names to treat as binary. If fIncludeBinary is zero, these files
f8339c2… drh 807 ** will be skipped in addition to files that may contain binary content.
a51808c… drh 808 */
99d0baa… drh 809 void diff_version_to_checkout(
5ef7435… drh 810 const char *zFrom, /* Version to difference from */
1347a1d… drh 811 DiffConfig *pCfg, /* Flags controlling diff output */
7ee98fe… drh 812 FileDirList *pFileDir, /* Which files to diff */
99d0baa… drh 813 Blob *pOut /* Blob to output diff instead of stdout */
5ef7435… drh 814 ){
c863ec1… drh 815 int vid;
a51808c… drh 816 Blob sql;
a51808c… drh 817 Stmt q;
e2bdc10… danield 818 int asNewFile; /* Treat non-existent files as empty files */
3d6cf6a… drh 819 int isNumStat; /* True for --numstat */
a51808c… drh 820
1347a1d… drh 821 asNewFile = (pCfg->diffFlags & (DIFF_VERBOSE|DIFF_NUMSTAT|DIFF_HTML))!=0;
1347a1d… drh 822 isNumStat = (pCfg->diffFlags & (DIFF_NUMSTAT|DIFF_TCL|DIFF_HTML))!=0;
c863ec1… drh 823 vid = db_lget_int("checkout", 0);
ae092ec… drh 824 vfile_check_signature(vid, CKSIG_ENOTFILE);
a51808c… drh 825 blob_zero(&sql);
a51808c… drh 826 db_begin_transaction();
a51808c… drh 827 if( zFrom ){
2a013f0… drh 828 int rid = name_to_typed_rid(zFrom, "ci");
a51808c… drh 829 if( !is_a_version(rid) ){
a51808c… drh 830 fossil_fatal("no such check-in: %s", zFrom);
a51808c… drh 831 }
a51808c… drh 832 load_vfile_from_rid(rid);
49b0ff1… drh 833 blob_append_sql(&sql,
e4f1c1f… drh 834 "SELECT v2.pathname, v2.deleted, v2.chnged, v2.rid==0, v1.rid, v1.islink"
a51808c… drh 835 " FROM vfile v1, vfile v2 "
a51808c… drh 836 " WHERE v1.pathname=v2.pathname AND v1.vid=%d AND v2.vid=%d"
34341a1… drh 837 " AND (v2.deleted OR v2.chnged OR v1.mrid!=v2.rid)"
a51808c… drh 838 "UNION "
e4f1c1f… drh 839 "SELECT pathname, 1, 0, 0, 0, islink"
a51808c… drh 840 " FROM vfile v1"
a51808c… drh 841 " WHERE v1.vid=%d"
a51808c… drh 842 " AND NOT EXISTS(SELECT 1 FROM vfile v2"
a51808c… drh 843 " WHERE v2.vid=%d AND v2.pathname=v1.pathname)"
a51808c… drh 844 "UNION "
e4f1c1f… drh 845 "SELECT pathname, 0, 0, 1, 0, islink"
a51808c… drh 846 " FROM vfile v2"
a51808c… drh 847 " WHERE v2.vid=%d"
a51808c… drh 848 " AND NOT EXISTS(SELECT 1 FROM vfile v1"
a51808c… drh 849 " WHERE v1.vid=%d AND v1.pathname=v2.pathname)"
421c9ee… drh 850 " ORDER BY 1 /*scan*/",
a51808c… drh 851 rid, vid, rid, vid, vid, rid
a51808c… drh 852 );
a51808c… drh 853 }else{
49b0ff1… drh 854 blob_append_sql(&sql,
e4f1c1f… drh 855 "SELECT pathname, deleted, chnged , rid==0, rid, islink"
a51808c… drh 856 " FROM vfile"
a51808c… drh 857 " WHERE vid=%d"
a51808c… drh 858 " AND (deleted OR chnged OR rid==0)"
421c9ee… drh 859 " ORDER BY pathname /*scan*/",
a51808c… drh 860 vid
a51808c… drh 861 );
a51808c… drh 862 }
41f6a45… danield 863 if( (pCfg->diffFlags & DIFF_SHOW_VERS)!=0 ){
41f6a45… danield 864 diff_print_versions(zFrom ? zFrom : db_lget("checkout-hash", 0),
41f6a45… danield 865 "(workdir)", pCfg);
41f6a45… danield 866 }
49b0ff1… drh 867 db_prepare(&q, "%s", blob_sql_text(&sql));
36babe0… drh 868 blob_reset(&sql);
c863ec1… drh 869 while( db_step(&q)==SQLITE_ROW ){
c863ec1… drh 870 const char *zPathname = db_column_text(&q,0);
c863ec1… drh 871 int isDeleted = db_column_int(&q, 1);
c863ec1… drh 872 int isChnged = db_column_int(&q,2);
a51808c… drh 873 int isNew = db_column_int(&q,3);
585360b… drh 874 int srcid = db_column_int(&q, 4);
e4f1c1f… drh 875 int isLink = db_column_int(&q, 5);
825d78b… mistachkin 876 const char *zFullName;
585360b… drh 877 int showDiff = 1;
825d78b… mistachkin 878 Blob fname;
825d78b… mistachkin 879
c46f980… drh 880 if( !file_dir_match(pFileDir, zPathname) ) continue;
825d78b… mistachkin 881 if( determine_exec_relative_option(0) ){
825d78b… mistachkin 882 blob_zero(&fname);
825d78b… mistachkin 883 file_relative_name(zPathname, &fname, 1);
825d78b… mistachkin 884 }else{
825d78b… mistachkin 885 blob_set(&fname, g.zLocalRoot);
825d78b… mistachkin 886 blob_append(&fname, zPathname, -1);
825d78b… mistachkin 887 }
825d78b… mistachkin 888 zFullName = blob_str(&fname);
caa6ad3… drh 889 pCfg->diffFlags &= (~DIFF_FILE_MASK);
585360b… drh 890 if( isDeleted ){
3d6cf6a… drh 891 if( !isNumStat ){ fossil_print("DELETED %s\n", zPathname); }
caa6ad3… drh 892 pCfg->diffFlags |= DIFF_FILE_DELETED;
135ed93… drh 893 if( !asNewFile ){ showDiff = 0; zFullName = NULL_DEVICE; }
9df71fe… jan.nijtmans 894 }else if( file_access(zFullName, F_OK) ){
3d6cf6a… drh 895 if( !isNumStat ){ fossil_print("MISSING %s\n", zPathname); }
585360b… drh 896 if( !asNewFile ){ showDiff = 0; }
585360b… drh 897 }else if( isNew ){
3d6cf6a… drh 898 if( !isNumStat ){ fossil_print("ADDED %s\n", zPathname); }
caa6ad3… drh 899 pCfg->diffFlags |= DIFF_FILE_ADDED;
585360b… drh 900 srcid = 0;
585360b… drh 901 if( !asNewFile ){ showDiff = 0; }
585360b… drh 902 }else if( isChnged==3 ){
3d6cf6a… drh 903 if( !isNumStat ){ fossil_print("ADDED_BY_MERGE %s\n", zPathname); }
caa6ad3… drh 904 pCfg->diffFlags |= DIFF_FILE_ADDED;
a6b999c… mistachkin 905 srcid = 0;
a6b999c… mistachkin 906 if( !asNewFile ){ showDiff = 0; }
a6b999c… mistachkin 907 }else if( isChnged==5 ){
3d6cf6a… drh 908 if( !isNumStat ){ fossil_print("ADDED_BY_INTEGRATE %s\n", zPathname); }
caa6ad3… drh 909 pCfg->diffFlags |= DIFF_FILE_ADDED;
585360b… drh 910 srcid = 0;
585360b… drh 911 if( !asNewFile ){ showDiff = 0; }
585360b… drh 912 }
fbaa7ca… drh 913 if( showDiff ){
a51808c… drh 914 Blob content;
1772357… drh 915 if( !isLink != !file_islink(zFullName) ){
1347a1d… drh 916 diff_print_index(zPathname, pCfg, 0);
1347a1d… drh 917 diff_print_filenames(zPathname, zPathname, pCfg, 0);
49b0ff1… drh 918 fossil_print("%s",DIFF_CANNOT_COMPUTE_SYMLINK);
e4f1c1f… drh 919 continue;
e4f1c1f… drh 920 }
585360b… drh 921 if( srcid>0 ){
585360b… drh 922 content_get(srcid, &content);
585360b… drh 923 }else{
585360b… drh 924 blob_zero(&content);
585360b… drh 925 }
caa6ad3… drh 926 if( isChnged==0
caa6ad3… drh 927 || pCfg->diffFlags & DIFF_FILE_DELETED
caa6ad3… drh 928 || !file_same_as_blob(&content, zFullName)
caa6ad3… drh 929 ){
960c9e8… drh 930 diff_print_index(zPathname, pCfg, pOut);
960c9e8… drh 931 diff_file(&content, zFullName, zPathname, pCfg, pOut);
960c9e8… drh 932 }
a51808c… drh 933 blob_reset(&content);
a51808c… drh 934 }
825d78b… mistachkin 935 blob_reset(&fname);
296b90a… drh 936 }
296b90a… drh 937 db_finalize(&q);
296b90a… drh 938 db_end_transaction(1); /* ROLLBACK */
296b90a… drh 939 }
296b90a… drh 940
296b90a… drh 941 /*
99d0baa… drh 942 ** Run a diff from the undo buffer to files on disk.
f8339c2… drh 943 **
f8339c2… drh 944 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
f8339c2… drh 945 ** command zDiffCmd to do the diffing.
f8339c2… drh 946 **
f8339c2… drh 947 ** When using an external diff program, zBinGlob contains the GLOB patterns
f8339c2… drh 948 ** for file names to treat as binary. If fIncludeBinary is zero, these files
f8339c2… drh 949 ** will be skipped in addition to files that may contain binary content.
7036534… drh 950 */
99d0baa… drh 951 static void diff_undo_to_checkout(
1347a1d… drh 952 DiffConfig *pCfg, /* Flags controlling diff output */
c46f980… drh 953 FileDirList *pFileDir /* List of files and directories to diff */
7036534… drh 954 ){
485aa80… drh 955 Stmt q;
f6c1363… drh 956 Blob content;
f6c1363… drh 957 db_prepare(&q, "SELECT pathname, content FROM undo");
f6c1363… drh 958 blob_init(&content, 0, 0);
41f6a45… danield 959 if( (pCfg->diffFlags & DIFF_SHOW_VERS)!=0 ){
41f6a45… danield 960 diff_print_versions("(undo)", "(workdir)", pCfg);
275da70… danield 961 }
485aa80… drh 962 while( db_step(&q)==SQLITE_ROW ){
c46f980… drh 963 char *zFullName;
f6c1363… drh 964 const char *zFile = (const char*)db_column_text(&q, 0);
c46f980… drh 965 if( !file_dir_match(pFileDir, zFile) ) continue;
c46f980… drh 966 zFullName = mprintf("%s%s", g.zLocalRoot, zFile);
f6c1363… drh 967 db_column_blob(&q, 1, &content);
db034a5… drh 968 diff_file(&content, zFullName, zFile, pCfg, 0);
f6c1363… drh 969 fossil_free(zFullName);
f6c1363… drh 970 blob_reset(&content);
485aa80… drh 971 }
485aa80… drh 972 db_finalize(&q);
7036534… drh 973 }
7036534… drh 974
7036534… drh 975 /*
7036534… drh 976 ** Show the difference between two files identified by ManifestFile
7036534… drh 977 ** entries.
f8339c2… drh 978 **
f8339c2… drh 979 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
f8339c2… drh 980 ** command zDiffCmd to do the diffing.
f8339c2… drh 981 **
f8339c2… drh 982 ** When using an external diff program, zBinGlob contains the GLOB patterns
f8339c2… drh 983 ** for file names to treat as binary. If fIncludeBinary is zero, these files
f8339c2… drh 984 ** will be skipped in addition to files that may contain binary content.
fe8bb01… drh 985 */
7036534… drh 986 static void diff_manifest_entry(
7036534… drh 987 struct ManifestFile *pFrom,
7036534… drh 988 struct ManifestFile *pTo,
1347a1d… drh 989 DiffConfig *pCfg
fe8bb01… drh 990 ){
7036534… drh 991 Blob f1, f2;
7036534… drh 992 int rid;
7807ec4… mistachkin 993 const char *zName;
7807ec4… mistachkin 994 if( pFrom ){
7807ec4… mistachkin 995 zName = pFrom->zName;
7807ec4… mistachkin 996 }else if( pTo ){
7807ec4… mistachkin 997 zName = pTo->zName;
7807ec4… mistachkin 998 }else{
7807ec4… mistachkin 999 zName = DIFF_NO_NAME;
7807ec4… mistachkin 1000 }
3732790… danield 1001 if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){
3732790… danield 1002 return;
3732790… danield 1003 }
1347a1d… drh 1004 diff_print_index(zName, pCfg, 0);
7036534… drh 1005 if( pFrom ){
7036534… drh 1006 rid = uuid_to_rid(pFrom->zUuid, 0);
7036534… drh 1007 content_get(rid, &f1);
7036534… drh 1008 }else{
7036534… drh 1009 blob_zero(&f1);
7036534… drh 1010 }
7036534… drh 1011 if( pTo ){
7036534… drh 1012 rid = uuid_to_rid(pTo->zUuid, 0);
7036534… drh 1013 content_get(rid, &f2);
7036534… drh 1014 }else{
7036534… drh 1015 blob_zero(&f2);
7036534… drh 1016 }
346de5d… drh 1017 diff_file_mem(&f1, &f2, zName, pCfg);
7036534… drh 1018 blob_reset(&f1);
7036534… drh 1019 blob_reset(&f2);
fe8bb01… drh 1020 }
fe8bb01… drh 1021
fe8bb01… drh 1022 /*
fe8bb01… drh 1023 ** Output the differences between two check-ins.
f8339c2… drh 1024 **
f8339c2… drh 1025 ** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
f8339c2… drh 1026 ** command zDiffCmd to do the diffing.
f8339c2… drh 1027 **
f8339c2… drh 1028 ** When using an external diff program, zBinGlob contains the GLOB patterns
f8339c2… drh 1029 ** for file names to treat as binary. If fIncludeBinary is zero, these files
f8339c2… drh 1030 ** will be skipped in addition to files that may contain binary content.
fe8bb01… drh 1031 */
c46f980… drh 1032 static void diff_two_versions(
fe8bb01… drh 1033 const char *zFrom,
fe8bb01… drh 1034 const char *zTo,
1347a1d… drh 1035 DiffConfig *pCfg,
c46f980… drh 1036 FileDirList *pFileDir
296b90a… drh 1037 ){
d13054c… drh 1038 Manifest *pFrom, *pTo;
d13054c… drh 1039 ManifestFile *pFromFile, *pToFile;
1347a1d… drh 1040 int asNewFlag = (pCfg->diffFlags & (DIFF_VERBOSE|DIFF_NUMSTAT))!=0 ? 1 : 0;
d13054c… drh 1041
d13054c… drh 1042 pFrom = manifest_get_by_name(zFrom, 0);
d13054c… drh 1043 manifest_file_rewind(pFrom);
d13054c… drh 1044 pFromFile = manifest_file_next(pFrom,0);
d13054c… drh 1045 pTo = manifest_get_by_name(zTo, 0);
d13054c… drh 1046 manifest_file_rewind(pTo);
d13054c… drh 1047 pToFile = manifest_file_next(pTo,0);
41f6a45… danield 1048 if( (pCfg->diffFlags & DIFF_SHOW_VERS)!=0 ){
41f6a45… danield 1049 diff_print_versions(zFrom, zTo, pCfg);
275da70… danield 1050 }
d13054c… drh 1051 while( pFromFile || pToFile ){
296b90a… drh 1052 int cmp;
d13054c… drh 1053 if( pFromFile==0 ){
296b90a… drh 1054 cmp = +1;
d13054c… drh 1055 }else if( pToFile==0 ){
296b90a… drh 1056 cmp = -1;
296b90a… drh 1057 }else{
31c52c7… drh 1058 cmp = fossil_strcmp(pFromFile->zName, pToFile->zName);
296b90a… drh 1059 }
caa6ad3… drh 1060 pCfg->diffFlags &= (~DIFF_FILE_MASK);
296b90a… drh 1061 if( cmp<0 ){
c46f980… drh 1062 if( file_dir_match(pFileDir, pFromFile->zName) ){
1347a1d… drh 1063 if( (pCfg->diffFlags & (DIFF_NUMSTAT|DIFF_HTML))==0 ){
3d6cf6a… drh 1064 fossil_print("DELETED %s\n", pFromFile->zName);
3d6cf6a… drh 1065 }
caa6ad3… drh 1066 pCfg->diffFlags |= DIFF_FILE_DELETED;
c46f980… drh 1067 if( asNewFlag ){
346de5d… drh 1068 diff_manifest_entry(pFromFile, 0, pCfg);
c46f980… drh 1069 }
7036534… drh 1070 }
d13054c… drh 1071 pFromFile = manifest_file_next(pFrom,0);
296b90a… drh 1072 }else if( cmp>0 ){
c46f980… drh 1073 if( file_dir_match(pFileDir, pToFile->zName) ){
1347a1d… drh 1074 if( (pCfg->diffFlags &
1347a1d… drh 1075 (DIFF_NUMSTAT|DIFF_HTML|DIFF_TCL|DIFF_JSON))==0 ){
3d6cf6a… drh 1076 fossil_print("ADDED %s\n", pToFile->zName);
3d6cf6a… drh 1077 }
caa6ad3… drh 1078 pCfg->diffFlags |= DIFF_FILE_ADDED;
c46f980… drh 1079 if( asNewFlag ){
346de5d… drh 1080 diff_manifest_entry(0, pToFile, pCfg);
c46f980… drh 1081 }
7036534… drh 1082 }
d13054c… drh 1083 pToFile = manifest_file_next(pTo,0);
31c52c7… drh 1084 }else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
296b90a… drh 1085 /* No changes */
c46f980… drh 1086 (void)file_dir_match(pFileDir, pFromFile->zName); /* Record name usage */
d13054c… drh 1087 pFromFile = manifest_file_next(pFrom,0);
d13054c… drh 1088 pToFile = manifest_file_next(pTo,0);
d13054c… drh 1089 }else{
c46f980… drh 1090 if( file_dir_match(pFileDir, pToFile->zName) ){
3732790… danield 1091 if((pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT)){
c46f980… drh 1092 fossil_print("CHANGED %s\n", pFromFile->zName);
c46f980… drh 1093 }else{
346de5d… drh 1094 diff_manifest_entry(pFromFile, pToFile, pCfg);
c46f980… drh 1095 }
e0565d4… drh 1096 }
d13054c… drh 1097 pFromFile = manifest_file_next(pFrom,0);
d13054c… drh 1098 pToFile = manifest_file_next(pTo,0);
d13054c… drh 1099 }
d13054c… drh 1100 }
d13054c… drh 1101 manifest_destroy(pFrom);
d13054c… drh 1102 manifest_destroy(pTo);
d13054c… drh 1103 }
d13054c… drh 1104
d13054c… drh 1105 /*
99d0baa… drh 1106 ** Compute the difference from an external tree of files to the current
99d0baa… drh 1107 ** working checkout with its edits.
99d0baa… drh 1108 **
99d0baa… drh 1109 ** To put it another way: Every managed file in the current working
36f3549… brickviking 1110 ** checkout is compared to the file with same name under zExternBase. The
99d0baa… drh 1111 ** zExternBase files are on the left and the files in the current working
99d0baa… drh 1112 ** directory are on the right.
99d0baa… drh 1113 */
99d0baa… drh 1114 void diff_externbase_to_checkout(
99d0baa… drh 1115 const char *zExternBase, /* Remote tree to use as the baseline */
99d0baa… drh 1116 DiffConfig *pCfg, /* Diff settings */
99d0baa… drh 1117 FileDirList *pFileDir /* Only look at these files */
99d0baa… drh 1118 ){
99d0baa… drh 1119 int vid;
99d0baa… drh 1120 Stmt q;
99d0baa… drh 1121
99d0baa… drh 1122 vid = db_lget_int("checkout",0);
99d0baa… drh 1123 if( file_isdir(zExternBase, ExtFILE)!=1 ){
99d0baa… drh 1124 fossil_fatal("\"%s\" is not a directory", zExternBase);
99d0baa… drh 1125 }
99d0baa… drh 1126 db_prepare(&q,
99d0baa… drh 1127 "SELECT pathname FROM vfile WHERE vid=%d ORDER BY pathname",
99d0baa… drh 1128 vid
99d0baa… drh 1129 );
99d0baa… drh 1130 while( db_step(&q)==SQLITE_ROW ){
99d0baa… drh 1131 const char *zFile; /* Name of file in the repository */
99d0baa… drh 1132 char *zLhs; /* Full name of left-hand side file */
99d0baa… drh 1133 char *zRhs; /* Full name of right-hand side file */
99d0baa… drh 1134 Blob rhs; /* Full text of RHS */
99d0baa… drh 1135 Blob lhs; /* Full text of LHS */
99d0baa… drh 1136
99d0baa… drh 1137 zFile = db_column_text(&q,0);
99d0baa… drh 1138 if( !file_dir_match(pFileDir, zFile) ) continue;
99d0baa… drh 1139 zLhs = mprintf("%s/%s", zExternBase, zFile);
99d0baa… drh 1140 zRhs = mprintf("%s%s", g.zLocalRoot, zFile);
99d0baa… drh 1141 if( file_size(zLhs, ExtFILE)<0 ){
99d0baa… drh 1142 blob_zero(&lhs);
99d0baa… drh 1143 }else{
99d0baa… drh 1144 blob_read_from_file(&lhs, zLhs, ExtFILE);
99d0baa… drh 1145 }
99d0baa… drh 1146 blob_read_from_file(&rhs, zRhs, ExtFILE);
99d0baa… drh 1147 if( blob_size(&lhs)!=blob_size(&rhs)
99d0baa… drh 1148 || memcmp(blob_buffer(&lhs), blob_buffer(&rhs), blob_size(&lhs))!=0
99d0baa… drh 1149 ){
99d0baa… drh 1150 diff_print_index(zFile, pCfg, 0);
99d0baa… drh 1151 diff_file_mem(&lhs, &rhs, zFile, pCfg);
99d0baa… drh 1152 }
99d0baa… drh 1153 blob_reset(&lhs);
99d0baa… drh 1154 blob_reset(&rhs);
99d0baa… drh 1155 fossil_free(zLhs);
99d0baa… drh 1156 fossil_free(zRhs);
99d0baa… drh 1157 }
99d0baa… drh 1158 db_finalize(&q);
99d0baa… drh 1159 }
99d0baa… drh 1160
99d0baa… drh 1161
99d0baa… drh 1162 /*
cad57bf… drh 1163 ** Return the name of the external diff command, or return NULL if
cad57bf… drh 1164 ** no external diff command is defined.
cad57bf… drh 1165 */
cad57bf… drh 1166 const char *diff_command_external(int guiDiff){
cad57bf… drh 1167 const char *zName;
954fb42… drh 1168 zName = guiDiff ? "gdiff-command" : "diff-command";
8c4c93b… drh 1169 return db_get(zName, 0);
5bec7a6… drh 1170 }
5bec7a6… drh 1171
5bec7a6… drh 1172 /*
5bec7a6… drh 1173 ** Return true if it reasonable to run "diff -tk" for "gdiff".
5bec7a6… drh 1174 **
5bec7a6… drh 1175 ** Details: Return true if all of the following are true:
5bec7a6… drh 1176 **
5bec7a6… drh 1177 ** (1) The isGDiff flags is true
5bec7a6… drh 1178 ** (2) The "gdiff-command" setting is undefined
5bec7a6… drh 1179 ** (3) There is a "tclsh" on PATH
5bec7a6… drh 1180 ** (4) There is a "wish" on PATH
5bec7a6… drh 1181 */
5bec7a6… drh 1182 int gdiff_using_tk(int isGdiff){
5bec7a6… drh 1183 if( isGdiff
5bec7a6… drh 1184 && db_get("gdiff-command","")[0]==0
5bec7a6… drh 1185 && fossil_app_on_path("tclsh",0)
5bec7a6… drh 1186 && fossil_app_on_path("wish",0)
5bec7a6… drh 1187 ){
5bec7a6… drh 1188 return 1;
5bec7a6… drh 1189 }
5bec7a6… drh 1190 return 0;
db6a8e8… drh 1191 }
22e5d71… drh 1192
22e5d71… drh 1193 /*
22e5d71… drh 1194 ** Show diff output in a Tcl/Tk window, in response to the --tk option
22e5d71… drh 1195 ** to the diff command.
45f3516… jan.nijtmans 1196 **
252aff3… jan.nijtmans 1197 ** If fossil has direct access to a Tcl interpreter (either loaded
252aff3… jan.nijtmans 1198 ** dynamically through stubs or linked in statically), we can use it
252aff3… jan.nijtmans 1199 ** directly. Otherwise:
22e5d71… drh 1200 ** (1) Write the Tcl/Tk script used for rendering into a temp file.
252aff3… jan.nijtmans 1201 ** (2) Invoke "tclsh" on the temp file using fossil_system().
22e5d71… drh 1202 ** (3) Delete the temp file.
22e5d71… drh 1203 */
2ed8cdc… drh 1204 void diff_tk(const char *zSubCmd, int firstArg){
22e5d71… drh 1205 int i;
22e5d71… drh 1206 Blob script;
c543079… drh 1207 const char *zTempFile = 0;
22e5d71… drh 1208 char *zCmd;
9fc945c… drh 1209 const char *zTclsh;
f5cb12d… drh 1210 int bDebug = find_option("tkdebug",0,0)!=0;
5a8516d… drh 1211 int bDarkMode = find_option("dark",0,0)!=0;
d7cf189… drh 1212 (void)find_option("debug",0,0);
22e5d71… drh 1213 blob_zero(&script);
f5cb12d… drh 1214 /* Caution: When this routine is called from the merge-info command,
f5cb12d… drh 1215 ** the --tcl argument requires an argument. But merge-info does not
f5cb12d… drh 1216 ** use -i, so we can take -i as that argument. This routine needs to
f5cb12d… drh 1217 ** always have -i after --tcl.
f5cb12d… drh 1218 ** CAUTION!
f5cb12d… drh 1219 ** vvvvvvv */
9e33074… drh 1220 blob_appendf(&script, "set fossilcmd {| \"%/\" %s -tcl -i -v",
2ed8cdc… drh 1221 g.nameOfExe, zSubCmd);
9e33074… drh 1222 find_option("tcl",0,0);
c060947… jan.nijtmans 1223 find_option("html",0,0);
c060947… jan.nijtmans 1224 find_option("side-by-side","y",0);
c060947… jan.nijtmans 1225 find_option("internal","i",0);
c060947… jan.nijtmans 1226 find_option("verbose","v",0);
9fc945c… drh 1227 zTclsh = find_option("tclsh",0,1);
9fc945c… drh 1228 if( zTclsh==0 ){
eb804dc… drh 1229 zTclsh = db_get("tclsh",0);
9fc945c… drh 1230 }
c060947… jan.nijtmans 1231 /* The undocumented --script FILENAME option causes the Tk script to
c060947… jan.nijtmans 1232 ** be written into the FILENAME instead of being run. This is used
c060947… jan.nijtmans 1233 ** for testing and debugging. */
c060947… jan.nijtmans 1234 zTempFile = find_option("script",0,1);
2ed8cdc… drh 1235 for(i=firstArg; i<g.argc; i++){
60d5b1f… drh 1236 const char *z = g.argv[i];
544d221… drh 1237 if( sqlite3_strglob("*}*",z) ){
544d221… drh 1238 blob_appendf(&script, " {%/}", z);
544d221… drh 1239 }else{
544d221… drh 1240 int j;
544d221… drh 1241 blob_append(&script, " ", 1);
544d221… drh 1242 for(j=0; z[j]; j++) blob_appendf(&script, "\\%03o", (unsigned char)z[j]);
544d221… drh 1243 }
544d221… drh 1244 }
5a8516d… drh 1245 blob_appendf(&script, "}\nset darkmode %d\n", bDarkMode);
f5cb12d… drh 1246 blob_appendf(&script, "set debug %d\n", bDebug);
5a8516d… drh 1247 blob_appendf(&script, "%s", builtin_file("diff.tcl", 0));
cee30a3… drh 1248 if( zTempFile ){
cee30a3… drh 1249 blob_write_to_file(&script, zTempFile);
9fc945c… drh 1250 fossil_print("To see diff, run: %s \"%s\"\n", zTclsh, zTempFile);
cee30a3… drh 1251 }else{
252aff3… jan.nijtmans 1252 #if defined(FOSSIL_ENABLE_TCL)
fe9990a… mistachkin 1253 Th_FossilInit(TH_INIT_DEFAULT);
be26772… mistachkin 1254 if( evaluateTclWithEvents(g.interp, &g.tcl, blob_str(&script),
18fc492… mistachkin 1255 blob_size(&script), 1, 1, 0)==TCL_OK ){
252aff3… jan.nijtmans 1256 blob_reset(&script);
252aff3… jan.nijtmans 1257 return;
252aff3… jan.nijtmans 1258 }
be26772… mistachkin 1259 /*
be26772… mistachkin 1260 * If evaluation of the Tcl script fails, the reason may be that Tk
be26772… mistachkin 1261 * could not be found by the loaded Tcl, or that Tcl cannot be loaded
be26772… mistachkin 1262 * dynamically (e.g. x64 Tcl with x86 Fossil). Therefore, fallback
be26772… mistachkin 1263 * to using the external "tclsh", if available.
be26772… mistachkin 1264 */
252aff3… jan.nijtmans 1265 #endif
cee30a3… drh 1266 zTempFile = write_blob_to_temp_file(&script);
2209f55… drh 1267 zCmd = mprintf("%$ %$", zTclsh, zTempFile);
cee30a3… drh 1268 fossil_system(zCmd);
cee30a3… drh 1269 file_delete(zTempFile);
cee30a3… drh 1270 fossil_free(zCmd);
cee30a3… drh 1271 }
cee30a3… drh 1272 blob_reset(&script);
f8339c2… drh 1273 }
f8339c2… drh 1274
f8339c2… drh 1275 /*
f8339c2… drh 1276 ** Returns the GLOB pattern for file names that should be treated as binary
f8339c2… drh 1277 ** by the diff subsystem, if any.
f8339c2… drh 1278 */
f8339c2… drh 1279 const char *diff_get_binary_glob(void){
f8339c2… drh 1280 const char *zBinGlob = find_option("binary", 0, 1);
f8339c2… drh 1281 if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0);
f8339c2… drh 1282 return zBinGlob;
cad57bf… drh 1283 }
cad57bf… drh 1284
cad57bf… drh 1285 /*
dbda8d6… drh 1286 ** COMMAND: diff
d0305b3… aku 1287 ** COMMAND: gdiff
d0305b3… aku 1288 **
2210be1… drh 1289 ** Usage: %fossil diff|gdiff ?OPTIONS? ?FILE1? ?FILE2 ...?
83c032d… drh 1290 **
83c032d… drh 1291 ** Show the difference between the current version of each of the FILEs
bc36fdc… danield 1292 ** specified (as they exist on disk) and that same file as it was checked-
dc757a5… drh 1293 ** out. Or if the FILE arguments are omitted, show all unsaved changes
5bec7a6… drh 1294 ** currently in the working check-out. The "gdiff" variant means to
e879d1e… brickviking 1295 ** use a GUI diff.
dc757a5… drh 1296 **
dc757a5… drh 1297 ** The default output format is a "unified patch" (the same as the
dc757a5… drh 1298 ** output of "diff -u" on most unix systems). Many alternative formats
dc757a5… drh 1299 ** are available. A few of the more useful alternatives:
dc757a5… drh 1300 **
b89f4cd… florian 1301 ** --tk Pop up a Tcl/Tk-based GUI to show the diff
dc757a5… drh 1302 ** --by Show a side-by-side diff in the default web browser
dc757a5… drh 1303 ** -b Show a linear diff in the default web browser
dc757a5… drh 1304 ** -y Show a text side-by-side diff
dc757a5… drh 1305 ** --webpage Format output as HTML
dc757a5… drh 1306 ** --webpage -y HTML output in the side-by-side format
dc757a5… drh 1307 **
e1b5115… danield 1308 ** The "--from VERSION" option is used to specify the source check-in
dc757a5… drh 1309 ** for the diff operation. If not specified, the source check-in is the
dc757a5… drh 1310 ** base check-in for the current check-out. Similarly, the "--to VERSION"
dc757a5… drh 1311 ** option specifies the check-in from which the second version of the file
dc757a5… drh 1312 ** or files is taken. If there is no "--to" option then the (possibly edited)
dc757a5… drh 1313 ** files in the current check-out are used. The "--checkin VERSION" option
dc757a5… drh 1314 ** shows the changes made by check-in VERSION relative to its primary parent.
5839aba… drh 1315 ** The "--branch BRANCHNAME" shows all the changes on the branch BRANCHNAME.
5839aba… drh 1316 **
763758d… drh 1317 ** With the "--from VERSION" option, if VERSION is actually a directory name
763758d… drh 1318 ** (not a tag or check-in hash) then the files under that directory are used
763758d… drh 1319 ** as the baseline for the diff.
763758d… drh 1320 **
e1b5115… danield 1321 ** The "-i" command-line option forces the use of Fossil's own internal
dc757a5… drh 1322 ** diff logic rather than any external diff program that might be configured
dc757a5… drh 1323 ** using the "setting" command. If no external diff program is configured,
dc757a5… drh 1324 ** then the "-i" option is a no-op. The "-i" option converts "gdiff" into
dc757a5… drh 1325 ** "diff".
f8339c2… drh 1326 **
f8339c2… drh 1327 ** The "--diff-binary" option enables or disables the inclusion of binary files
f8339c2… drh 1328 ** when using an external diff program.
f8339c2… drh 1329 **
f8339c2… drh 1330 ** The "--binary" option causes files matching the glob PATTERN to be treated
e1b5115… danield 1331 ** as binary when considering if they should be used with the external diff
e1b5115… danield 1332 ** program. This option overrides the "binary-glob" setting.
dc757a5… drh 1333 **
33d3bf3… km 1334 ** These commands show differences between managed files. Use the "fossil xdiff"
dc757a5… drh 1335 ** command to see differences in unmanaged files.
2210be1… drh 1336 **
2210be1… drh 1337 ** Options:
11384f1… drh 1338 ** --binary PATTERN Treat files that match the glob PATTERN
11384f1… drh 1339 ** as binary
11384f1… drh 1340 ** --branch BRANCH Show diff of all changes on BRANCH
11384f1… drh 1341 ** --brief Show filenames only
9a3372e… drh 1342 ** -b|--browser Show the diff output in a web-browser
9a3372e… drh 1343 ** --by Shorthand for "--browser -y"
c8a7ee7… danield 1344 ** -ci|--checkin VERSION Show diff of all changes in VERSION
11384f1… drh 1345 ** --command PROG External diff program. Overrides "diff-command"
275da70… danield 1346 ** -c|--context N Show N lines of context around each change,
275da70… danield 1347 ** with negative N meaning show all content
b89f4cd… florian 1348 ** --dark Use dark mode for the Tcl/Tk-based GUI and HTML
11384f1… drh 1349 ** --diff-binary BOOL Include binary files with external commands
11384f1… drh 1350 ** --exec-abs-paths Force absolute path names on external commands
11384f1… drh 1351 ** --exec-rel-paths Force relative path names on external commands
763758d… drh 1352 ** -r|--from VERSION Use VERSION as the baseline for the diff, or
763758d… drh 1353 ** if VERSION is a directory name, use files in
763758d… drh 1354 ** that directory as the baseline.
ea52b7d… drh 1355 ** -w|--ignore-all-space Ignore white space when comparing lines
7f3c728… jamsek 1356 ** -i|--internal Use internal diff logic
eb4efc8… danield 1357 ** --invert Invert the diff
dc757a5… drh 1358 ** --json Output formatted as JSON
eb4efc8… danield 1359 ** -n|--linenum Show line numbers
ea52b7d… drh 1360 ** -N|--new-file Alias for --verbose
df119a3… danield 1361 ** -s|--numstat Show the number of added and deleted lines per
7764287… drh 1362 ** file, omitting the diff. When combined
7764287… drh 1363 ** with --brief, show only the total row.
7f3c728… jamsek 1364 ** -y|--side-by-side Side-by-side diff
11384f1… drh 1365 ** --strip-trailing-cr Strip trailing CR
75c45fd… drh 1366 ** --tcl Tcl-formatted output used internally by --tk
b89f4cd… florian 1367 ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh")
11384f1… drh 1368 ** --tk Launch a Tcl/Tk GUI for display
11384f1… drh 1369 ** --to VERSION Select VERSION as target for the diff
99d0baa… drh 1370 ** --undo Use the undo buffer as the baseline
11384f1… drh 1371 ** --unified Unified diff
11384f1… drh 1372 ** -v|--verbose Output complete text of added or deleted files
41f6a45… danield 1373 ** -h|--versions Show compared versions in the diff header
ea52b7d… drh 1374 ** --webpage Format output as a stand-alone HTML webpage
11384f1… drh 1375 ** -W|--width N Width of lines in side-by-side diff
11384f1… drh 1376 ** -Z|--ignore-trailing-space Ignore changes to end-of-line whitespace
dbda8d6… drh 1377 */
dbda8d6… drh 1378 void diff_cmd(void){
85670cf… drh 1379 int isGDiff; /* True for gdiff. False for normal diff */
a51808c… drh 1380 const char *zFrom; /* Source version number */
a51808c… drh 1381 const char *zTo; /* Target version number */
e7c2454… drh 1382 const char *zCheckin; /* Check-in version number */
4140eb3… drh 1383 const char *zBranch; /* Branch to diff */
485aa80… drh 1384 int againstUndo = 0; /* Diff against files in the undo buffer */
c46f980… drh 1385 FileDirList *pFileDir = 0; /* Restrict the diff to these files */
1347a1d… drh 1386 DiffConfig DCfg; /* Diff configuration object */
763758d… drh 1387 int bFromIsDir = 0; /* True if zFrom is a directory name */
4140eb3… drh 1388
5bec7a6… drh 1389 isGDiff = g.argv[1][0]=='g';
5bec7a6… drh 1390 if( find_option("tk",0,0)!=0|| has_option("tclsh") ){
2ed8cdc… drh 1391 diff_tk("diff", 2);
22e5d71… drh 1392 return;
22e5d71… drh 1393 }
a51808c… drh 1394 zFrom = find_option("from", "r", 1);
a51808c… drh 1395 zTo = find_option("to", 0, 1);
c8a7ee7… danield 1396 zCheckin = find_option("checkin", "ci", 1);
9f83e03… drh 1397 zBranch = find_option("branch", 0, 1);
485aa80… drh 1398 againstUndo = find_option("undo",0,0)!=0;
99d0baa… drh 1399 if( againstUndo && (zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){
e7c2454… drh 1400 fossil_fatal("cannot use --undo together with --from, --to, --checkin,"
e7c2454… drh 1401 " or --branch");
e3f7ba5… jan.nijtmans 1402 }
9f83e03… drh 1403 if( zBranch ){
e7c2454… drh 1404 if( zTo || zFrom || zCheckin ){
e7c2454… drh 1405 fossil_fatal("cannot use --from, --to, or --checkin with --branch");
9f83e03… drh 1406 }
9f83e03… drh 1407 zTo = zBranch;
9f83e03… drh 1408 zFrom = mprintf("root:%s", zBranch);
5bec7a6… drh 1409 zBranch = 0;
e7c2454… drh 1410 }
99d0baa… drh 1411 if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){
e7c2454… drh 1412 fossil_fatal("cannot use --checkin together with --from or --to");
485aa80… drh 1413 }
8e8e026… stephan 1414 if( 0==zCheckin ){
8e8e026… stephan 1415 if( zTo==0 || againstUndo ){
8e8e026… stephan 1416 db_must_be_within_tree();
8e8e026… stephan 1417 }else if( zFrom==0 ){
8e8e026… stephan 1418 fossil_fatal("must use --from if --to is present");
8e8e026… stephan 1419 }else{
8e8e026… stephan 1420 db_find_and_open_repository(0, 0);
8e8e026… stephan 1421 }
c0c3d92… drh 1422 }else{
c0c3d92… drh 1423 db_find_and_open_repository(0, 0);
5bec7a6… drh 1424 }
5bec7a6… drh 1425 if( gdiff_using_tk(isGDiff) ){
5bec7a6… drh 1426 restore_option("--from", zFrom, 1);
5bec7a6… drh 1427 restore_option("--to", zTo, 1);
5bec7a6… drh 1428 restore_option("--checkin", zCheckin, 1);
5bec7a6… drh 1429 restore_option("--branch", zBranch, 1);
5bec7a6… drh 1430 if( againstUndo ) restore_option("--undo", 0, 0);
5bec7a6… drh 1431 diff_tk("diff", 2);
5bec7a6… drh 1432 return;
763758d… drh 1433 }
763758d… drh 1434 determine_exec_relative_option(1);
763758d… drh 1435 if( zFrom!=file_tail(zFrom)
763758d… drh 1436 && file_isdir(zFrom, ExtFILE)==1
763758d… drh 1437 && !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom)
763758d… drh 1438 ){
763758d… drh 1439 bFromIsDir = 1;
763758d… drh 1440 if( zTo ){
763758d… drh 1441 fossil_fatal("cannot use --to together with \"--from PATH\"");
763758d… drh 1442 }
763758d… drh 1443 }
763758d… drh 1444 diff_options(&DCfg, isGDiff, 0);
485aa80… drh 1445 verify_all_options();
a2a0390… drh 1446 g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0;
c46f980… drh 1447 if( g.argc>=3 ){
c46f980… drh 1448 int i;
c46f980… drh 1449 Blob fname;
c46f980… drh 1450 pFileDir = fossil_malloc( sizeof(*pFileDir) * (g.argc-1) );
c46f980… drh 1451 memset(pFileDir, 0, sizeof(*pFileDir) * (g.argc-1));
c46f980… drh 1452 for(i=2; i<g.argc; i++){
c46f980… drh 1453 file_tree_name(g.argv[i], &fname, 0, 1);
c46f980… drh 1454 pFileDir[i-2].zName = fossil_strdup(blob_str(&fname));
c46f980… drh 1455 if( strcmp(pFileDir[i-2].zName,".")==0 ){
c46f980… drh 1456 pFileDir[0].zName[0] = '.';
c46f980… drh 1457 pFileDir[0].zName[1] = 0;
c46f980… drh 1458 break;
c46f980… drh 1459 }
c46f980… drh 1460 pFileDir[i-2].nName = blob_size(&fname);
c46f980… drh 1461 pFileDir[i-2].nUsed = 0;
c46f980… drh 1462 blob_reset(&fname);
c46f980… drh 1463 }
c46f980… drh 1464 }
c14f217… danield 1465 if( (DCfg.diffFlags & DIFF_NUMSTAT) && !(DCfg.diffFlags & DIFF_BRIEF) ){
7bcd662… drh 1466 fossil_print("%10s %10s\n", "INSERTED", "DELETED");
7bcd662… drh 1467 }
7bcd662… drh 1468 if( zCheckin!=0 ){
e7c2454… drh 1469 int ridTo = name_to_typed_rid(zCheckin, "ci");
e7c2454… drh 1470 zTo = zCheckin;
e7c2454… drh 1471 zFrom = db_text(0,
e7c2454… drh 1472 "SELECT uuid FROM blob, plink"
e7c2454… drh 1473 " WHERE plink.cid=%d AND plink.isprim AND plink.pid=blob.rid",
e7c2454… drh 1474 ridTo);
e7c2454… drh 1475 if( zFrom==0 ){
e7c2454… drh 1476 fossil_fatal("check-in %s has no parent", zTo);
e7c2454… drh 1477 }
e7c2454… drh 1478 }
1347a1d… drh 1479 diff_begin(&DCfg);
763758d… drh 1480 if( bFromIsDir ){
763758d… drh 1481 diff_externbase_to_checkout(zFrom, &DCfg, pFileDir);
99d0baa… drh 1482 }else if( againstUndo ){
485aa80… drh 1483 if( db_lget_int("undo_available",0)==0 ){
485aa80… drh 1484 fossil_print("No undo or redo is available\n");
485aa80… drh 1485 return;
485aa80… drh 1486 }
99d0baa… drh 1487 diff_undo_to_checkout(&DCfg, pFileDir);
c46f980… drh 1488 }else if( zTo==0 ){
99d0baa… drh 1489 diff_version_to_checkout(zFrom, &DCfg, pFileDir, 0);
c46f980… drh 1490 }else{
346de5d… drh 1491 diff_two_versions(zFrom, zTo, &DCfg, pFileDir);
c46f980… drh 1492 }
c46f980… drh 1493 if( pFileDir ){
c46f980… drh 1494 int i;
c46f980… drh 1495 for(i=0; pFileDir[i].zName; i++){
b789df4… drh 1496 if( pFileDir[i].nUsed==0
b789df4… drh 1497 && strcmp(pFileDir[0].zName,".")!=0
1772357… drh 1498 && !file_isdir(g.argv[i+2], ExtFILE)
b789df4… drh 1499 ){
c46f980… drh 1500 fossil_fatal("not found: '%s'", g.argv[i+2]);
c46f980… drh 1501 }
c46f980… drh 1502 fossil_free(pFileDir[i].zName);
c46f980… drh 1503 }
c46f980… drh 1504 fossil_free(pFileDir);
1772357… drh 1505 }
1347a1d… drh 1506 diff_end(&DCfg, 0);
1347a1d… drh 1507 if ( DCfg.diffFlags & DIFF_NUMSTAT ){
3732790… danield 1508 fossil_print("%10d %10d TOTAL over %d changed file%s\n",
3732790… danield 1509 g.diffCnt[1], g.diffCnt[2], g.diffCnt[0], g.diffCnt[0]!=1 ? "s": "");
9bb61a4… drh 1510 }
4bdf71b… drh 1511 }
4bdf71b… drh 1512
4bdf71b… drh 1513 /*
4bdf71b… drh 1514 ** WEBPAGE: vpatch
7ab0328… drh 1515 ** URL: /vpatch?from=FROM&to=TO
7ab0328… drh 1516 **
7ab0328… drh 1517 ** Show a patch that goes from check-in FROM to check-in TO.
4bdf71b… drh 1518 */
4bdf71b… drh 1519 void vpatch_page(void){
4bdf71b… drh 1520 const char *zFrom = P("from");
4bdf71b… drh 1521 const char *zTo = P("to");
1347a1d… drh 1522 DiffConfig DCfg;
57f1e87… drh 1523 cgi_check_for_malice();
4bdf71b… drh 1524 login_check_credentials();
653dd40… drh 1525 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
4bdf71b… drh 1526 if( zFrom==0 || zTo==0 ) fossil_redirect_home();
16b3309… drh 1527 if( robot_restrict("diff") ) return;
4bdf71b… drh 1528
9413395… drh 1529 fossil_nice_default();
4bdf71b… drh 1530 cgi_set_content_type("text/plain");
1347a1d… drh 1531 diff_config_init(&DCfg, DIFF_VERBOSE);
346de5d… drh 1532 diff_two_versions(zFrom, zTo, &DCfg, 0);
dbda8d6… drh 1533 }

Keyboard Shortcuts

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