Fossil SCM

Enhance the "fossil merge-info --tk" application with a new menu that will bring up detailed two-way diffs between source components. This involves various diff subsystem upgrades, including: (1) Add the "fdiff" command. (Not actually needed here, but useful.) (2) Add --label options on diff commands. (3) Add --tkdebug on diff commands for debugging. (4) Improvements to column titles in two-way diffs.

drh 2025-02-20 13:36 trunk merge
Commit f5cb12df5a5c9858208dd9ca6eb861d893f87e416f658a039d72526f9b061b1b
+52 -3
--- src/diff.c
+++ src/diff.c
@@ -108,10 +108,11 @@
108108
u32 nFile; /* Number of files diffed so far */
109109
const char *zDiffCmd; /* External diff command to use instead of builtin */
110110
const char *zBinGlob; /* GLOB pattern for binary files */
111111
ReCompiled *pRe; /* Show only changes matching this pattern */
112112
const char *zLeftHash; /* HASH-id of the left file */
113
+ const char *azLabel[2]; /* Optional labels for left and right files */
113114
};
114115
115116
#endif /* INTERFACE */
116117
117118
/*
@@ -3236,10 +3237,11 @@
32363237
** --dark Use dark mode for Tcl/Tk and HTML output
32373238
** --html Format for HTML DIFF_HTML
32383239
** -i|--internal Use built-in diff, not an external tool
32393240
** --invert Invert the diff DIFF_INVERT
32403241
** --json Output formatted as JSON
3242
+** --label NAME Column label. Can be repeated once.
32413243
** -n|--linenum Show line numbers DIFF_LINENO
32423244
** -N|--new-file Alias for --verbose
32433245
** --noopt Disable optimization DIFF_NOOPT
32443246
** --numstat Show change counts DIFF_NUMSTAT
32453247
** --strip-trailing-cr Strip trailing CR DIFF_STRIP_EOLCR
@@ -3308,10 +3310,14 @@
33083310
}
33093311
}
33103312
if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){
33113313
pCfg->wColumn = f;
33123314
}
3315
+ pCfg->azLabel[0] = find_option("label",0,1);
3316
+ if( pCfg->azLabel[0] ){
3317
+ pCfg->azLabel[1] = find_option("label",0,1);
3318
+ }
33133319
if( find_option("linenum","n",0)!=0 ) diffFlags |= DIFF_LINENO;
33143320
if( find_option("noopt",0,0)!=0 ) diffFlags |= DIFF_NOOPT;
33153321
if( find_option("numstat",0,0)!=0 ) diffFlags |= DIFF_NUMSTAT;
33163322
if( find_option("versions","h",0)!=0 ) diffFlags |= DIFF_SHOW_VERS;
33173323
if( find_option("dark",0,0)!=0 ) diffFlags |= DIFF_DARKMODE;
@@ -3353,13 +3359,10 @@
33533359
** computing differences between files that are under management.
33543360
**
33553361
** This command prints the differences between the two files FILE1 and FILE2.
33563362
** all of the usual diff formatting options (--tk, --by, -c N, etc.) apply.
33573363
** See the "diff" command for a full list of command-line options.
3358
-**
3359
-** This command used to be called "test-diff". The older "test-diff" spelling
3360
-** still works, for compatibility.
33613364
*/
33623365
void xdiff_cmd(void){
33633366
Blob a, b, out;
33643367
const char *zRe; /* Regex filter for diff output */
33653368
DiffConfig DCfg;
@@ -3381,10 +3384,56 @@
33813384
blob_zero(&out);
33823385
diff_begin(&DCfg);
33833386
diff_print_filenames(g.argv[2], g.argv[3], &DCfg, &out);
33843387
blob_read_from_file(&a, g.argv[2], ExtFILE);
33853388
blob_read_from_file(&b, g.argv[3], ExtFILE);
3389
+ text_diff(&a, &b, &out, &DCfg);
3390
+ blob_write_to_file(&out, "-");
3391
+ diff_end(&DCfg, 0);
3392
+ re_free(DCfg.pRe);
3393
+}
3394
+
3395
+/*
3396
+** COMMAND: fdiff
3397
+**
3398
+** Usage: %fossil fdiff [options] HASH1 HASH2
3399
+**
3400
+** Compute a diff between two artifacts in a repository (either a repository
3401
+** identified by the "-R FILENAME" option, or the repository that contains
3402
+** the working directory).
3403
+**
3404
+** All of the usual diff formatting options (--tk, --by, -c N, etc.) apply.
3405
+** See the "diff" command for a full list of command-line options.
3406
+*/
3407
+void fdiff_cmd(void){
3408
+ Blob a, b, out;
3409
+ const char *zRe; /* Regex filter for diff output */
3410
+ int rid;
3411
+ DiffConfig DCfg;
3412
+
3413
+ if( find_option("tk",0,0)!=0 ){
3414
+ diff_tk("fdiff", 2);
3415
+ return;
3416
+ }
3417
+ find_option("i",0,0);
3418
+ find_option("v",0,0);
3419
+ diff_options(&DCfg, 0, 0);
3420
+ zRe = find_option("regexp","e",1);
3421
+ if( zRe ){
3422
+ const char *zErr = re_compile(&DCfg.pRe, zRe, 0);
3423
+ if( zErr ) fossil_fatal("regex error: %s", zErr);
3424
+ }
3425
+ db_find_and_open_repository(0, 0);
3426
+ verify_all_options();
3427
+ if( g.argc!=4 ) usage("HASH1 HASH2");
3428
+ blob_zero(&out);
3429
+ diff_begin(&DCfg);
3430
+ diff_print_filenames(g.argv[2], g.argv[3], &DCfg, &out);
3431
+ rid = name_to_typed_rid(g.argv[2], 0);
3432
+ content_get(rid, &a);
3433
+ rid = name_to_typed_rid(g.argv[3], 0);
3434
+ content_get(rid, &b);
33863435
text_diff(&a, &b, &out, &DCfg);
33873436
blob_write_to_file(&out, "-");
33883437
diff_end(&DCfg, 0);
33893438
re_free(DCfg.pRe);
33903439
}
33913440
--- src/diff.c
+++ src/diff.c
@@ -108,10 +108,11 @@
108 u32 nFile; /* Number of files diffed so far */
109 const char *zDiffCmd; /* External diff command to use instead of builtin */
110 const char *zBinGlob; /* GLOB pattern for binary files */
111 ReCompiled *pRe; /* Show only changes matching this pattern */
112 const char *zLeftHash; /* HASH-id of the left file */
 
113 };
114
115 #endif /* INTERFACE */
116
117 /*
@@ -3236,10 +3237,11 @@
3236 ** --dark Use dark mode for Tcl/Tk and HTML output
3237 ** --html Format for HTML DIFF_HTML
3238 ** -i|--internal Use built-in diff, not an external tool
3239 ** --invert Invert the diff DIFF_INVERT
3240 ** --json Output formatted as JSON
 
3241 ** -n|--linenum Show line numbers DIFF_LINENO
3242 ** -N|--new-file Alias for --verbose
3243 ** --noopt Disable optimization DIFF_NOOPT
3244 ** --numstat Show change counts DIFF_NUMSTAT
3245 ** --strip-trailing-cr Strip trailing CR DIFF_STRIP_EOLCR
@@ -3308,10 +3310,14 @@
3308 }
3309 }
3310 if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){
3311 pCfg->wColumn = f;
3312 }
 
 
 
 
3313 if( find_option("linenum","n",0)!=0 ) diffFlags |= DIFF_LINENO;
3314 if( find_option("noopt",0,0)!=0 ) diffFlags |= DIFF_NOOPT;
3315 if( find_option("numstat",0,0)!=0 ) diffFlags |= DIFF_NUMSTAT;
3316 if( find_option("versions","h",0)!=0 ) diffFlags |= DIFF_SHOW_VERS;
3317 if( find_option("dark",0,0)!=0 ) diffFlags |= DIFF_DARKMODE;
@@ -3353,13 +3359,10 @@
3353 ** computing differences between files that are under management.
3354 **
3355 ** This command prints the differences between the two files FILE1 and FILE2.
3356 ** all of the usual diff formatting options (--tk, --by, -c N, etc.) apply.
3357 ** See the "diff" command for a full list of command-line options.
3358 **
3359 ** This command used to be called "test-diff". The older "test-diff" spelling
3360 ** still works, for compatibility.
3361 */
3362 void xdiff_cmd(void){
3363 Blob a, b, out;
3364 const char *zRe; /* Regex filter for diff output */
3365 DiffConfig DCfg;
@@ -3381,10 +3384,56 @@
3381 blob_zero(&out);
3382 diff_begin(&DCfg);
3383 diff_print_filenames(g.argv[2], g.argv[3], &DCfg, &out);
3384 blob_read_from_file(&a, g.argv[2], ExtFILE);
3385 blob_read_from_file(&b, g.argv[3], ExtFILE);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3386 text_diff(&a, &b, &out, &DCfg);
3387 blob_write_to_file(&out, "-");
3388 diff_end(&DCfg, 0);
3389 re_free(DCfg.pRe);
3390 }
3391
--- src/diff.c
+++ src/diff.c
@@ -108,10 +108,11 @@
108 u32 nFile; /* Number of files diffed so far */
109 const char *zDiffCmd; /* External diff command to use instead of builtin */
110 const char *zBinGlob; /* GLOB pattern for binary files */
111 ReCompiled *pRe; /* Show only changes matching this pattern */
112 const char *zLeftHash; /* HASH-id of the left file */
113 const char *azLabel[2]; /* Optional labels for left and right files */
114 };
115
116 #endif /* INTERFACE */
117
118 /*
@@ -3236,10 +3237,11 @@
3237 ** --dark Use dark mode for Tcl/Tk and HTML output
3238 ** --html Format for HTML DIFF_HTML
3239 ** -i|--internal Use built-in diff, not an external tool
3240 ** --invert Invert the diff DIFF_INVERT
3241 ** --json Output formatted as JSON
3242 ** --label NAME Column label. Can be repeated once.
3243 ** -n|--linenum Show line numbers DIFF_LINENO
3244 ** -N|--new-file Alias for --verbose
3245 ** --noopt Disable optimization DIFF_NOOPT
3246 ** --numstat Show change counts DIFF_NUMSTAT
3247 ** --strip-trailing-cr Strip trailing CR DIFF_STRIP_EOLCR
@@ -3308,10 +3310,14 @@
3310 }
3311 }
3312 if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){
3313 pCfg->wColumn = f;
3314 }
3315 pCfg->azLabel[0] = find_option("label",0,1);
3316 if( pCfg->azLabel[0] ){
3317 pCfg->azLabel[1] = find_option("label",0,1);
3318 }
3319 if( find_option("linenum","n",0)!=0 ) diffFlags |= DIFF_LINENO;
3320 if( find_option("noopt",0,0)!=0 ) diffFlags |= DIFF_NOOPT;
3321 if( find_option("numstat",0,0)!=0 ) diffFlags |= DIFF_NUMSTAT;
3322 if( find_option("versions","h",0)!=0 ) diffFlags |= DIFF_SHOW_VERS;
3323 if( find_option("dark",0,0)!=0 ) diffFlags |= DIFF_DARKMODE;
@@ -3353,13 +3359,10 @@
3359 ** computing differences between files that are under management.
3360 **
3361 ** This command prints the differences between the two files FILE1 and FILE2.
3362 ** all of the usual diff formatting options (--tk, --by, -c N, etc.) apply.
3363 ** See the "diff" command for a full list of command-line options.
 
 
 
3364 */
3365 void xdiff_cmd(void){
3366 Blob a, b, out;
3367 const char *zRe; /* Regex filter for diff output */
3368 DiffConfig DCfg;
@@ -3381,10 +3384,56 @@
3384 blob_zero(&out);
3385 diff_begin(&DCfg);
3386 diff_print_filenames(g.argv[2], g.argv[3], &DCfg, &out);
3387 blob_read_from_file(&a, g.argv[2], ExtFILE);
3388 blob_read_from_file(&b, g.argv[3], ExtFILE);
3389 text_diff(&a, &b, &out, &DCfg);
3390 blob_write_to_file(&out, "-");
3391 diff_end(&DCfg, 0);
3392 re_free(DCfg.pRe);
3393 }
3394
3395 /*
3396 ** COMMAND: fdiff
3397 **
3398 ** Usage: %fossil fdiff [options] HASH1 HASH2
3399 **
3400 ** Compute a diff between two artifacts in a repository (either a repository
3401 ** identified by the "-R FILENAME" option, or the repository that contains
3402 ** the working directory).
3403 **
3404 ** All of the usual diff formatting options (--tk, --by, -c N, etc.) apply.
3405 ** See the "diff" command for a full list of command-line options.
3406 */
3407 void fdiff_cmd(void){
3408 Blob a, b, out;
3409 const char *zRe; /* Regex filter for diff output */
3410 int rid;
3411 DiffConfig DCfg;
3412
3413 if( find_option("tk",0,0)!=0 ){
3414 diff_tk("fdiff", 2);
3415 return;
3416 }
3417 find_option("i",0,0);
3418 find_option("v",0,0);
3419 diff_options(&DCfg, 0, 0);
3420 zRe = find_option("regexp","e",1);
3421 if( zRe ){
3422 const char *zErr = re_compile(&DCfg.pRe, zRe, 0);
3423 if( zErr ) fossil_fatal("regex error: %s", zErr);
3424 }
3425 db_find_and_open_repository(0, 0);
3426 verify_all_options();
3427 if( g.argc!=4 ) usage("HASH1 HASH2");
3428 blob_zero(&out);
3429 diff_begin(&DCfg);
3430 diff_print_filenames(g.argv[2], g.argv[3], &DCfg, &out);
3431 rid = name_to_typed_rid(g.argv[2], 0);
3432 content_get(rid, &a);
3433 rid = name_to_typed_rid(g.argv[3], 0);
3434 content_get(rid, &b);
3435 text_diff(&a, &b, &out, &DCfg);
3436 blob_write_to_file(&out, "-");
3437 diff_end(&DCfg, 0);
3438 re_free(DCfg.pRe);
3439 }
3440
+39 -13
--- src/diff.tcl
+++ src/diff.tcl
@@ -91,12 +91,16 @@
9191
incr ii
9292
return $x
9393
}
9494
9595
proc readDiffs {fossilcmd} {
96
- global difftxt
96
+ global difftxt debug
9797
if {![info exists difftxt]} {
98
+ if {$debug} {
99
+ puts "# [list open $fossilcmd r]"
100
+ flush stdout
101
+ }
98102
set in [open $fossilcmd r]
99103
fconfigure $in -encoding utf-8
100104
set difftxt [split [read $in] \n]
101105
close $in
102106
}
@@ -110,34 +114,56 @@
110114
111115
set fromIndex [lsearch -glob $fossilcmd *-from]
112116
set toIndex [lsearch -glob $fossilcmd *-to]
113117
set branchIndex [lsearch -glob $fossilcmd *-branch]
114118
set checkinIndex [lsearch -glob $fossilcmd *-checkin]
115
- if {[string match *?--external-baseline* $fossilcmd]} {
116
- set fA {external baseline}
119
+ if {[lsearch -glob $fossilcmd *-label]>=0
120
+ || [lsearch {xdiff fdiff merge-info} [lindex $fossilcmd 2]]>=0
121
+ } {
122
+ set fA {}
123
+ set fB {}
117124
} else {
118
- set fA {base check-in}
119
- }
120
- set fB {current check-out}
121
- if {$fromIndex > -1} {set fA [lindex $fossilcmd $fromIndex+1]}
122
- if {$toIndex > -1} {set fB [lindex $fossilcmd $toIndex+1]}
123
- if {$branchIndex > -1} {set fA "branch point"; set fB "leaf of branch '[lindex $fossilcmd $branchIndex+1]'"}
124
- if {$checkinIndex > -1} {set fA "primary parent"; set fB [lindex $fossilcmd $checkinIndex+1]}
125
-
125
+ if {[string match *?--external-baseline* $fossilcmd]} {
126
+ set fA {external baseline}
127
+ } else {
128
+ set fA {base check-in}
129
+ }
130
+ set fB {current check-out}
131
+ if {$fromIndex > -1} {
132
+ set fA [lindex $fossilcmd $fromIndex+1]
133
+ }
134
+ if {$toIndex > -1} {
135
+ set fB [lindex $fossilcmd $toIndex+1]
136
+ }
137
+ if {$branchIndex > -1} {
138
+ set fA "branch point"; set fB "leaf of branch '[lindex $fossilcmd $branchIndex+1]'"
139
+ }
140
+ if {$checkinIndex > -1} {
141
+ set fA "primary parent"; set fB [lindex $fossilcmd $checkinIndex+1]
142
+ }
143
+ }
126144
127145
while {[set line [getLine $difftxt $N ii]] != -1} {
128146
switch -- [lindex $line 0] {
129147
FILE {
130148
incr nDiffs
131149
foreach wx [list [string length $n1] [string length $n2]] {
132150
if {$wx>$widths(ln)} {set widths(ln) $wx}
133151
}
134152
.lnA insert end \n fn \n -
135
- .txtA insert end "[lindex $line 1] ($fA)\n" fn \n -
153
+ if {$fA==""} {
154
+ .txtA insert end "[lindex $line 1]\n" fn \n -
155
+ } else {
156
+ .txtA insert end "[lindex $line 1] ($fA)\n" fn \n -
157
+ }
136158
.mkr insert end \n fn \n -
137159
.lnB insert end \n fn \n -
138
- .txtB insert end "[lindex $line 2] ($fB)\n" fn \n -
160
+ if {$fB==""} {
161
+ .txtB insert end "[lindex $line 2]\n" fn \n -
162
+ } else {
163
+ .txtB insert end "[lindex $line 2] ($fB)\n" fn \n -
164
+ }
139165
.wfiles.lb insert end [lindex $line 2]
140166
set n1 0
141167
set n2 0
142168
}
143169
SKIP {
144170
--- src/diff.tcl
+++ src/diff.tcl
@@ -91,12 +91,16 @@
91 incr ii
92 return $x
93 }
94
95 proc readDiffs {fossilcmd} {
96 global difftxt
97 if {![info exists difftxt]} {
 
 
 
 
98 set in [open $fossilcmd r]
99 fconfigure $in -encoding utf-8
100 set difftxt [split [read $in] \n]
101 close $in
102 }
@@ -110,34 +114,56 @@
110
111 set fromIndex [lsearch -glob $fossilcmd *-from]
112 set toIndex [lsearch -glob $fossilcmd *-to]
113 set branchIndex [lsearch -glob $fossilcmd *-branch]
114 set checkinIndex [lsearch -glob $fossilcmd *-checkin]
115 if {[string match *?--external-baseline* $fossilcmd]} {
116 set fA {external baseline}
 
 
 
117 } else {
118 set fA {base check-in}
119 }
120 set fB {current check-out}
121 if {$fromIndex > -1} {set fA [lindex $fossilcmd $fromIndex+1]}
122 if {$toIndex > -1} {set fB [lindex $fossilcmd $toIndex+1]}
123 if {$branchIndex > -1} {set fA "branch point"; set fB "leaf of branch '[lindex $fossilcmd $branchIndex+1]'"}
124 if {$checkinIndex > -1} {set fA "primary parent"; set fB [lindex $fossilcmd $checkinIndex+1]}
125
 
 
 
 
 
 
 
 
 
 
 
126
127 while {[set line [getLine $difftxt $N ii]] != -1} {
128 switch -- [lindex $line 0] {
129 FILE {
130 incr nDiffs
131 foreach wx [list [string length $n1] [string length $n2]] {
132 if {$wx>$widths(ln)} {set widths(ln) $wx}
133 }
134 .lnA insert end \n fn \n -
135 .txtA insert end "[lindex $line 1] ($fA)\n" fn \n -
 
 
 
 
136 .mkr insert end \n fn \n -
137 .lnB insert end \n fn \n -
138 .txtB insert end "[lindex $line 2] ($fB)\n" fn \n -
 
 
 
 
139 .wfiles.lb insert end [lindex $line 2]
140 set n1 0
141 set n2 0
142 }
143 SKIP {
144
--- src/diff.tcl
+++ src/diff.tcl
@@ -91,12 +91,16 @@
91 incr ii
92 return $x
93 }
94
95 proc readDiffs {fossilcmd} {
96 global difftxt debug
97 if {![info exists difftxt]} {
98 if {$debug} {
99 puts "# [list open $fossilcmd r]"
100 flush stdout
101 }
102 set in [open $fossilcmd r]
103 fconfigure $in -encoding utf-8
104 set difftxt [split [read $in] \n]
105 close $in
106 }
@@ -110,34 +114,56 @@
114
115 set fromIndex [lsearch -glob $fossilcmd *-from]
116 set toIndex [lsearch -glob $fossilcmd *-to]
117 set branchIndex [lsearch -glob $fossilcmd *-branch]
118 set checkinIndex [lsearch -glob $fossilcmd *-checkin]
119 if {[lsearch -glob $fossilcmd *-label]>=0
120 || [lsearch {xdiff fdiff merge-info} [lindex $fossilcmd 2]]>=0
121 } {
122 set fA {}
123 set fB {}
124 } else {
125 if {[string match *?--external-baseline* $fossilcmd]} {
126 set fA {external baseline}
127 } else {
128 set fA {base check-in}
129 }
130 set fB {current check-out}
131 if {$fromIndex > -1} {
132 set fA [lindex $fossilcmd $fromIndex+1]
133 }
134 if {$toIndex > -1} {
135 set fB [lindex $fossilcmd $toIndex+1]
136 }
137 if {$branchIndex > -1} {
138 set fA "branch point"; set fB "leaf of branch '[lindex $fossilcmd $branchIndex+1]'"
139 }
140 if {$checkinIndex > -1} {
141 set fA "primary parent"; set fB [lindex $fossilcmd $checkinIndex+1]
142 }
143 }
144
145 while {[set line [getLine $difftxt $N ii]] != -1} {
146 switch -- [lindex $line 0] {
147 FILE {
148 incr nDiffs
149 foreach wx [list [string length $n1] [string length $n2]] {
150 if {$wx>$widths(ln)} {set widths(ln) $wx}
151 }
152 .lnA insert end \n fn \n -
153 if {$fA==""} {
154 .txtA insert end "[lindex $line 1]\n" fn \n -
155 } else {
156 .txtA insert end "[lindex $line 1] ($fA)\n" fn \n -
157 }
158 .mkr insert end \n fn \n -
159 .lnB insert end \n fn \n -
160 if {$fB==""} {
161 .txtB insert end "[lindex $line 2]\n" fn \n -
162 } else {
163 .txtB insert end "[lindex $line 2] ($fB)\n" fn \n -
164 }
165 .wfiles.lb insert end [lindex $line 2]
166 set n1 0
167 set n2 0
168 }
169 SKIP {
170
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -164,10 +164,12 @@
164164
){
165165
u64 diffFlags = pCfg->diffFlags;
166166
/* Standardize on /dev/null, regardless of platform. */
167167
if( pCfg->diffFlags & DIFF_FILE_ADDED ) zLeft = "/dev/null";
168168
if( pCfg->diffFlags & DIFF_FILE_DELETED ) zRight = "/dev/null";
169
+ if( pCfg->azLabel[0] ) zLeft = pCfg->azLabel[0];
170
+ if( pCfg->azLabel[1] ) zRight = pCfg->azLabel[1];
169171
if( diffFlags & (DIFF_BRIEF|DIFF_RAW) ){
170172
/* no-op */
171173
}else if( diffFlags & DIFF_DEBUG ){
172174
blob_appendf(pOut, "FILE-LEFT %s\nFILE-RIGHT %s\n", zLeft, zRight);
173175
}else if( diffFlags & DIFF_WEBPAGE ){
@@ -1186,12 +1188,19 @@
11861188
int i;
11871189
Blob script;
11881190
const char *zTempFile = 0;
11891191
char *zCmd;
11901192
const char *zTclsh;
1193
+ int bDebug = find_option("tkdebug",0,0)!=0;
11911194
int bDarkMode = find_option("dark",0,0)!=0;
11921195
blob_zero(&script);
1196
+ /* Caution: When this routine is called from the merge-info command,
1197
+ ** the --tcl argument requires an argument. But merge-info does not
1198
+ ** use -i, so we can take -i as that argument. This routine needs to
1199
+ ** always have -i after --tcl.
1200
+ ** CAUTION!
1201
+ ** vvvvvvv */
11931202
blob_appendf(&script, "set fossilcmd {| \"%/\" %s -tcl -i -v",
11941203
g.nameOfExe, zSubCmd);
11951204
find_option("tcl",0,0);
11961205
find_option("html",0,0);
11971206
find_option("side-by-side","y",0);
@@ -1214,10 +1223,11 @@
12141223
blob_append(&script, " ", 1);
12151224
for(j=0; z[j]; j++) blob_appendf(&script, "\\%03o", (unsigned char)z[j]);
12161225
}
12171226
}
12181227
blob_appendf(&script, "}\nset darkmode %d\n", bDarkMode);
1228
+ blob_appendf(&script, "set debug %d\n", bDebug);
12191229
blob_appendf(&script, "%s", builtin_file("diff.tcl", 0));
12201230
if( zTempFile ){
12211231
blob_write_to_file(&script, zTempFile);
12221232
fossil_print("To see diff, run: %s \"%s\"\n", zTclsh, zTempFile);
12231233
}else{
12241234
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -164,10 +164,12 @@
164 ){
165 u64 diffFlags = pCfg->diffFlags;
166 /* Standardize on /dev/null, regardless of platform. */
167 if( pCfg->diffFlags & DIFF_FILE_ADDED ) zLeft = "/dev/null";
168 if( pCfg->diffFlags & DIFF_FILE_DELETED ) zRight = "/dev/null";
 
 
169 if( diffFlags & (DIFF_BRIEF|DIFF_RAW) ){
170 /* no-op */
171 }else if( diffFlags & DIFF_DEBUG ){
172 blob_appendf(pOut, "FILE-LEFT %s\nFILE-RIGHT %s\n", zLeft, zRight);
173 }else if( diffFlags & DIFF_WEBPAGE ){
@@ -1186,12 +1188,19 @@
1186 int i;
1187 Blob script;
1188 const char *zTempFile = 0;
1189 char *zCmd;
1190 const char *zTclsh;
 
1191 int bDarkMode = find_option("dark",0,0)!=0;
1192 blob_zero(&script);
 
 
 
 
 
 
1193 blob_appendf(&script, "set fossilcmd {| \"%/\" %s -tcl -i -v",
1194 g.nameOfExe, zSubCmd);
1195 find_option("tcl",0,0);
1196 find_option("html",0,0);
1197 find_option("side-by-side","y",0);
@@ -1214,10 +1223,11 @@
1214 blob_append(&script, " ", 1);
1215 for(j=0; z[j]; j++) blob_appendf(&script, "\\%03o", (unsigned char)z[j]);
1216 }
1217 }
1218 blob_appendf(&script, "}\nset darkmode %d\n", bDarkMode);
 
1219 blob_appendf(&script, "%s", builtin_file("diff.tcl", 0));
1220 if( zTempFile ){
1221 blob_write_to_file(&script, zTempFile);
1222 fossil_print("To see diff, run: %s \"%s\"\n", zTclsh, zTempFile);
1223 }else{
1224
--- src/diffcmd.c
+++ src/diffcmd.c
@@ -164,10 +164,12 @@
164 ){
165 u64 diffFlags = pCfg->diffFlags;
166 /* Standardize on /dev/null, regardless of platform. */
167 if( pCfg->diffFlags & DIFF_FILE_ADDED ) zLeft = "/dev/null";
168 if( pCfg->diffFlags & DIFF_FILE_DELETED ) zRight = "/dev/null";
169 if( pCfg->azLabel[0] ) zLeft = pCfg->azLabel[0];
170 if( pCfg->azLabel[1] ) zRight = pCfg->azLabel[1];
171 if( diffFlags & (DIFF_BRIEF|DIFF_RAW) ){
172 /* no-op */
173 }else if( diffFlags & DIFF_DEBUG ){
174 blob_appendf(pOut, "FILE-LEFT %s\nFILE-RIGHT %s\n", zLeft, zRight);
175 }else if( diffFlags & DIFF_WEBPAGE ){
@@ -1186,12 +1188,19 @@
1188 int i;
1189 Blob script;
1190 const char *zTempFile = 0;
1191 char *zCmd;
1192 const char *zTclsh;
1193 int bDebug = find_option("tkdebug",0,0)!=0;
1194 int bDarkMode = find_option("dark",0,0)!=0;
1195 blob_zero(&script);
1196 /* Caution: When this routine is called from the merge-info command,
1197 ** the --tcl argument requires an argument. But merge-info does not
1198 ** use -i, so we can take -i as that argument. This routine needs to
1199 ** always have -i after --tcl.
1200 ** CAUTION!
1201 ** vvvvvvv */
1202 blob_appendf(&script, "set fossilcmd {| \"%/\" %s -tcl -i -v",
1203 g.nameOfExe, zSubCmd);
1204 find_option("tcl",0,0);
1205 find_option("html",0,0);
1206 find_option("side-by-side","y",0);
@@ -1214,10 +1223,11 @@
1223 blob_append(&script, " ", 1);
1224 for(j=0; z[j]; j++) blob_appendf(&script, "\\%03o", (unsigned char)z[j]);
1225 }
1226 }
1227 blob_appendf(&script, "}\nset darkmode %d\n", bDarkMode);
1228 blob_appendf(&script, "set debug %d\n", bDebug);
1229 blob_appendf(&script, "%s", builtin_file("diff.tcl", 0));
1230 if( zTempFile ){
1231 blob_write_to_file(&script, zTempFile);
1232 fossil_print("To see diff, run: %s \"%s\"\n", zTclsh, zTempFile);
1233 }else{
1234
+155 -86
--- src/merge.c
+++ src/merge.c
@@ -39,15 +39,16 @@
3939
}
4040
/* The undocumented --script FILENAME option causes the Tk script to
4141
** be written into the FILENAME instead of being run. This is used
4242
** for testing and debugging. */
4343
zTempFile = find_option("script",0,1);
44
- bDebug = find_option("debug",0,0)!=0;
44
+ bDebug = find_option("tkdebug",0,0)!=0;
4545
verify_all_options();
4646
4747
blob_zero(&script);
4848
blob_appendf(&script, "set ncontext %d\n", nContext);
49
+ blob_appendf(&script, "set fossilexe {\"%/\"}\n", g.nameOfExe);
4950
blob_appendf(&script, "set fossilcmd {| \"%/\" merge-info}\n",
5051
g.nameOfExe);
5152
blob_appendf(&script, "set filelist [list");
5253
if( g.argc==2 ){
5354
/* No files named on the command-line. Use every file mentioned
@@ -136,12 +137,19 @@
136137
** command associated with file "zFName". zFName must be the filename
137138
** relative to the root of the check-in - in other words a "tree name".
138139
**
139140
** When this routine is called, we know that the mergestat table
140141
** exists, but we do not know if zFName is mentioned in that table.
142
+**
143
+** The diffMode variable has these values:
144
+**
145
+** 0 Standard 3-way diff
146
+** 12 2-way diff between baseline and local
147
+** 13 2-way diff between baseline and merge-in
148
+** 23 2-way diff between local and merge-in
141149
*/
142
-static void merge_info_tcl(const char *zFName, int nContext){
150
+static void merge_info_tcl(const char *zFName, int nContext, int diffMode){
143151
const char *zTreename;/* Name of the file in the tree */
144152
Stmt q; /* To query the MERGESTAT table */
145153
MergeBuilder mb; /* The merge builder object */
146154
Blob pivot,v1,v2,out; /* Blobs for holding content */
147155
const char *zFN; /* A filename */
@@ -162,82 +170,117 @@
162170
return;
163171
}
164172
mergebuilder_init_tcl(&mb);
165173
mb.nContext = nContext;
166174
167
- /* Set up the pivot */
168
- zFN = db_column_text(&q, 0);
169
- if( zFN==0 ){
170
- /* No pivot because the file was added */
171
- mb.zPivot = "(no baseline)";
172
- blob_zero(&pivot);
173
- }else{
174
- mb.zPivot = mprintf("%s (baseline)", file_tail(zFN));
175
- rid = db_column_int(&q, 1);
176
- content_get(rid, &pivot);
177
- }
178
- mb.pPivot = &pivot;
179
-
180
- /* Set up the merge-in as V2 */
181
- zFN = db_column_text(&q, 5);
182
- if( zFN==0 ){
183
- /* File deleted in the merged-in branch */
184
- mb.zV2 = "(deleted file)";
185
- blob_zero(&v2);
186
- }else{
187
- mb.zV2 = mprintf("%s (merge-in)", file_tail(zFN));
188
- rid = db_column_int(&q, 6);
189
- content_get(rid, &v2);
190
- }
191
- mb.pV2 = &v2;
192
-
193
- /* Set up the local content as V1 */
194
- zFN = db_column_text(&q, 2);
195
- if( zFN==0 ){
196
- /* File added by merge */
197
- mb.zV1 = "(no original)";
198
- blob_zero(&v1);
199
- }else{
200
- mb.zV1 = mprintf("%s (local)", file_tail(zFN));
201
- rid = db_column_int(&q, 3);
202
- sz = db_column_int(&q, 4);
203
- if( rid==0 && sz>0 ){
204
- /* The origin file had been edited so we'll have to pull its
205
- ** original content out of the undo buffer */
206
- Stmt q2;
207
- db_prepare(&q2,
208
- "SELECT content FROM undo"
209
- " WHERE pathname=%Q AND octet_length(content)=%d",
210
- zFN, sz
211
- );
212
- blob_zero(&v1);
213
- if( db_step(&q2)==SQLITE_ROW ){
214
- db_column_blob(&q2, 0, &v1);
215
- }else{
216
- mb.zV1 = "(local content missing)";
217
- }
218
- db_finalize(&q2);
219
- }else{
220
- /* The origin file was unchanged when the merge first occurred */
221
- content_get(rid, &v1);
222
- }
223
- }
224
- mb.pV1 = &v1;
225
-
226
- /* Set up the output */
227
- zFN = db_column_text(&q, 7);
228
- if( zFN==0 ){
229
- mb.zOut = "(Merge Result)";
230
- }else{
231
- mb.zOut = mprintf("%s (after merge)", file_tail(zFN));
232
- }
233
- blob_zero(&out);
234
- mb.pOut = &out;
235
-
236
- merge_three_blobs(&mb);
237
- blob_write_to_file(&out, "-");
238
-
175
+ blob_zero(&pivot);
176
+ if( diffMode!=23 ){
177
+ /* Set up the pivot or baseline */
178
+ zFN = db_column_text(&q, 0);
179
+ if( zFN==0 ){
180
+ /* No pivot because the file was added */
181
+ mb.zPivot = "(no baseline)";
182
+ }else{
183
+ mb.zPivot = mprintf("%s (baseline)", file_tail(zFN));
184
+ rid = db_column_int(&q, 1);
185
+ content_get(rid, &pivot);
186
+ }
187
+ mb.pPivot = &pivot;
188
+ }
189
+
190
+ blob_zero(&v2);
191
+ if( diffMode!=12 ){
192
+ /* Set up the merge-in as V2 */
193
+ zFN = db_column_text(&q, 5);
194
+ if( zFN==0 ){
195
+ /* File deleted in the merged-in branch */
196
+ mb.zV2 = "(deleted file)";
197
+ }else{
198
+ mb.zV2 = mprintf("%s (merge-in)", file_tail(zFN));
199
+ rid = db_column_int(&q, 6);
200
+ content_get(rid, &v2);
201
+ }
202
+ mb.pV2 = &v2;
203
+ }
204
+
205
+ blob_zero(&v1);
206
+ if( diffMode!=13 ){
207
+ /* Set up the local content as V1 */
208
+ zFN = db_column_text(&q, 2);
209
+ if( zFN==0 ){
210
+ /* File added by merge */
211
+ mb.zV1 = "(no original)";
212
+ }else{
213
+ mb.zV1 = mprintf("%s (local)", file_tail(zFN));
214
+ rid = db_column_int(&q, 3);
215
+ sz = db_column_int(&q, 4);
216
+ if( rid==0 && sz>0 ){
217
+ /* The origin file had been edited so we'll have to pull its
218
+ ** original content out of the undo buffer */
219
+ Stmt q2;
220
+ db_prepare(&q2,
221
+ "SELECT content FROM undo"
222
+ " WHERE pathname=%Q AND octet_length(content)=%d",
223
+ zFN, sz
224
+ );
225
+ blob_zero(&v1);
226
+ if( db_step(&q2)==SQLITE_ROW ){
227
+ db_column_blob(&q2, 0, &v1);
228
+ }else{
229
+ mb.zV1 = "(local content missing)";
230
+ }
231
+ db_finalize(&q2);
232
+ }else{
233
+ /* The origin file was unchanged when the merge first occurred */
234
+ content_get(rid, &v1);
235
+ }
236
+ }
237
+ mb.pV1 = &v1;
238
+ }
239
+
240
+ blob_zero(&out);
241
+ if( diffMode==0 ){
242
+ /* Set up the output and do a 3-way diff */
243
+ zFN = db_column_text(&q, 7);
244
+ if( zFN==0 ){
245
+ mb.zOut = "(Merge Result)";
246
+ }else{
247
+ mb.zOut = mprintf("%s (after merge)", file_tail(zFN));
248
+ }
249
+ mb.pOut = &out;
250
+ merge_three_blobs(&mb);
251
+ }else{
252
+ /* Set up to do a two-way diff */
253
+ Blob *pLeft, *pRight;
254
+ const char *zTagLeft, *zTagRight;
255
+ DiffConfig cfg;
256
+ memset(&cfg, 0, sizeof(cfg));
257
+ cfg.diffFlags = DIFF_TCL;
258
+ cfg.nContext = mb.nContext;
259
+ if( diffMode==12 || diffMode==13 ){
260
+ pLeft = &pivot;
261
+ zTagLeft = "baseline";
262
+ }else{
263
+ pLeft = &v1;
264
+ zTagLeft = "local";
265
+ }
266
+ if( diffMode==12 ){
267
+ pRight = &v1;
268
+ zTagRight = "local";
269
+ }else{
270
+ pRight = &v2;
271
+ zTagRight = "merge-in";
272
+ }
273
+ cfg.azLabel[0] = mprintf("%s (%s)", zFName, zTagLeft);
274
+ cfg.azLabel[1] = mprintf("%s (%s)", zFName, zTagRight);
275
+ diff_print_filenames("", "", &cfg, &out);
276
+ text_diff(pLeft, pRight, &out, &cfg);
277
+ fossil_free((char*)cfg.azLabel[0]);
278
+ fossil_free((char*)cfg.azLabel[1]);
279
+ }
280
+
281
+ blob_write_to_file(&out, "-");
239282
mb.xDestroy(&mb);
240283
blob_reset(&pivot);
241284
blob_reset(&v1);
242285
blob_reset(&v2);
243286
blob_reset(&out);
@@ -257,21 +300,26 @@
257300
** lines are shown
258301
** -c|--context N Show N lines of context around each change,
259302
** with negative N meaning show all content. Only
260303
** meaningful in combination with --tcl or --tk.
261304
** --dark Use dark mode for the Tcl/Tk-based GUI
305
+** --tk Bring up a Tcl/Tk GUI that shows the changes
306
+** associated with the most recent merge.
307
+**
308
+** Options used internally by --tk:
309
+** --diff12 FILE Bring up a separate --tk diff for just the baseline
310
+** and local variants of FILE.
311
+** --diff13 FILE Like --diff12 but for baseline versus merge-in
312
+** --diff23 FILE Like --diff12 but for local versus merge-in
262313
** --tcl FILE Generate (to stdout) a TCL list containing
263314
** information needed to display the changes to
264315
** FILE caused by the most recent merge. FILE must
265316
** be a pathname relative to the root of the check-out.
266
-** --tk Bring up a Tcl/Tk GUI that shows the changes
267
-** associated with the most recent merge.
268317
**
269
-** Additional debugging options available only when --tk is used:
270
-** --debug Show sub-commands run to implement --tk
318
+** Debugging options available only when --tk is used:
319
+** --tkdebug Show sub-commands run to implement --tk
271320
** --script FILE Write script used to implement --tk into FILE
272
-
273321
*/
274322
void merge_info_cmd(void){
275323
const char *zCnt;
276324
const char *zTcl;
277325
int bTk;
@@ -279,23 +327,29 @@
279327
int bAll;
280328
int nContext;
281329
Stmt q;
282330
const char *zWhere;
283331
int cnt = 0;
332
+ const char *zDiff2 = 0;
333
+ int diffMode = 0;
284334
285335
db_must_be_within_tree();
286
- zTcl = find_option("tcl", 0, 1);
287336
bTk = find_option("tk", 0, 0)!=0;
337
+ zTcl = find_option("tcl", 0, 1);
288338
zCnt = find_option("context", "c", 1);
289339
bDark = find_option("dark", 0, 0)!=0;
290340
bAll = find_option("all", "a", 0)!=0;
291
- if( bTk==0 ){
292
- verify_all_options();
293
- if( g.argc>2 ){
294
- usage("[OPTIONS]");
295
- }
341
+ if( (zDiff2 = find_option("diff12", 0, 1))!=0 ){
342
+ diffMode = 12;
343
+ }else
344
+ if( (zDiff2 = find_option("diff13", 0, 1))!=0 ){
345
+ diffMode = 13;
346
+ }else
347
+ if( (zDiff2 = find_option("diff23", 0, 1))!=0 ){
348
+ diffMode = 23;
296349
}
350
+
297351
if( zCnt ){
298352
nContext = atoi(zCnt);
299353
if( nContext<0 ) nContext = 0xfffffff;
300354
}else{
301355
nContext = 6;
@@ -311,13 +365,28 @@
311365
if( bTk ){
312366
merge_info_tk(bDark, bAll, nContext);
313367
return;
314368
}
315369
if( zTcl ){
316
- merge_info_tcl(zTcl, nContext);
370
+ if( diffMode ) zTcl = zDiff2;
371
+ merge_info_tcl(zTcl, nContext, diffMode);
372
+ return;
373
+ }
374
+ if( diffMode ){
375
+ char *zCmd;
376
+ zCmd = mprintf("merge-info --diff%d %!$ -c %d%s",
377
+ diffMode, zDiff2, nContext, bDark ? " --dark" : "");
378
+ diff_tk(zCmd, g.argc);
379
+ fossil_free(zCmd);
317380
return;
318381
}
382
+
383
+ verify_all_options();
384
+ if( g.argc>2 ){
385
+ usage("[OPTIONS]");
386
+ }
387
+
319388
if( bAll ){
320389
zWhere = "";
321390
}else{
322391
zWhere = "WHERE op IN ('MERGE','CONFLICT','ERROR')";
323392
}
324393
--- src/merge.c
+++ src/merge.c
@@ -39,15 +39,16 @@
39 }
40 /* The undocumented --script FILENAME option causes the Tk script to
41 ** be written into the FILENAME instead of being run. This is used
42 ** for testing and debugging. */
43 zTempFile = find_option("script",0,1);
44 bDebug = find_option("debug",0,0)!=0;
45 verify_all_options();
46
47 blob_zero(&script);
48 blob_appendf(&script, "set ncontext %d\n", nContext);
 
49 blob_appendf(&script, "set fossilcmd {| \"%/\" merge-info}\n",
50 g.nameOfExe);
51 blob_appendf(&script, "set filelist [list");
52 if( g.argc==2 ){
53 /* No files named on the command-line. Use every file mentioned
@@ -136,12 +137,19 @@
136 ** command associated with file "zFName". zFName must be the filename
137 ** relative to the root of the check-in - in other words a "tree name".
138 **
139 ** When this routine is called, we know that the mergestat table
140 ** exists, but we do not know if zFName is mentioned in that table.
 
 
 
 
 
 
 
141 */
142 static void merge_info_tcl(const char *zFName, int nContext){
143 const char *zTreename;/* Name of the file in the tree */
144 Stmt q; /* To query the MERGESTAT table */
145 MergeBuilder mb; /* The merge builder object */
146 Blob pivot,v1,v2,out; /* Blobs for holding content */
147 const char *zFN; /* A filename */
@@ -162,82 +170,117 @@
162 return;
163 }
164 mergebuilder_init_tcl(&mb);
165 mb.nContext = nContext;
166
167 /* Set up the pivot */
168 zFN = db_column_text(&q, 0);
169 if( zFN==0 ){
170 /* No pivot because the file was added */
171 mb.zPivot = "(no baseline)";
172 blob_zero(&pivot);
173 }else{
174 mb.zPivot = mprintf("%s (baseline)", file_tail(zFN));
175 rid = db_column_int(&q, 1);
176 content_get(rid, &pivot);
177 }
178 mb.pPivot = &pivot;
179
180 /* Set up the merge-in as V2 */
181 zFN = db_column_text(&q, 5);
182 if( zFN==0 ){
183 /* File deleted in the merged-in branch */
184 mb.zV2 = "(deleted file)";
185 blob_zero(&v2);
186 }else{
187 mb.zV2 = mprintf("%s (merge-in)", file_tail(zFN));
188 rid = db_column_int(&q, 6);
189 content_get(rid, &v2);
190 }
191 mb.pV2 = &v2;
192
193 /* Set up the local content as V1 */
194 zFN = db_column_text(&q, 2);
195 if( zFN==0 ){
196 /* File added by merge */
197 mb.zV1 = "(no original)";
198 blob_zero(&v1);
199 }else{
200 mb.zV1 = mprintf("%s (local)", file_tail(zFN));
201 rid = db_column_int(&q, 3);
202 sz = db_column_int(&q, 4);
203 if( rid==0 && sz>0 ){
204 /* The origin file had been edited so we'll have to pull its
205 ** original content out of the undo buffer */
206 Stmt q2;
207 db_prepare(&q2,
208 "SELECT content FROM undo"
209 " WHERE pathname=%Q AND octet_length(content)=%d",
210 zFN, sz
211 );
212 blob_zero(&v1);
213 if( db_step(&q2)==SQLITE_ROW ){
214 db_column_blob(&q2, 0, &v1);
215 }else{
216 mb.zV1 = "(local content missing)";
217 }
218 db_finalize(&q2);
219 }else{
220 /* The origin file was unchanged when the merge first occurred */
221 content_get(rid, &v1);
222 }
223 }
224 mb.pV1 = &v1;
225
226 /* Set up the output */
227 zFN = db_column_text(&q, 7);
228 if( zFN==0 ){
229 mb.zOut = "(Merge Result)";
230 }else{
231 mb.zOut = mprintf("%s (after merge)", file_tail(zFN));
232 }
233 blob_zero(&out);
234 mb.pOut = &out;
235
236 merge_three_blobs(&mb);
237 blob_write_to_file(&out, "-");
238
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239 mb.xDestroy(&mb);
240 blob_reset(&pivot);
241 blob_reset(&v1);
242 blob_reset(&v2);
243 blob_reset(&out);
@@ -257,21 +300,26 @@
257 ** lines are shown
258 ** -c|--context N Show N lines of context around each change,
259 ** with negative N meaning show all content. Only
260 ** meaningful in combination with --tcl or --tk.
261 ** --dark Use dark mode for the Tcl/Tk-based GUI
 
 
 
 
 
 
 
 
262 ** --tcl FILE Generate (to stdout) a TCL list containing
263 ** information needed to display the changes to
264 ** FILE caused by the most recent merge. FILE must
265 ** be a pathname relative to the root of the check-out.
266 ** --tk Bring up a Tcl/Tk GUI that shows the changes
267 ** associated with the most recent merge.
268 **
269 ** Additional debugging options available only when --tk is used:
270 ** --debug Show sub-commands run to implement --tk
271 ** --script FILE Write script used to implement --tk into FILE
272
273 */
274 void merge_info_cmd(void){
275 const char *zCnt;
276 const char *zTcl;
277 int bTk;
@@ -279,23 +327,29 @@
279 int bAll;
280 int nContext;
281 Stmt q;
282 const char *zWhere;
283 int cnt = 0;
 
 
284
285 db_must_be_within_tree();
286 zTcl = find_option("tcl", 0, 1);
287 bTk = find_option("tk", 0, 0)!=0;
 
288 zCnt = find_option("context", "c", 1);
289 bDark = find_option("dark", 0, 0)!=0;
290 bAll = find_option("all", "a", 0)!=0;
291 if( bTk==0 ){
292 verify_all_options();
293 if( g.argc>2 ){
294 usage("[OPTIONS]");
295 }
 
 
 
296 }
 
297 if( zCnt ){
298 nContext = atoi(zCnt);
299 if( nContext<0 ) nContext = 0xfffffff;
300 }else{
301 nContext = 6;
@@ -311,13 +365,28 @@
311 if( bTk ){
312 merge_info_tk(bDark, bAll, nContext);
313 return;
314 }
315 if( zTcl ){
316 merge_info_tcl(zTcl, nContext);
 
 
 
 
 
 
 
 
 
317 return;
318 }
 
 
 
 
 
 
319 if( bAll ){
320 zWhere = "";
321 }else{
322 zWhere = "WHERE op IN ('MERGE','CONFLICT','ERROR')";
323 }
324
--- src/merge.c
+++ src/merge.c
@@ -39,15 +39,16 @@
39 }
40 /* The undocumented --script FILENAME option causes the Tk script to
41 ** be written into the FILENAME instead of being run. This is used
42 ** for testing and debugging. */
43 zTempFile = find_option("script",0,1);
44 bDebug = find_option("tkdebug",0,0)!=0;
45 verify_all_options();
46
47 blob_zero(&script);
48 blob_appendf(&script, "set ncontext %d\n", nContext);
49 blob_appendf(&script, "set fossilexe {\"%/\"}\n", g.nameOfExe);
50 blob_appendf(&script, "set fossilcmd {| \"%/\" merge-info}\n",
51 g.nameOfExe);
52 blob_appendf(&script, "set filelist [list");
53 if( g.argc==2 ){
54 /* No files named on the command-line. Use every file mentioned
@@ -136,12 +137,19 @@
137 ** command associated with file "zFName". zFName must be the filename
138 ** relative to the root of the check-in - in other words a "tree name".
139 **
140 ** When this routine is called, we know that the mergestat table
141 ** exists, but we do not know if zFName is mentioned in that table.
142 **
143 ** The diffMode variable has these values:
144 **
145 ** 0 Standard 3-way diff
146 ** 12 2-way diff between baseline and local
147 ** 13 2-way diff between baseline and merge-in
148 ** 23 2-way diff between local and merge-in
149 */
150 static void merge_info_tcl(const char *zFName, int nContext, int diffMode){
151 const char *zTreename;/* Name of the file in the tree */
152 Stmt q; /* To query the MERGESTAT table */
153 MergeBuilder mb; /* The merge builder object */
154 Blob pivot,v1,v2,out; /* Blobs for holding content */
155 const char *zFN; /* A filename */
@@ -162,82 +170,117 @@
170 return;
171 }
172 mergebuilder_init_tcl(&mb);
173 mb.nContext = nContext;
174
175 blob_zero(&pivot);
176 if( diffMode!=23 ){
177 /* Set up the pivot or baseline */
178 zFN = db_column_text(&q, 0);
179 if( zFN==0 ){
180 /* No pivot because the file was added */
181 mb.zPivot = "(no baseline)";
182 }else{
183 mb.zPivot = mprintf("%s (baseline)", file_tail(zFN));
184 rid = db_column_int(&q, 1);
185 content_get(rid, &pivot);
186 }
187 mb.pPivot = &pivot;
188 }
189
190 blob_zero(&v2);
191 if( diffMode!=12 ){
192 /* Set up the merge-in as V2 */
193 zFN = db_column_text(&q, 5);
194 if( zFN==0 ){
195 /* File deleted in the merged-in branch */
196 mb.zV2 = "(deleted file)";
197 }else{
198 mb.zV2 = mprintf("%s (merge-in)", file_tail(zFN));
199 rid = db_column_int(&q, 6);
200 content_get(rid, &v2);
201 }
202 mb.pV2 = &v2;
203 }
204
205 blob_zero(&v1);
206 if( diffMode!=13 ){
207 /* Set up the local content as V1 */
208 zFN = db_column_text(&q, 2);
209 if( zFN==0 ){
210 /* File added by merge */
211 mb.zV1 = "(no original)";
212 }else{
213 mb.zV1 = mprintf("%s (local)", file_tail(zFN));
214 rid = db_column_int(&q, 3);
215 sz = db_column_int(&q, 4);
216 if( rid==0 && sz>0 ){
217 /* The origin file had been edited so we'll have to pull its
218 ** original content out of the undo buffer */
219 Stmt q2;
220 db_prepare(&q2,
221 "SELECT content FROM undo"
222 " WHERE pathname=%Q AND octet_length(content)=%d",
223 zFN, sz
224 );
225 blob_zero(&v1);
226 if( db_step(&q2)==SQLITE_ROW ){
227 db_column_blob(&q2, 0, &v1);
228 }else{
229 mb.zV1 = "(local content missing)";
230 }
231 db_finalize(&q2);
232 }else{
233 /* The origin file was unchanged when the merge first occurred */
234 content_get(rid, &v1);
235 }
236 }
237 mb.pV1 = &v1;
238 }
239
240 blob_zero(&out);
241 if( diffMode==0 ){
242 /* Set up the output and do a 3-way diff */
243 zFN = db_column_text(&q, 7);
244 if( zFN==0 ){
245 mb.zOut = "(Merge Result)";
246 }else{
247 mb.zOut = mprintf("%s (after merge)", file_tail(zFN));
248 }
249 mb.pOut = &out;
250 merge_three_blobs(&mb);
251 }else{
252 /* Set up to do a two-way diff */
253 Blob *pLeft, *pRight;
254 const char *zTagLeft, *zTagRight;
255 DiffConfig cfg;
256 memset(&cfg, 0, sizeof(cfg));
257 cfg.diffFlags = DIFF_TCL;
258 cfg.nContext = mb.nContext;
259 if( diffMode==12 || diffMode==13 ){
260 pLeft = &pivot;
261 zTagLeft = "baseline";
262 }else{
263 pLeft = &v1;
264 zTagLeft = "local";
265 }
266 if( diffMode==12 ){
267 pRight = &v1;
268 zTagRight = "local";
269 }else{
270 pRight = &v2;
271 zTagRight = "merge-in";
272 }
273 cfg.azLabel[0] = mprintf("%s (%s)", zFName, zTagLeft);
274 cfg.azLabel[1] = mprintf("%s (%s)", zFName, zTagRight);
275 diff_print_filenames("", "", &cfg, &out);
276 text_diff(pLeft, pRight, &out, &cfg);
277 fossil_free((char*)cfg.azLabel[0]);
278 fossil_free((char*)cfg.azLabel[1]);
279 }
280
281 blob_write_to_file(&out, "-");
282 mb.xDestroy(&mb);
283 blob_reset(&pivot);
284 blob_reset(&v1);
285 blob_reset(&v2);
286 blob_reset(&out);
@@ -257,21 +300,26 @@
300 ** lines are shown
301 ** -c|--context N Show N lines of context around each change,
302 ** with negative N meaning show all content. Only
303 ** meaningful in combination with --tcl or --tk.
304 ** --dark Use dark mode for the Tcl/Tk-based GUI
305 ** --tk Bring up a Tcl/Tk GUI that shows the changes
306 ** associated with the most recent merge.
307 **
308 ** Options used internally by --tk:
309 ** --diff12 FILE Bring up a separate --tk diff for just the baseline
310 ** and local variants of FILE.
311 ** --diff13 FILE Like --diff12 but for baseline versus merge-in
312 ** --diff23 FILE Like --diff12 but for local versus merge-in
313 ** --tcl FILE Generate (to stdout) a TCL list containing
314 ** information needed to display the changes to
315 ** FILE caused by the most recent merge. FILE must
316 ** be a pathname relative to the root of the check-out.
 
 
317 **
318 ** Debugging options available only when --tk is used:
319 ** --tkdebug Show sub-commands run to implement --tk
320 ** --script FILE Write script used to implement --tk into FILE
 
321 */
322 void merge_info_cmd(void){
323 const char *zCnt;
324 const char *zTcl;
325 int bTk;
@@ -279,23 +327,29 @@
327 int bAll;
328 int nContext;
329 Stmt q;
330 const char *zWhere;
331 int cnt = 0;
332 const char *zDiff2 = 0;
333 int diffMode = 0;
334
335 db_must_be_within_tree();
 
336 bTk = find_option("tk", 0, 0)!=0;
337 zTcl = find_option("tcl", 0, 1);
338 zCnt = find_option("context", "c", 1);
339 bDark = find_option("dark", 0, 0)!=0;
340 bAll = find_option("all", "a", 0)!=0;
341 if( (zDiff2 = find_option("diff12", 0, 1))!=0 ){
342 diffMode = 12;
343 }else
344 if( (zDiff2 = find_option("diff13", 0, 1))!=0 ){
345 diffMode = 13;
346 }else
347 if( (zDiff2 = find_option("diff23", 0, 1))!=0 ){
348 diffMode = 23;
349 }
350
351 if( zCnt ){
352 nContext = atoi(zCnt);
353 if( nContext<0 ) nContext = 0xfffffff;
354 }else{
355 nContext = 6;
@@ -311,13 +365,28 @@
365 if( bTk ){
366 merge_info_tk(bDark, bAll, nContext);
367 return;
368 }
369 if( zTcl ){
370 if( diffMode ) zTcl = zDiff2;
371 merge_info_tcl(zTcl, nContext, diffMode);
372 return;
373 }
374 if( diffMode ){
375 char *zCmd;
376 zCmd = mprintf("merge-info --diff%d %!$ -c %d%s",
377 diffMode, zDiff2, nContext, bDark ? " --dark" : "");
378 diff_tk(zCmd, g.argc);
379 fossil_free(zCmd);
380 return;
381 }
382
383 verify_all_options();
384 if( g.argc>2 ){
385 usage("[OPTIONS]");
386 }
387
388 if( bAll ){
389 zWhere = "";
390 }else{
391 zWhere = "WHERE op IN ('MERGE','CONFLICT','ERROR')";
392 }
393
+48 -15
--- src/merge.tcl
+++ src/merge.tcl
@@ -1,23 +1,27 @@
11
# Show details of a 3-way merge operation. The left-most column is the
22
# common ancestor. The next two columns are edits of that common ancestor.
33
# The right-most column is the result of the merge.
44
#
5
-# There is always a "fossilcmd" variable which tells the script how to
6
-# invoke Fossil to get the information it needs. This script will
7
-# automatically append "-c N" to tell Fossil how much context it wants.
5
+# Several variables will have been initialized:
6
+#
7
+# ncontext The number of lines of context to show on each change
8
+#
9
+# fossilexe Pathname of the fossil program
10
+#
11
+# filelist A list of "merge-type filename" pairs.
12
+#
13
+# darkmode Boolean. True for dark mode
14
+#
15
+# debug Boolean. True for debugging output
816
#
917
# If the "filelist" global variable is defined, then it is a list of
1018
# alternating "merge-type names" (ex: UPDATE, MERGE, CONFLICT, ERROR) and
1119
# filenames. In that case, the initial display shows the changes for
1220
# the first pair on the list and there is a optionmenu that allows the
1321
# user to select other fiels on the list.
1422
#
15
-# There should also be a global variable named "ncontext" which is the
16
-# number of lines of context to display. The value of this variable
17
-# controls the "-c N" argument that is appended to fossilcmd.
18
-#
1923
# This header comment is stripped off by the "mkbuiltin.c" program.
2024
#
2125
package require Tk
2226
2327
array set CFG_light {
@@ -93,19 +97,19 @@
9397
regexp {[a-z]+} $c type
9498
return $type
9599
}
96100
97101
proc readMerge {args} {
98
- global fossilcmd ncontext current_file debug
102
+ global fossilexe ncontext current_file debug
99103
if {$ncontext=="All"} {
100
- set cmd "$fossilcmd -c -1"
104
+ set cmd "| $fossilexe merge-info -c -1"
101105
} else {
102
- set cmd "$fossilcmd -c $ncontext"
106
+ set cmd "| $fossilexe merge-info -c $ncontext"
103107
}
104108
if {[info exists current_file]} {
105109
regsub {^[A-Z]+ } $current_file {} fn
106
- append cmd " -tcl [list $fn]"
110
+ lappend cmd -tcl $fn
107111
}
108112
if {$debug} {
109113
regsub {^\| +} $cmd {} cmd2
110114
puts $cmd2
111115
flush stdout
@@ -342,10 +346,38 @@
342346
bind . <$key> "scroll-$axis $args; break"
343347
bind . <Shift-$key> continue
344348
}
345349
346350
frame .bb
351
+::ttk::menubutton .bb.diff2 -text {2-way diff} -menu .bb.diff2.m
352
+menu .bb.diff2.m -tearoff 0
353
+.bb.diff2.m add command -label {baseline vs. local} -command {two-way 12}
354
+.bb.diff2.m add command -label {baseline vs. merge-in} -command {two-way 13}
355
+.bb.diff2.m add command -label {local vs. merge-in} -command {two-way 23}
356
+
357
+# Bring up a separate two-way diff between a pair of columns
358
+# the argument is one of:
359
+# 12 Baseline versus Local
360
+# 13 Baseline versus Merge-in
361
+# 23 Local versus Merge-in
362
+#
363
+proc two-way {mode} {
364
+ global current_file fossilexe debug darkmode ncontext
365
+ regsub {^[A-Z]+ } $current_file {} fn
366
+ set cmd $fossilexe
367
+ lappend cmd merge-info --diff$mode $fn -c $ncontext
368
+ if {$darkmode} {
369
+ lappend cmd --dark
370
+ }
371
+ if {$debug} {
372
+ lappend cmd --tkdebug
373
+ puts $cmd
374
+ flush stdout
375
+ }
376
+ exec {*}$cmd &
377
+}
378
+
347379
set useOptionMenu 1
348380
if {[info exists filelist]} {
349381
set current_file "[lindex $filelist 0] [lindex $filelist 1]"
350382
if {[llength $filelist]>2} {
351383
trace add variable current_file write readMerge
@@ -579,16 +611,17 @@
579611
}
580612
set ::search $w
581613
}
582614
::ttk::button .bb.quit -text {Quit} -command exit
583615
::ttk::button .bb.search -text {Search} -command searchOnOff
584
-pack .bb.quit -side left
616
+pack .bb.quit -side left -fill y
617
+pack .bb.diff2 -side left -fill y
585618
if {[winfo exists .bb.files]} {
586
- pack .bb.files -side left
619
+ pack .bb.files -side left -fill y
587620
}
588
-pack .bb.ctxtag .bb.ctx -side left
589
-pack .bb.search -side left
621
+pack .bb.ctxtag .bb.ctx -side left -fill y
622
+pack .bb.search -side left -fill y
590623
grid rowconfigure . 1 -weight 1 -minsize [winfo reqheight .nameA]
591624
grid rowconfigure . 2 -weight 100
592625
readMerge
593626
grid .bb -row 0 -columnspan 8
594627
grid .nameA -row 1 -column 1 -sticky ew
595628
--- src/merge.tcl
+++ src/merge.tcl
@@ -1,23 +1,27 @@
1 # Show details of a 3-way merge operation. The left-most column is the
2 # common ancestor. The next two columns are edits of that common ancestor.
3 # The right-most column is the result of the merge.
4 #
5 # There is always a "fossilcmd" variable which tells the script how to
6 # invoke Fossil to get the information it needs. This script will
7 # automatically append "-c N" to tell Fossil how much context it wants.
 
 
 
 
 
 
 
 
8 #
9 # If the "filelist" global variable is defined, then it is a list of
10 # alternating "merge-type names" (ex: UPDATE, MERGE, CONFLICT, ERROR) and
11 # filenames. In that case, the initial display shows the changes for
12 # the first pair on the list and there is a optionmenu that allows the
13 # user to select other fiels on the list.
14 #
15 # There should also be a global variable named "ncontext" which is the
16 # number of lines of context to display. The value of this variable
17 # controls the "-c N" argument that is appended to fossilcmd.
18 #
19 # This header comment is stripped off by the "mkbuiltin.c" program.
20 #
21 package require Tk
22
23 array set CFG_light {
@@ -93,19 +97,19 @@
93 regexp {[a-z]+} $c type
94 return $type
95 }
96
97 proc readMerge {args} {
98 global fossilcmd ncontext current_file debug
99 if {$ncontext=="All"} {
100 set cmd "$fossilcmd -c -1"
101 } else {
102 set cmd "$fossilcmd -c $ncontext"
103 }
104 if {[info exists current_file]} {
105 regsub {^[A-Z]+ } $current_file {} fn
106 append cmd " -tcl [list $fn]"
107 }
108 if {$debug} {
109 regsub {^\| +} $cmd {} cmd2
110 puts $cmd2
111 flush stdout
@@ -342,10 +346,38 @@
342 bind . <$key> "scroll-$axis $args; break"
343 bind . <Shift-$key> continue
344 }
345
346 frame .bb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
347 set useOptionMenu 1
348 if {[info exists filelist]} {
349 set current_file "[lindex $filelist 0] [lindex $filelist 1]"
350 if {[llength $filelist]>2} {
351 trace add variable current_file write readMerge
@@ -579,16 +611,17 @@
579 }
580 set ::search $w
581 }
582 ::ttk::button .bb.quit -text {Quit} -command exit
583 ::ttk::button .bb.search -text {Search} -command searchOnOff
584 pack .bb.quit -side left
 
585 if {[winfo exists .bb.files]} {
586 pack .bb.files -side left
587 }
588 pack .bb.ctxtag .bb.ctx -side left
589 pack .bb.search -side left
590 grid rowconfigure . 1 -weight 1 -minsize [winfo reqheight .nameA]
591 grid rowconfigure . 2 -weight 100
592 readMerge
593 grid .bb -row 0 -columnspan 8
594 grid .nameA -row 1 -column 1 -sticky ew
595
--- src/merge.tcl
+++ src/merge.tcl
@@ -1,23 +1,27 @@
1 # Show details of a 3-way merge operation. The left-most column is the
2 # common ancestor. The next two columns are edits of that common ancestor.
3 # The right-most column is the result of the merge.
4 #
5 # Several variables will have been initialized:
6 #
7 # ncontext The number of lines of context to show on each change
8 #
9 # fossilexe Pathname of the fossil program
10 #
11 # filelist A list of "merge-type filename" pairs.
12 #
13 # darkmode Boolean. True for dark mode
14 #
15 # debug Boolean. True for debugging output
16 #
17 # If the "filelist" global variable is defined, then it is a list of
18 # alternating "merge-type names" (ex: UPDATE, MERGE, CONFLICT, ERROR) and
19 # filenames. In that case, the initial display shows the changes for
20 # the first pair on the list and there is a optionmenu that allows the
21 # user to select other fiels on the list.
22 #
 
 
 
 
23 # This header comment is stripped off by the "mkbuiltin.c" program.
24 #
25 package require Tk
26
27 array set CFG_light {
@@ -93,19 +97,19 @@
97 regexp {[a-z]+} $c type
98 return $type
99 }
100
101 proc readMerge {args} {
102 global fossilexe ncontext current_file debug
103 if {$ncontext=="All"} {
104 set cmd "| $fossilexe merge-info -c -1"
105 } else {
106 set cmd "| $fossilexe merge-info -c $ncontext"
107 }
108 if {[info exists current_file]} {
109 regsub {^[A-Z]+ } $current_file {} fn
110 lappend cmd -tcl $fn
111 }
112 if {$debug} {
113 regsub {^\| +} $cmd {} cmd2
114 puts $cmd2
115 flush stdout
@@ -342,10 +346,38 @@
346 bind . <$key> "scroll-$axis $args; break"
347 bind . <Shift-$key> continue
348 }
349
350 frame .bb
351 ::ttk::menubutton .bb.diff2 -text {2-way diff} -menu .bb.diff2.m
352 menu .bb.diff2.m -tearoff 0
353 .bb.diff2.m add command -label {baseline vs. local} -command {two-way 12}
354 .bb.diff2.m add command -label {baseline vs. merge-in} -command {two-way 13}
355 .bb.diff2.m add command -label {local vs. merge-in} -command {two-way 23}
356
357 # Bring up a separate two-way diff between a pair of columns
358 # the argument is one of:
359 # 12 Baseline versus Local
360 # 13 Baseline versus Merge-in
361 # 23 Local versus Merge-in
362 #
363 proc two-way {mode} {
364 global current_file fossilexe debug darkmode ncontext
365 regsub {^[A-Z]+ } $current_file {} fn
366 set cmd $fossilexe
367 lappend cmd merge-info --diff$mode $fn -c $ncontext
368 if {$darkmode} {
369 lappend cmd --dark
370 }
371 if {$debug} {
372 lappend cmd --tkdebug
373 puts $cmd
374 flush stdout
375 }
376 exec {*}$cmd &
377 }
378
379 set useOptionMenu 1
380 if {[info exists filelist]} {
381 set current_file "[lindex $filelist 0] [lindex $filelist 1]"
382 if {[llength $filelist]>2} {
383 trace add variable current_file write readMerge
@@ -579,16 +611,17 @@
611 }
612 set ::search $w
613 }
614 ::ttk::button .bb.quit -text {Quit} -command exit
615 ::ttk::button .bb.search -text {Search} -command searchOnOff
616 pack .bb.quit -side left -fill y
617 pack .bb.diff2 -side left -fill y
618 if {[winfo exists .bb.files]} {
619 pack .bb.files -side left -fill y
620 }
621 pack .bb.ctxtag .bb.ctx -side left -fill y
622 pack .bb.search -side left -fill y
623 grid rowconfigure . 1 -weight 1 -minsize [winfo reqheight .nameA]
624 grid rowconfigure . 2 -weight 100
625 readMerge
626 grid .bb -row 0 -columnspan 8
627 grid .nameA -row 1 -column 1 -sticky ew
628

Keyboard Shortcuts

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