Fossil SCM
Add filename labels at the top of each column in the merge display.
Commit
7b8be85206d9940a74ef14259765c519377663aa3c8fbda379c5be497be561b0
Parent
4a70c64a51b284b…
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: | |
| 5 | 5 | # |
| 6 | -# set fossilcmd {| "./fossil" diff --tcl -i -v} | |
| 6 | +# set fossilcmd(file1.txt) {| "./fossil" diff --tcl -i -v} | |
| 7 | 7 | # |
| 8 | 8 | # This header comment is stripped off by the "mkbuiltin.c" program. |
| 9 | 9 | # |
| 10 | 10 | package require Tk |
| 11 | 11 | |
| @@ -129,10 +129,12 @@ | ||
| 129 | 129 | set key4 [string index $D 0] |
| 130 | 130 | if {$key4=="X"} {set dtag rm} {set dtag -} |
| 131 | 131 | if {$key1=="."} { |
| 132 | 132 | .lnA insert end \n - |
| 133 | 133 | .txtA insert end \n $dtag |
| 134 | + } elseif {$key1=="N"} { | |
| 135 | + .nameA config -text [string range $A 1 end] | |
| 134 | 136 | } else { |
| 135 | 137 | .lnA insert end $lnA\n - |
| 136 | 138 | incr lnA |
| 137 | 139 | .txtA insert end [string range $A 1 end]\n $dtag |
| 138 | 140 | } |
| @@ -143,10 +145,12 @@ | ||
| 143 | 145 | .lnB insert end $lnB\n - |
| 144 | 146 | incr lnB |
| 145 | 147 | if {$key4=="2"} {set tag chng} {set tag $dtag} |
| 146 | 148 | if {$key2=="1"} { |
| 147 | 149 | .txtB insert end [string range $A 1 end]\n $tag |
| 150 | + } elseif {$key2=="N"} { | |
| 151 | + .nameB config -text [string range $B 1 end] | |
| 148 | 152 | } else { |
| 149 | 153 | .txtB insert end [string range $B 1 end]\n $tag |
| 150 | 154 | } |
| 151 | 155 | } |
| 152 | 156 | if {$key3=="."} { |
| @@ -158,10 +162,12 @@ | ||
| 158 | 162 | if {$key4=="3"} {set tag add} {set tag $dtag} |
| 159 | 163 | if {$key3=="1"} { |
| 160 | 164 | .txtC insert end [string range $A 1 end]\n $tag |
| 161 | 165 | } elseif {$key3=="2"} { |
| 162 | 166 | .txtC insert end [string range $B 1 end]\n chng |
| 167 | + } elseif {$key3=="N"} { | |
| 168 | + .nameC config -text [string range $C 1 end] | |
| 163 | 169 | } else { |
| 164 | 170 | .txtC insert end [string range $C 1 end]\n $tag |
| 165 | 171 | } |
| 166 | 172 | } |
| 167 | 173 | if {$key4=="." || $key4=="X"} { |
| @@ -174,10 +180,12 @@ | ||
| 174 | 180 | .txtD insert end [string range $A 1 end]\n - |
| 175 | 181 | } elseif {$key4=="2"} { |
| 176 | 182 | .txtD insert end [string range $B 1 end]\n chng |
| 177 | 183 | } elseif {$key4=="3"} { |
| 178 | 184 | .txtD insert end [string range $C 1 end]\n add |
| 185 | + } elseif {$key4=="N"} { | |
| 186 | + .nameD config -text [string range $D 1 end] | |
| 179 | 187 | } else { |
| 180 | 188 | .txtD insert end [string range $D 1 end]\n - |
| 181 | 189 | } |
| 182 | 190 | } |
| 183 | 191 | } |
| @@ -239,19 +247,19 @@ | ||
| 239 | 247 | } |
| 240 | 248 | |
| 241 | 249 | proc sync-x {col first last} { |
| 242 | 250 | disableSync x |
| 243 | 251 | $col xview moveto [expr {$first*[xvis $col]/($last-$first)}] |
| 244 | - foreach side {A B} { | |
| 252 | + foreach side {A B C D} { | |
| 245 | 253 | set sb .sbx$side |
| 246 | 254 | 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 | +# } | |
| 253 | 261 | } |
| 254 | 262 | enableSync x |
| 255 | 263 | } |
| 256 | 264 | |
| 257 | 265 | proc sync-y {first last} { |
| @@ -382,10 +390,14 @@ | ||
| 382 | 390 | $c tag config fn -spacing1 $CFG(FN_PAD) -spacing3 $CFG(FN_PAD) |
| 383 | 391 | bindtags $c ". $c Text all" |
| 384 | 392 | bind $c <1> {focus %W} |
| 385 | 393 | } |
| 386 | 394 | |
| 395 | +label .nameA | |
| 396 | +label .nameB | |
| 397 | +label .nameC | |
| 398 | +label .nameD -text {Merge Result} | |
| 387 | 399 | ::ttk::scrollbar .sby -command {.txtA yview} -orient vertical |
| 388 | 400 | ::ttk::scrollbar .sbxA -command {.txtA xview} -orient horizontal |
| 389 | 401 | ::ttk::scrollbar .sbxB -command {.txtB xview} -orient horizontal |
| 390 | 402 | ::ttk::scrollbar .sbxC -command {.txtC xview} -orient horizontal |
| 391 | 403 | ::ttk::scrollbar .sbxD -command {.txtD xview} -orient horizontal |
| @@ -479,13 +491,19 @@ | ||
| 479 | 491 | grid columnconfigure . $rn -weight 1 -uniform a |
| 480 | 492 | grid columnconfigure . [expr {$rn+1}] -weight 1 -uniform b |
| 481 | 493 | incr rn 2 |
| 482 | 494 | } |
| 483 | 495 | 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 | + | |
| 489 | 507 | |
| 490 | 508 | .spacer config -height [winfo height .sbxA] |
| 491 | 509 | wm deiconify . |
| 492 | 510 |
| --- 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 @@ | ||
| 334 | 334 | ** represent the content for one line from baseline, v1, v2, and output |
| 335 | 335 | ** respectively. The first character of each token provides auxiliary |
| 336 | 336 | ** information: |
| 337 | 337 | ** |
| 338 | 338 | ** . This line is omitted. |
| 339 | +** N Name of the file. | |
| 339 | 340 | ** T Literal text follows that should have a \n terminator. |
| 340 | 341 | ** R Literal text follows that needs a \r\n terminator. |
| 342 | +** X Merge conflict. (Column 4 only) | |
| 341 | 343 | ** Z Literal text without a line terminator. |
| 342 | 344 | ** S Skipped lines in all 4 files. |
| 343 | 345 | ** 1 Text is a copy of token 1 |
| 344 | 346 | ** 2 Use data from data-token 2 |
| 345 | 347 | ** 3 Use data from data-token 3 |
| 346 | 348 | */ |
| 347 | 349 | |
| 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]; | |
| 366 | 355 | if( c=='\\' ){ |
| 367 | 356 | blob_append(pOut, "\\\\", 2); |
| 368 | 357 | }else if( c=='"' ){ |
| 369 | 358 | blob_append(pOut, "\\\"", 2); |
| 370 | 359 | }else if( c<' ' || c>0x7e ){ |
| @@ -377,12 +366,47 @@ | ||
| 377 | 366 | blob_append(pOut, z, 4); |
| 378 | 367 | }else{ |
| 379 | 368 | blob_append_char(pOut, c); |
| 380 | 369 | } |
| 381 | 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); | |
| 382 | 390 | pIn->iCursor = i; |
| 383 | 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); | |
| 384 | 408 | } |
| 385 | 409 | static void tclSame(MergeBuilder *p, unsigned int N){ |
| 386 | 410 | int i = 0; |
| 387 | 411 | int nSkip; |
| 388 | 412 | |
| @@ -523,10 +547,11 @@ | ||
| 523 | 547 | p->lnV1 += nV1; |
| 524 | 548 | p->lnV2 += nV2; |
| 525 | 549 | } |
| 526 | 550 | static void mergebuilder_init_tcl(MergeBuilder *p){ |
| 527 | 551 | mergebuilder_init(p); |
| 552 | + p->xStart = tclStart; | |
| 528 | 553 | p->xSame = tclSame; |
| 529 | 554 | p->xChngV1 = tclChngV1; |
| 530 | 555 | p->xChngV2 = tclChngV2; |
| 531 | 556 | p->xChngBoth = tclChngBoth; |
| 532 | 557 | p->xConflict = tclConflict; |
| @@ -768,17 +793,26 @@ | ||
| 768 | 793 | ** directly. Otherwise: |
| 769 | 794 | ** (1) Write the Tcl/Tk script used for rendering into a temp file. |
| 770 | 795 | ** (2) Invoke "tclsh" on the temp file using fossil_system(). |
| 771 | 796 | ** (3) Delete the temp file. |
| 772 | 797 | */ |
| 773 | -void merge_tk(const char *zSubCmd, int firstArg, int nContext){ | |
| 798 | +void merge_tk(const char *zSubCmd, int firstArg){ | |
| 774 | 799 | int i; |
| 775 | 800 | Blob script; |
| 776 | 801 | const char *zTempFile = 0; |
| 777 | 802 | char *zCmd; |
| 778 | 803 | const char *zTclsh; |
| 804 | + const char *zCnt; | |
| 779 | 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 | + } | |
| 780 | 814 | blob_zero(&script); |
| 781 | 815 | blob_appendf(&script, "set fossilcmd {| \"%/\" %s -tcl -c %d", |
| 782 | 816 | g.nameOfExe, zSubCmd, nContext); |
| 783 | 817 | find_option("tcl",0,0); |
| 784 | 818 | find_option("debug",0,0); |
| @@ -788,10 +822,16 @@ | ||
| 788 | 822 | } |
| 789 | 823 | /* The undocumented --script FILENAME option causes the Tk script to |
| 790 | 824 | ** be written into the FILENAME instead of being run. This is used |
| 791 | 825 | ** for testing and debugging. */ |
| 792 | 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 | + | |
| 793 | 833 | for(i=firstArg; i<g.argc; i++){ |
| 794 | 834 | const char *z = g.argv[i]; |
| 795 | 835 | if( sqlite3_strglob("*}*",z) ){ |
| 796 | 836 | blob_appendf(&script, " {%/}", z); |
| 797 | 837 | }else{ |
| @@ -861,22 +901,24 @@ | ||
| 861 | 901 | void merge_3way_cmd(void){ |
| 862 | 902 | MergeBuilder s; |
| 863 | 903 | int nConflict; |
| 864 | 904 | Blob pivot, v1, v2, out; |
| 865 | 905 | int noWarn = 0; |
| 866 | - int flagTk = 0; | |
| 867 | 906 | const char *zCnt; |
| 868 | 907 | |
| 908 | + if( find_option("tk", 0, 0)!=0 ){ | |
| 909 | + merge_tk("3-way-merge", 2); | |
| 910 | + return; | |
| 911 | + } | |
| 869 | 912 | mergebuilder_init_text(&s); |
| 870 | 913 | if( find_option("debug", 0, 0) ){ |
| 871 | 914 | mergebuilder_init(&s); |
| 872 | 915 | } |
| 873 | 916 | if( find_option("tcl", 0, 0) ){ |
| 874 | 917 | mergebuilder_init_tcl(&s); |
| 875 | 918 | noWarn = 1; |
| 876 | 919 | } |
| 877 | - flagTk = find_option("tk", 0, 0)!=0; | |
| 878 | 920 | zCnt = find_option("context", "c", 1); |
| 879 | 921 | if( zCnt ){ |
| 880 | 922 | s.nContext = atoi(zCnt); |
| 881 | 923 | if( s.nContext<0 ) s.nContext = 0xfffffff; |
| 882 | 924 | }else{ |
| @@ -891,18 +933,13 @@ | ||
| 891 | 933 | verify_all_options(); |
| 892 | 934 | |
| 893 | 935 | if( g.argc!=6 && g.argc!=5 ){ |
| 894 | 936 | usage("[OPTIONS] PIVOT V1 V2 [MERGED]"); |
| 895 | 937 | } |
| 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]); | |
| 904 | 941 | if( blob_read_from_file(s.pPivot, g.argv[2], ExtFILE)<0 ){ |
| 905 | 942 | fossil_fatal("cannot read %s", g.argv[2]); |
| 906 | 943 | } |
| 907 | 944 | if( blob_read_from_file(s.pV1, g.argv[3], ExtFILE)<0 ){ |
| 908 | 945 | fossil_fatal("cannot read %s", g.argv[3]); |
| @@ -910,12 +947,14 @@ | ||
| 910 | 947 | if( blob_read_from_file(s.pV2, g.argv[4], ExtFILE)<0 ){ |
| 911 | 948 | fossil_fatal("cannot read %s", g.argv[4]); |
| 912 | 949 | } |
| 913 | 950 | nConflict = merge_three_blobs(&s); |
| 914 | 951 | if( g.argc==6 ){ |
| 952 | + s.zOut = file_tail(g.argv[5]); | |
| 915 | 953 | blob_write_to_file(s.pOut, g.argv[5]); |
| 916 | 954 | }else{ |
| 955 | + s.zOut = "(Merge Result)"; | |
| 917 | 956 | blob_write_to_file(s.pOut, "-"); |
| 918 | 957 | } |
| 919 | 958 | s.xDestroy(&s); |
| 920 | 959 | blob_reset(&pivot); |
| 921 | 960 | blob_reset(&v1); |
| 922 | 961 |
| --- 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 |