Fossil SCM

Add filename labels at the top of each column in the merge display.

drh 2024-12-03 13:36 merge-enhancements
Commit 7b8be85206d9940a74ef14259765c519377663aa3c8fbda379c5be497be561b0
2 files changed +35 -17 +68 -29
+35 -17
--- src/merge.tcl
+++ src/merge.tcl
@@ -1,11 +1,11 @@
1
-# The "diff --tk" command outputs prepends a "set fossilcmd {...}" line
2
-# to this file, then runs this file using "tclsh" in order to display the
3
-# graphical diff in a separate window. A typical "set fossilcmd" line
4
-# looks like this:
1
+# The "--tk" option to various merge commands prepends one or more
2
+# "set fossilcmd(NAME) {...}" lines to this file, then runs this file using
3
+# "tclsh" in order to show a graphical analysis of the merge results.
4
+# A typical "set fossilcmd" line looks like this:
55
#
6
-# set fossilcmd {| "./fossil" diff --tcl -i -v}
6
+# set fossilcmd(file1.txt) {| "./fossil" diff --tcl -i -v}
77
#
88
# This header comment is stripped off by the "mkbuiltin.c" program.
99
#
1010
package require Tk
1111
@@ -129,10 +129,12 @@
129129
set key4 [string index $D 0]
130130
if {$key4=="X"} {set dtag rm} {set dtag -}
131131
if {$key1=="."} {
132132
.lnA insert end \n -
133133
.txtA insert end \n $dtag
134
+ } elseif {$key1=="N"} {
135
+ .nameA config -text [string range $A 1 end]
134136
} else {
135137
.lnA insert end $lnA\n -
136138
incr lnA
137139
.txtA insert end [string range $A 1 end]\n $dtag
138140
}
@@ -143,10 +145,12 @@
143145
.lnB insert end $lnB\n -
144146
incr lnB
145147
if {$key4=="2"} {set tag chng} {set tag $dtag}
146148
if {$key2=="1"} {
147149
.txtB insert end [string range $A 1 end]\n $tag
150
+ } elseif {$key2=="N"} {
151
+ .nameB config -text [string range $B 1 end]
148152
} else {
149153
.txtB insert end [string range $B 1 end]\n $tag
150154
}
151155
}
152156
if {$key3=="."} {
@@ -158,10 +162,12 @@
158162
if {$key4=="3"} {set tag add} {set tag $dtag}
159163
if {$key3=="1"} {
160164
.txtC insert end [string range $A 1 end]\n $tag
161165
} elseif {$key3=="2"} {
162166
.txtC insert end [string range $B 1 end]\n chng
167
+ } elseif {$key3=="N"} {
168
+ .nameC config -text [string range $C 1 end]
163169
} else {
164170
.txtC insert end [string range $C 1 end]\n $tag
165171
}
166172
}
167173
if {$key4=="." || $key4=="X"} {
@@ -174,10 +180,12 @@
174180
.txtD insert end [string range $A 1 end]\n -
175181
} elseif {$key4=="2"} {
176182
.txtD insert end [string range $B 1 end]\n chng
177183
} elseif {$key4=="3"} {
178184
.txtD insert end [string range $C 1 end]\n add
185
+ } elseif {$key4=="N"} {
186
+ .nameD config -text [string range $D 1 end]
179187
} else {
180188
.txtD insert end [string range $D 1 end]\n -
181189
}
182190
}
183191
}
@@ -239,19 +247,19 @@
239247
}
240248
241249
proc sync-x {col first last} {
242250
disableSync x
243251
$col xview moveto [expr {$first*[xvis $col]/($last-$first)}]
244
- foreach side {A B} {
252
+ foreach side {A B C D} {
245253
set sb .sbx$side
246254
set xview [.txt$side xview]
247
- if {[lindex $xview 0] > 0 || [lindex $xview 1] < 1} {
248
- grid $sb
249
- eval $sb set $xview
250
- } else {
251
- grid remove $sb
252
- }
255
+# if {[lindex $xview 0] > 0 || [lindex $xview 1] < 1} {
256
+# grid $sb
257
+# eval $sb set $xview
258
+# } else {
259
+# grid remove $sb
260
+# }
253261
}
254262
enableSync x
255263
}
256264
257265
proc sync-y {first last} {
@@ -382,10 +390,14 @@
382390
$c tag config fn -spacing1 $CFG(FN_PAD) -spacing3 $CFG(FN_PAD)
383391
bindtags $c ". $c Text all"
384392
bind $c <1> {focus %W}
385393
}
386394
395
+label .nameA
396
+label .nameB
397
+label .nameC
398
+label .nameD -text {Merge Result}
387399
::ttk::scrollbar .sby -command {.txtA yview} -orient vertical
388400
::ttk::scrollbar .sbxA -command {.txtA xview} -orient horizontal
389401
::ttk::scrollbar .sbxB -command {.txtB xview} -orient horizontal
390402
::ttk::scrollbar .sbxC -command {.txtC xview} -orient horizontal
391403
::ttk::scrollbar .sbxD -command {.txtD xview} -orient horizontal
@@ -479,13 +491,19 @@
479491
grid columnconfigure . $rn -weight 1 -uniform a
480492
grid columnconfigure . [expr {$rn+1}] -weight 1 -uniform b
481493
incr rn 2
482494
}
483495
grid .bb -row 0 -columnspan 8
484
-eval grid [cols] -row 1 -sticky nsew
485
-grid .sby -row 1 -column 8 -sticky ns
486
-grid .sbxA -row 2 -columnspan 2 -sticky ew
487
-grid .spacer -row 2 -column 2
488
-grid .sbxB -row 2 -column 3 -columnspan 2 -sticky ew
496
+grid .nameA -row 1 -column 1 -sticky ew
497
+grid .nameB -row 1 -column 3 -sticky ew
498
+grid .nameC -row 1 -column 5 -sticky ew
499
+grid .nameD -row 1 -column 7 -sticky ew
500
+eval grid [cols] -row 2 -sticky nsew
501
+grid .sby -row 2 -column 8 -sticky ns
502
+grid .sbxA -row 3 -column 1 -sticky ew
503
+grid .sbxB -row 3 -column 3 -sticky ew
504
+grid .sbxC -row 3 -column 5 -sticky ew
505
+grid .sbxD -row 3 -column 7 -sticky ew
506
+
489507
490508
.spacer config -height [winfo height .sbxA]
491509
wm deiconify .
492510
--- src/merge.tcl
+++ src/merge.tcl
@@ -1,11 +1,11 @@
1 # The "diff --tk" command outputs prepends a "set fossilcmd {...}" line
2 # to this file, then runs this file using "tclsh" in order to display the
3 # graphical diff in a separate window. A typical "set fossilcmd" line
4 # looks like this:
5 #
6 # set fossilcmd {| "./fossil" diff --tcl -i -v}
7 #
8 # This header comment is stripped off by the "mkbuiltin.c" program.
9 #
10 package require Tk
11
@@ -129,10 +129,12 @@
129 set key4 [string index $D 0]
130 if {$key4=="X"} {set dtag rm} {set dtag -}
131 if {$key1=="."} {
132 .lnA insert end \n -
133 .txtA insert end \n $dtag
 
 
134 } else {
135 .lnA insert end $lnA\n -
136 incr lnA
137 .txtA insert end [string range $A 1 end]\n $dtag
138 }
@@ -143,10 +145,12 @@
143 .lnB insert end $lnB\n -
144 incr lnB
145 if {$key4=="2"} {set tag chng} {set tag $dtag}
146 if {$key2=="1"} {
147 .txtB insert end [string range $A 1 end]\n $tag
 
 
148 } else {
149 .txtB insert end [string range $B 1 end]\n $tag
150 }
151 }
152 if {$key3=="."} {
@@ -158,10 +162,12 @@
158 if {$key4=="3"} {set tag add} {set tag $dtag}
159 if {$key3=="1"} {
160 .txtC insert end [string range $A 1 end]\n $tag
161 } elseif {$key3=="2"} {
162 .txtC insert end [string range $B 1 end]\n chng
 
 
163 } else {
164 .txtC insert end [string range $C 1 end]\n $tag
165 }
166 }
167 if {$key4=="." || $key4=="X"} {
@@ -174,10 +180,12 @@
174 .txtD insert end [string range $A 1 end]\n -
175 } elseif {$key4=="2"} {
176 .txtD insert end [string range $B 1 end]\n chng
177 } elseif {$key4=="3"} {
178 .txtD insert end [string range $C 1 end]\n add
 
 
179 } else {
180 .txtD insert end [string range $D 1 end]\n -
181 }
182 }
183 }
@@ -239,19 +247,19 @@
239 }
240
241 proc sync-x {col first last} {
242 disableSync x
243 $col xview moveto [expr {$first*[xvis $col]/($last-$first)}]
244 foreach side {A B} {
245 set sb .sbx$side
246 set xview [.txt$side xview]
247 if {[lindex $xview 0] > 0 || [lindex $xview 1] < 1} {
248 grid $sb
249 eval $sb set $xview
250 } else {
251 grid remove $sb
252 }
253 }
254 enableSync x
255 }
256
257 proc sync-y {first last} {
@@ -382,10 +390,14 @@
382 $c tag config fn -spacing1 $CFG(FN_PAD) -spacing3 $CFG(FN_PAD)
383 bindtags $c ". $c Text all"
384 bind $c <1> {focus %W}
385 }
386
 
 
 
 
387 ::ttk::scrollbar .sby -command {.txtA yview} -orient vertical
388 ::ttk::scrollbar .sbxA -command {.txtA xview} -orient horizontal
389 ::ttk::scrollbar .sbxB -command {.txtB xview} -orient horizontal
390 ::ttk::scrollbar .sbxC -command {.txtC xview} -orient horizontal
391 ::ttk::scrollbar .sbxD -command {.txtD xview} -orient horizontal
@@ -479,13 +491,19 @@
479 grid columnconfigure . $rn -weight 1 -uniform a
480 grid columnconfigure . [expr {$rn+1}] -weight 1 -uniform b
481 incr rn 2
482 }
483 grid .bb -row 0 -columnspan 8
484 eval grid [cols] -row 1 -sticky nsew
485 grid .sby -row 1 -column 8 -sticky ns
486 grid .sbxA -row 2 -columnspan 2 -sticky ew
487 grid .spacer -row 2 -column 2
488 grid .sbxB -row 2 -column 3 -columnspan 2 -sticky ew
 
 
 
 
 
 
489
490 .spacer config -height [winfo height .sbxA]
491 wm deiconify .
492
--- src/merge.tcl
+++ src/merge.tcl
@@ -1,11 +1,11 @@
1 # The "--tk" option to various merge commands prepends one or more
2 # "set fossilcmd(NAME) {...}" lines to this file, then runs this file using
3 # "tclsh" in order to show a graphical analysis of the merge results.
4 # A typical "set fossilcmd" line looks like this:
5 #
6 # set fossilcmd(file1.txt) {| "./fossil" diff --tcl -i -v}
7 #
8 # This header comment is stripped off by the "mkbuiltin.c" program.
9 #
10 package require Tk
11
@@ -129,10 +129,12 @@
129 set key4 [string index $D 0]
130 if {$key4=="X"} {set dtag rm} {set dtag -}
131 if {$key1=="."} {
132 .lnA insert end \n -
133 .txtA insert end \n $dtag
134 } elseif {$key1=="N"} {
135 .nameA config -text [string range $A 1 end]
136 } else {
137 .lnA insert end $lnA\n -
138 incr lnA
139 .txtA insert end [string range $A 1 end]\n $dtag
140 }
@@ -143,10 +145,12 @@
145 .lnB insert end $lnB\n -
146 incr lnB
147 if {$key4=="2"} {set tag chng} {set tag $dtag}
148 if {$key2=="1"} {
149 .txtB insert end [string range $A 1 end]\n $tag
150 } elseif {$key2=="N"} {
151 .nameB config -text [string range $B 1 end]
152 } else {
153 .txtB insert end [string range $B 1 end]\n $tag
154 }
155 }
156 if {$key3=="."} {
@@ -158,10 +162,12 @@
162 if {$key4=="3"} {set tag add} {set tag $dtag}
163 if {$key3=="1"} {
164 .txtC insert end [string range $A 1 end]\n $tag
165 } elseif {$key3=="2"} {
166 .txtC insert end [string range $B 1 end]\n chng
167 } elseif {$key3=="N"} {
168 .nameC config -text [string range $C 1 end]
169 } else {
170 .txtC insert end [string range $C 1 end]\n $tag
171 }
172 }
173 if {$key4=="." || $key4=="X"} {
@@ -174,10 +180,12 @@
180 .txtD insert end [string range $A 1 end]\n -
181 } elseif {$key4=="2"} {
182 .txtD insert end [string range $B 1 end]\n chng
183 } elseif {$key4=="3"} {
184 .txtD insert end [string range $C 1 end]\n add
185 } elseif {$key4=="N"} {
186 .nameD config -text [string range $D 1 end]
187 } else {
188 .txtD insert end [string range $D 1 end]\n -
189 }
190 }
191 }
@@ -239,19 +247,19 @@
247 }
248
249 proc sync-x {col first last} {
250 disableSync x
251 $col xview moveto [expr {$first*[xvis $col]/($last-$first)}]
252 foreach side {A B C D} {
253 set sb .sbx$side
254 set xview [.txt$side xview]
255 # if {[lindex $xview 0] > 0 || [lindex $xview 1] < 1} {
256 # grid $sb
257 # eval $sb set $xview
258 # } else {
259 # grid remove $sb
260 # }
261 }
262 enableSync x
263 }
264
265 proc sync-y {first last} {
@@ -382,10 +390,14 @@
390 $c tag config fn -spacing1 $CFG(FN_PAD) -spacing3 $CFG(FN_PAD)
391 bindtags $c ". $c Text all"
392 bind $c <1> {focus %W}
393 }
394
395 label .nameA
396 label .nameB
397 label .nameC
398 label .nameD -text {Merge Result}
399 ::ttk::scrollbar .sby -command {.txtA yview} -orient vertical
400 ::ttk::scrollbar .sbxA -command {.txtA xview} -orient horizontal
401 ::ttk::scrollbar .sbxB -command {.txtB xview} -orient horizontal
402 ::ttk::scrollbar .sbxC -command {.txtC xview} -orient horizontal
403 ::ttk::scrollbar .sbxD -command {.txtD xview} -orient horizontal
@@ -479,13 +491,19 @@
491 grid columnconfigure . $rn -weight 1 -uniform a
492 grid columnconfigure . [expr {$rn+1}] -weight 1 -uniform b
493 incr rn 2
494 }
495 grid .bb -row 0 -columnspan 8
496 grid .nameA -row 1 -column 1 -sticky ew
497 grid .nameB -row 1 -column 3 -sticky ew
498 grid .nameC -row 1 -column 5 -sticky ew
499 grid .nameD -row 1 -column 7 -sticky ew
500 eval grid [cols] -row 2 -sticky nsew
501 grid .sby -row 2 -column 8 -sticky ns
502 grid .sbxA -row 3 -column 1 -sticky ew
503 grid .sbxB -row 3 -column 3 -sticky ew
504 grid .sbxC -row 3 -column 5 -sticky ew
505 grid .sbxD -row 3 -column 7 -sticky ew
506
507
508 .spacer config -height [winfo height .sbxA]
509 wm deiconify .
510
+68 -29
--- src/merge3.c
+++ src/merge3.c
@@ -334,37 +334,26 @@
334334
** represent the content for one line from baseline, v1, v2, and output
335335
** respectively. The first character of each token provides auxiliary
336336
** information:
337337
**
338338
** . This line is omitted.
339
+** N Name of the file.
339340
** T Literal text follows that should have a \n terminator.
340341
** R Literal text follows that needs a \r\n terminator.
342
+** X Merge conflict. (Column 4 only)
341343
** Z Literal text without a line terminator.
342344
** S Skipped lines in all 4 files.
343345
** 1 Text is a copy of token 1
344346
** 2 Use data from data-token 2
345347
** 3 Use data from data-token 3
346348
*/
347349
348
-/* Copy one line of text from pIn and append to pOut, encoded as TCL */
349
-static void tclLineOfText(Blob *pOut, Blob *pIn){
350
- int i, j, k;
351
- for(i=pIn->iCursor; i<pIn->nUsed && pIn->aData[i]!='\n'; i++){}
352
- if( i==pIn->nUsed ){
353
- blob_append(pOut, "\"Z", 2);
354
- k = i;
355
- }else if( i>pIn->iCursor && pIn->aData[i-1]=='\r' ){
356
- blob_append(pOut, "\"R", 2);
357
- k = i-1;
358
- i++;
359
- }else{
360
- blob_append(pOut, "\"T", 2);
361
- k = i;
362
- i++;
363
- }
364
- for(j=pIn->iCursor; j<k; j++){
365
- char c = pIn->aData[j];
350
+/* Write text that goes into the interior of a double-quoted string in TCL */
351
+static void tclWriteQuotedText(Blob *pOut, const char *zIn, int nIn){
352
+ int j;
353
+ for(j=0; j<nIn; j++){
354
+ char c = zIn[j];
366355
if( c=='\\' ){
367356
blob_append(pOut, "\\\\", 2);
368357
}else if( c=='"' ){
369358
blob_append(pOut, "\\\"", 2);
370359
}else if( c<' ' || c>0x7e ){
@@ -377,12 +366,47 @@
377366
blob_append(pOut, z, 4);
378367
}else{
379368
blob_append_char(pOut, c);
380369
}
381370
}
371
+}
372
+
373
+/* Copy one line of text from pIn and append to pOut, encoded as TCL */
374
+static void tclLineOfText(Blob *pOut, Blob *pIn){
375
+ int i, k;
376
+ for(i=pIn->iCursor; i<pIn->nUsed && pIn->aData[i]!='\n'; i++){}
377
+ if( i==pIn->nUsed ){
378
+ blob_append(pOut, "\"Z", 2);
379
+ k = i;
380
+ }else if( i>pIn->iCursor && pIn->aData[i-1]=='\r' ){
381
+ blob_append(pOut, "\"R", 2);
382
+ k = i-1;
383
+ i++;
384
+ }else{
385
+ blob_append(pOut, "\"T", 2);
386
+ k = i;
387
+ i++;
388
+ }
389
+ tclWriteQuotedText(pOut, pIn->aData+pIn->iCursor, k-pIn->iCursor);
382390
pIn->iCursor = i;
383391
blob_append_char(pOut, '"');
392
+}
393
+static void tclStart(MergeBuilder *p){
394
+ Blob *pOut = p->pOut;
395
+ blob_append(pOut, "\"N", 2);
396
+ tclWriteQuotedText(pOut, p->zPivot, (int)strlen(p->zPivot));
397
+ blob_append(pOut, "\" \"N", 4);
398
+ tclWriteQuotedText(pOut, p->zV1, (int)strlen(p->zV1));
399
+ blob_append(pOut, "\" \"N", 4);
400
+ tclWriteQuotedText(pOut, p->zV2, (int)strlen(p->zV2));
401
+ blob_append(pOut, "\" \"N", 4);
402
+ if( p->zOut ){
403
+ tclWriteQuotedText(pOut, p->zOut, (int)strlen(p->zOut));
404
+ }else{
405
+ blob_append(pOut, "(Merge Result)", -1);
406
+ }
407
+ blob_append(pOut, "\"\n", 2);
384408
}
385409
static void tclSame(MergeBuilder *p, unsigned int N){
386410
int i = 0;
387411
int nSkip;
388412
@@ -523,10 +547,11 @@
523547
p->lnV1 += nV1;
524548
p->lnV2 += nV2;
525549
}
526550
static void mergebuilder_init_tcl(MergeBuilder *p){
527551
mergebuilder_init(p);
552
+ p->xStart = tclStart;
528553
p->xSame = tclSame;
529554
p->xChngV1 = tclChngV1;
530555
p->xChngV2 = tclChngV2;
531556
p->xChngBoth = tclChngBoth;
532557
p->xConflict = tclConflict;
@@ -768,17 +793,26 @@
768793
** directly. Otherwise:
769794
** (1) Write the Tcl/Tk script used for rendering into a temp file.
770795
** (2) Invoke "tclsh" on the temp file using fossil_system().
771796
** (3) Delete the temp file.
772797
*/
773
-void merge_tk(const char *zSubCmd, int firstArg, int nContext){
798
+void merge_tk(const char *zSubCmd, int firstArg){
774799
int i;
775800
Blob script;
776801
const char *zTempFile = 0;
777802
char *zCmd;
778803
const char *zTclsh;
804
+ const char *zCnt;
779805
int bDarkMode = find_option("dark",0,0)!=0;
806
+ int nContext;
807
+ zCnt = find_option("context", "c", 1);
808
+ if( zCnt==0 ){
809
+ nContext = 6;
810
+ }else{
811
+ nContext = atoi(zCnt);
812
+ if( nContext<0 ) nContext = 0xfffffff;
813
+ }
780814
blob_zero(&script);
781815
blob_appendf(&script, "set fossilcmd {| \"%/\" %s -tcl -c %d",
782816
g.nameOfExe, zSubCmd, nContext);
783817
find_option("tcl",0,0);
784818
find_option("debug",0,0);
@@ -788,10 +822,16 @@
788822
}
789823
/* The undocumented --script FILENAME option causes the Tk script to
790824
** be written into the FILENAME instead of being run. This is used
791825
** for testing and debugging. */
792826
zTempFile = find_option("script",0,1);
827
+ verify_all_options();
828
+
829
+ if( (g.argc - firstArg)!=3 ){
830
+ fossil_fatal("Requires 3 filename arguments");
831
+ }
832
+
793833
for(i=firstArg; i<g.argc; i++){
794834
const char *z = g.argv[i];
795835
if( sqlite3_strglob("*}*",z) ){
796836
blob_appendf(&script, " {%/}", z);
797837
}else{
@@ -861,22 +901,24 @@
861901
void merge_3way_cmd(void){
862902
MergeBuilder s;
863903
int nConflict;
864904
Blob pivot, v1, v2, out;
865905
int noWarn = 0;
866
- int flagTk = 0;
867906
const char *zCnt;
868907
908
+ if( find_option("tk", 0, 0)!=0 ){
909
+ merge_tk("3-way-merge", 2);
910
+ return;
911
+ }
869912
mergebuilder_init_text(&s);
870913
if( find_option("debug", 0, 0) ){
871914
mergebuilder_init(&s);
872915
}
873916
if( find_option("tcl", 0, 0) ){
874917
mergebuilder_init_tcl(&s);
875918
noWarn = 1;
876919
}
877
- flagTk = find_option("tk", 0, 0)!=0;
878920
zCnt = find_option("context", "c", 1);
879921
if( zCnt ){
880922
s.nContext = atoi(zCnt);
881923
if( s.nContext<0 ) s.nContext = 0xfffffff;
882924
}else{
@@ -891,18 +933,13 @@
891933
verify_all_options();
892934
893935
if( g.argc!=6 && g.argc!=5 ){
894936
usage("[OPTIONS] PIVOT V1 V2 [MERGED]");
895937
}
896
- if( flagTk ){
897
- if( g.argc==6 ){
898
- fossil_fatal("Cannot use an output file (\"%s\") with the --tk option",
899
- g.argv[5]);
900
- }
901
- merge_tk("3-way-merge", 2, s.nContext);
902
- return;
903
- }
938
+ s.zPivot = file_tail(g.argv[2]);
939
+ s.zV1 = file_tail(g.argv[3]);
940
+ s.zV2 = file_tail(g.argv[4]);
904941
if( blob_read_from_file(s.pPivot, g.argv[2], ExtFILE)<0 ){
905942
fossil_fatal("cannot read %s", g.argv[2]);
906943
}
907944
if( blob_read_from_file(s.pV1, g.argv[3], ExtFILE)<0 ){
908945
fossil_fatal("cannot read %s", g.argv[3]);
@@ -910,12 +947,14 @@
910947
if( blob_read_from_file(s.pV2, g.argv[4], ExtFILE)<0 ){
911948
fossil_fatal("cannot read %s", g.argv[4]);
912949
}
913950
nConflict = merge_three_blobs(&s);
914951
if( g.argc==6 ){
952
+ s.zOut = file_tail(g.argv[5]);
915953
blob_write_to_file(s.pOut, g.argv[5]);
916954
}else{
955
+ s.zOut = "(Merge Result)";
917956
blob_write_to_file(s.pOut, "-");
918957
}
919958
s.xDestroy(&s);
920959
blob_reset(&pivot);
921960
blob_reset(&v1);
922961
--- src/merge3.c
+++ src/merge3.c
@@ -334,37 +334,26 @@
334 ** represent the content for one line from baseline, v1, v2, and output
335 ** respectively. The first character of each token provides auxiliary
336 ** information:
337 **
338 ** . This line is omitted.
 
339 ** T Literal text follows that should have a \n terminator.
340 ** R Literal text follows that needs a \r\n terminator.
 
341 ** Z Literal text without a line terminator.
342 ** S Skipped lines in all 4 files.
343 ** 1 Text is a copy of token 1
344 ** 2 Use data from data-token 2
345 ** 3 Use data from data-token 3
346 */
347
348 /* Copy one line of text from pIn and append to pOut, encoded as TCL */
349 static void tclLineOfText(Blob *pOut, Blob *pIn){
350 int i, j, k;
351 for(i=pIn->iCursor; i<pIn->nUsed && pIn->aData[i]!='\n'; i++){}
352 if( i==pIn->nUsed ){
353 blob_append(pOut, "\"Z", 2);
354 k = i;
355 }else if( i>pIn->iCursor && pIn->aData[i-1]=='\r' ){
356 blob_append(pOut, "\"R", 2);
357 k = i-1;
358 i++;
359 }else{
360 blob_append(pOut, "\"T", 2);
361 k = i;
362 i++;
363 }
364 for(j=pIn->iCursor; j<k; j++){
365 char c = pIn->aData[j];
366 if( c=='\\' ){
367 blob_append(pOut, "\\\\", 2);
368 }else if( c=='"' ){
369 blob_append(pOut, "\\\"", 2);
370 }else if( c<' ' || c>0x7e ){
@@ -377,12 +366,47 @@
377 blob_append(pOut, z, 4);
378 }else{
379 blob_append_char(pOut, c);
380 }
381 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
382 pIn->iCursor = i;
383 blob_append_char(pOut, '"');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
384 }
385 static void tclSame(MergeBuilder *p, unsigned int N){
386 int i = 0;
387 int nSkip;
388
@@ -523,10 +547,11 @@
523 p->lnV1 += nV1;
524 p->lnV2 += nV2;
525 }
526 static void mergebuilder_init_tcl(MergeBuilder *p){
527 mergebuilder_init(p);
 
528 p->xSame = tclSame;
529 p->xChngV1 = tclChngV1;
530 p->xChngV2 = tclChngV2;
531 p->xChngBoth = tclChngBoth;
532 p->xConflict = tclConflict;
@@ -768,17 +793,26 @@
768 ** directly. Otherwise:
769 ** (1) Write the Tcl/Tk script used for rendering into a temp file.
770 ** (2) Invoke "tclsh" on the temp file using fossil_system().
771 ** (3) Delete the temp file.
772 */
773 void merge_tk(const char *zSubCmd, int firstArg, int nContext){
774 int i;
775 Blob script;
776 const char *zTempFile = 0;
777 char *zCmd;
778 const char *zTclsh;
 
779 int bDarkMode = find_option("dark",0,0)!=0;
 
 
 
 
 
 
 
 
780 blob_zero(&script);
781 blob_appendf(&script, "set fossilcmd {| \"%/\" %s -tcl -c %d",
782 g.nameOfExe, zSubCmd, nContext);
783 find_option("tcl",0,0);
784 find_option("debug",0,0);
@@ -788,10 +822,16 @@
788 }
789 /* The undocumented --script FILENAME option causes the Tk script to
790 ** be written into the FILENAME instead of being run. This is used
791 ** for testing and debugging. */
792 zTempFile = find_option("script",0,1);
 
 
 
 
 
 
793 for(i=firstArg; i<g.argc; i++){
794 const char *z = g.argv[i];
795 if( sqlite3_strglob("*}*",z) ){
796 blob_appendf(&script, " {%/}", z);
797 }else{
@@ -861,22 +901,24 @@
861 void merge_3way_cmd(void){
862 MergeBuilder s;
863 int nConflict;
864 Blob pivot, v1, v2, out;
865 int noWarn = 0;
866 int flagTk = 0;
867 const char *zCnt;
868
 
 
 
 
869 mergebuilder_init_text(&s);
870 if( find_option("debug", 0, 0) ){
871 mergebuilder_init(&s);
872 }
873 if( find_option("tcl", 0, 0) ){
874 mergebuilder_init_tcl(&s);
875 noWarn = 1;
876 }
877 flagTk = find_option("tk", 0, 0)!=0;
878 zCnt = find_option("context", "c", 1);
879 if( zCnt ){
880 s.nContext = atoi(zCnt);
881 if( s.nContext<0 ) s.nContext = 0xfffffff;
882 }else{
@@ -891,18 +933,13 @@
891 verify_all_options();
892
893 if( g.argc!=6 && g.argc!=5 ){
894 usage("[OPTIONS] PIVOT V1 V2 [MERGED]");
895 }
896 if( flagTk ){
897 if( g.argc==6 ){
898 fossil_fatal("Cannot use an output file (\"%s\") with the --tk option",
899 g.argv[5]);
900 }
901 merge_tk("3-way-merge", 2, s.nContext);
902 return;
903 }
904 if( blob_read_from_file(s.pPivot, g.argv[2], ExtFILE)<0 ){
905 fossil_fatal("cannot read %s", g.argv[2]);
906 }
907 if( blob_read_from_file(s.pV1, g.argv[3], ExtFILE)<0 ){
908 fossil_fatal("cannot read %s", g.argv[3]);
@@ -910,12 +947,14 @@
910 if( blob_read_from_file(s.pV2, g.argv[4], ExtFILE)<0 ){
911 fossil_fatal("cannot read %s", g.argv[4]);
912 }
913 nConflict = merge_three_blobs(&s);
914 if( g.argc==6 ){
 
915 blob_write_to_file(s.pOut, g.argv[5]);
916 }else{
 
917 blob_write_to_file(s.pOut, "-");
918 }
919 s.xDestroy(&s);
920 blob_reset(&pivot);
921 blob_reset(&v1);
922
--- src/merge3.c
+++ src/merge3.c
@@ -334,37 +334,26 @@
334 ** represent the content for one line from baseline, v1, v2, and output
335 ** respectively. The first character of each token provides auxiliary
336 ** information:
337 **
338 ** . This line is omitted.
339 ** N Name of the file.
340 ** T Literal text follows that should have a \n terminator.
341 ** R Literal text follows that needs a \r\n terminator.
342 ** X Merge conflict. (Column 4 only)
343 ** Z Literal text without a line terminator.
344 ** S Skipped lines in all 4 files.
345 ** 1 Text is a copy of token 1
346 ** 2 Use data from data-token 2
347 ** 3 Use data from data-token 3
348 */
349
350 /* Write text that goes into the interior of a double-quoted string in TCL */
351 static void tclWriteQuotedText(Blob *pOut, const char *zIn, int nIn){
352 int j;
353 for(j=0; j<nIn; j++){
354 char c = zIn[j];
 
 
 
 
 
 
 
 
 
 
 
 
 
355 if( c=='\\' ){
356 blob_append(pOut, "\\\\", 2);
357 }else if( c=='"' ){
358 blob_append(pOut, "\\\"", 2);
359 }else if( c<' ' || c>0x7e ){
@@ -377,12 +366,47 @@
366 blob_append(pOut, z, 4);
367 }else{
368 blob_append_char(pOut, c);
369 }
370 }
371 }
372
373 /* Copy one line of text from pIn and append to pOut, encoded as TCL */
374 static void tclLineOfText(Blob *pOut, Blob *pIn){
375 int i, k;
376 for(i=pIn->iCursor; i<pIn->nUsed && pIn->aData[i]!='\n'; i++){}
377 if( i==pIn->nUsed ){
378 blob_append(pOut, "\"Z", 2);
379 k = i;
380 }else if( i>pIn->iCursor && pIn->aData[i-1]=='\r' ){
381 blob_append(pOut, "\"R", 2);
382 k = i-1;
383 i++;
384 }else{
385 blob_append(pOut, "\"T", 2);
386 k = i;
387 i++;
388 }
389 tclWriteQuotedText(pOut, pIn->aData+pIn->iCursor, k-pIn->iCursor);
390 pIn->iCursor = i;
391 blob_append_char(pOut, '"');
392 }
393 static void tclStart(MergeBuilder *p){
394 Blob *pOut = p->pOut;
395 blob_append(pOut, "\"N", 2);
396 tclWriteQuotedText(pOut, p->zPivot, (int)strlen(p->zPivot));
397 blob_append(pOut, "\" \"N", 4);
398 tclWriteQuotedText(pOut, p->zV1, (int)strlen(p->zV1));
399 blob_append(pOut, "\" \"N", 4);
400 tclWriteQuotedText(pOut, p->zV2, (int)strlen(p->zV2));
401 blob_append(pOut, "\" \"N", 4);
402 if( p->zOut ){
403 tclWriteQuotedText(pOut, p->zOut, (int)strlen(p->zOut));
404 }else{
405 blob_append(pOut, "(Merge Result)", -1);
406 }
407 blob_append(pOut, "\"\n", 2);
408 }
409 static void tclSame(MergeBuilder *p, unsigned int N){
410 int i = 0;
411 int nSkip;
412
@@ -523,10 +547,11 @@
547 p->lnV1 += nV1;
548 p->lnV2 += nV2;
549 }
550 static void mergebuilder_init_tcl(MergeBuilder *p){
551 mergebuilder_init(p);
552 p->xStart = tclStart;
553 p->xSame = tclSame;
554 p->xChngV1 = tclChngV1;
555 p->xChngV2 = tclChngV2;
556 p->xChngBoth = tclChngBoth;
557 p->xConflict = tclConflict;
@@ -768,17 +793,26 @@
793 ** directly. Otherwise:
794 ** (1) Write the Tcl/Tk script used for rendering into a temp file.
795 ** (2) Invoke "tclsh" on the temp file using fossil_system().
796 ** (3) Delete the temp file.
797 */
798 void merge_tk(const char *zSubCmd, int firstArg){
799 int i;
800 Blob script;
801 const char *zTempFile = 0;
802 char *zCmd;
803 const char *zTclsh;
804 const char *zCnt;
805 int bDarkMode = find_option("dark",0,0)!=0;
806 int nContext;
807 zCnt = find_option("context", "c", 1);
808 if( zCnt==0 ){
809 nContext = 6;
810 }else{
811 nContext = atoi(zCnt);
812 if( nContext<0 ) nContext = 0xfffffff;
813 }
814 blob_zero(&script);
815 blob_appendf(&script, "set fossilcmd {| \"%/\" %s -tcl -c %d",
816 g.nameOfExe, zSubCmd, nContext);
817 find_option("tcl",0,0);
818 find_option("debug",0,0);
@@ -788,10 +822,16 @@
822 }
823 /* The undocumented --script FILENAME option causes the Tk script to
824 ** be written into the FILENAME instead of being run. This is used
825 ** for testing and debugging. */
826 zTempFile = find_option("script",0,1);
827 verify_all_options();
828
829 if( (g.argc - firstArg)!=3 ){
830 fossil_fatal("Requires 3 filename arguments");
831 }
832
833 for(i=firstArg; i<g.argc; i++){
834 const char *z = g.argv[i];
835 if( sqlite3_strglob("*}*",z) ){
836 blob_appendf(&script, " {%/}", z);
837 }else{
@@ -861,22 +901,24 @@
901 void merge_3way_cmd(void){
902 MergeBuilder s;
903 int nConflict;
904 Blob pivot, v1, v2, out;
905 int noWarn = 0;
 
906 const char *zCnt;
907
908 if( find_option("tk", 0, 0)!=0 ){
909 merge_tk("3-way-merge", 2);
910 return;
911 }
912 mergebuilder_init_text(&s);
913 if( find_option("debug", 0, 0) ){
914 mergebuilder_init(&s);
915 }
916 if( find_option("tcl", 0, 0) ){
917 mergebuilder_init_tcl(&s);
918 noWarn = 1;
919 }
 
920 zCnt = find_option("context", "c", 1);
921 if( zCnt ){
922 s.nContext = atoi(zCnt);
923 if( s.nContext<0 ) s.nContext = 0xfffffff;
924 }else{
@@ -891,18 +933,13 @@
933 verify_all_options();
934
935 if( g.argc!=6 && g.argc!=5 ){
936 usage("[OPTIONS] PIVOT V1 V2 [MERGED]");
937 }
938 s.zPivot = file_tail(g.argv[2]);
939 s.zV1 = file_tail(g.argv[3]);
940 s.zV2 = file_tail(g.argv[4]);
 
 
 
 
 
941 if( blob_read_from_file(s.pPivot, g.argv[2], ExtFILE)<0 ){
942 fossil_fatal("cannot read %s", g.argv[2]);
943 }
944 if( blob_read_from_file(s.pV1, g.argv[3], ExtFILE)<0 ){
945 fossil_fatal("cannot read %s", g.argv[3]);
@@ -910,12 +947,14 @@
947 if( blob_read_from_file(s.pV2, g.argv[4], ExtFILE)<0 ){
948 fossil_fatal("cannot read %s", g.argv[4]);
949 }
950 nConflict = merge_three_blobs(&s);
951 if( g.argc==6 ){
952 s.zOut = file_tail(g.argv[5]);
953 blob_write_to_file(s.pOut, g.argv[5]);
954 }else{
955 s.zOut = "(Merge Result)";
956 blob_write_to_file(s.pOut, "-");
957 }
958 s.xDestroy(&s);
959 blob_reset(&pivot);
960 blob_reset(&v1);
961

Keyboard Shortcuts

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