Fossil SCM
Bring the merge-info-html branch up to date with trunk, step 2 of 2.
Commit
c2b1c2c9fa3ef699d5087f2c700774e52132e03f325a2fa4097cd763c91363bf
Parent
1140b020de296a7…
7 files changed
+52
-3
+39
-13
+10
+10
+156
-79
+156
-79
+48
-15
+52
-3
| --- src/diff.c | ||
| +++ src/diff.c | ||
| @@ -108,10 +108,11 @@ | ||
| 108 | 108 | u32 nFile; /* Number of files diffed so far */ |
| 109 | 109 | const char *zDiffCmd; /* External diff command to use instead of builtin */ |
| 110 | 110 | const char *zBinGlob; /* GLOB pattern for binary files */ |
| 111 | 111 | ReCompiled *pRe; /* Show only changes matching this pattern */ |
| 112 | 112 | const char *zLeftHash; /* HASH-id of the left file */ |
| 113 | + const char *azLabel[2]; /* Optional labels for left and right files */ | |
| 113 | 114 | }; |
| 114 | 115 | |
| 115 | 116 | #endif /* INTERFACE */ |
| 116 | 117 | |
| 117 | 118 | /* |
| @@ -3236,10 +3237,11 @@ | ||
| 3236 | 3237 | ** --dark Use dark mode for Tcl/Tk and HTML output |
| 3237 | 3238 | ** --html Format for HTML DIFF_HTML |
| 3238 | 3239 | ** -i|--internal Use built-in diff, not an external tool |
| 3239 | 3240 | ** --invert Invert the diff DIFF_INVERT |
| 3240 | 3241 | ** --json Output formatted as JSON |
| 3242 | +** --label NAME Column label. Can be repeated once. | |
| 3241 | 3243 | ** -n|--linenum Show line numbers DIFF_LINENO |
| 3242 | 3244 | ** -N|--new-file Alias for --verbose |
| 3243 | 3245 | ** --noopt Disable optimization DIFF_NOOPT |
| 3244 | 3246 | ** --numstat Show change counts DIFF_NUMSTAT |
| 3245 | 3247 | ** --strip-trailing-cr Strip trailing CR DIFF_STRIP_EOLCR |
| @@ -3308,10 +3310,14 @@ | ||
| 3308 | 3310 | } |
| 3309 | 3311 | } |
| 3310 | 3312 | if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){ |
| 3311 | 3313 | pCfg->wColumn = f; |
| 3312 | 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 | + } | |
| 3313 | 3319 | if( find_option("linenum","n",0)!=0 ) diffFlags |= DIFF_LINENO; |
| 3314 | 3320 | if( find_option("noopt",0,0)!=0 ) diffFlags |= DIFF_NOOPT; |
| 3315 | 3321 | if( find_option("numstat",0,0)!=0 ) diffFlags |= DIFF_NUMSTAT; |
| 3316 | 3322 | if( find_option("versions","h",0)!=0 ) diffFlags |= DIFF_SHOW_VERS; |
| 3317 | 3323 | if( find_option("dark",0,0)!=0 ) diffFlags |= DIFF_DARKMODE; |
| @@ -3353,13 +3359,10 @@ | ||
| 3353 | 3359 | ** computing differences between files that are under management. |
| 3354 | 3360 | ** |
| 3355 | 3361 | ** This command prints the differences between the two files FILE1 and FILE2. |
| 3356 | 3362 | ** all of the usual diff formatting options (--tk, --by, -c N, etc.) apply. |
| 3357 | 3363 | ** 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 | 3364 | */ |
| 3362 | 3365 | void xdiff_cmd(void){ |
| 3363 | 3366 | Blob a, b, out; |
| 3364 | 3367 | const char *zRe; /* Regex filter for diff output */ |
| 3365 | 3368 | DiffConfig DCfg; |
| @@ -3381,10 +3384,56 @@ | ||
| 3381 | 3384 | blob_zero(&out); |
| 3382 | 3385 | diff_begin(&DCfg); |
| 3383 | 3386 | diff_print_filenames(g.argv[2], g.argv[3], &DCfg, &out); |
| 3384 | 3387 | blob_read_from_file(&a, g.argv[2], ExtFILE); |
| 3385 | 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); | |
| 3386 | 3435 | text_diff(&a, &b, &out, &DCfg); |
| 3387 | 3436 | blob_write_to_file(&out, "-"); |
| 3388 | 3437 | diff_end(&DCfg, 0); |
| 3389 | 3438 | re_free(DCfg.pRe); |
| 3390 | 3439 | } |
| 3391 | 3440 |
| --- 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 @@ | ||
| 91 | 91 | incr ii |
| 92 | 92 | return $x |
| 93 | 93 | } |
| 94 | 94 | |
| 95 | 95 | proc readDiffs {fossilcmd} { |
| 96 | - global difftxt | |
| 96 | + global difftxt debug | |
| 97 | 97 | if {![info exists difftxt]} { |
| 98 | + if {$debug} { | |
| 99 | + puts "# [list open $fossilcmd r]" | |
| 100 | + flush stdout | |
| 101 | + } | |
| 98 | 102 | set in [open $fossilcmd r] |
| 99 | 103 | fconfigure $in -encoding utf-8 |
| 100 | 104 | set difftxt [split [read $in] \n] |
| 101 | 105 | close $in |
| 102 | 106 | } |
| @@ -110,34 +114,56 @@ | ||
| 110 | 114 | |
| 111 | 115 | set fromIndex [lsearch -glob $fossilcmd *-from] |
| 112 | 116 | set toIndex [lsearch -glob $fossilcmd *-to] |
| 113 | 117 | set branchIndex [lsearch -glob $fossilcmd *-branch] |
| 114 | 118 | 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 {} | |
| 117 | 124 | } 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 | + } | |
| 126 | 144 | |
| 127 | 145 | while {[set line [getLine $difftxt $N ii]] != -1} { |
| 128 | 146 | switch -- [lindex $line 0] { |
| 129 | 147 | FILE { |
| 130 | 148 | incr nDiffs |
| 131 | 149 | foreach wx [list [string length $n1] [string length $n2]] { |
| 132 | 150 | if {$wx>$widths(ln)} {set widths(ln) $wx} |
| 133 | 151 | } |
| 134 | 152 | .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 | + } | |
| 136 | 158 | .mkr insert end \n fn \n - |
| 137 | 159 | .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 | + } | |
| 139 | 165 | .wfiles.lb insert end [lindex $line 2] |
| 140 | 166 | set n1 0 |
| 141 | 167 | set n2 0 |
| 142 | 168 | } |
| 143 | 169 | SKIP { |
| 144 | 170 |
| --- 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 |
+10
| --- src/diffcmd.c | ||
| +++ src/diffcmd.c | ||
| @@ -164,10 +164,12 @@ | ||
| 164 | 164 | ){ |
| 165 | 165 | u64 diffFlags = pCfg->diffFlags; |
| 166 | 166 | /* Standardize on /dev/null, regardless of platform. */ |
| 167 | 167 | if( pCfg->diffFlags & DIFF_FILE_ADDED ) zLeft = "/dev/null"; |
| 168 | 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]; | |
| 169 | 171 | if( diffFlags & (DIFF_BRIEF|DIFF_RAW) ){ |
| 170 | 172 | /* no-op */ |
| 171 | 173 | }else if( diffFlags & DIFF_DEBUG ){ |
| 172 | 174 | blob_appendf(pOut, "FILE-LEFT %s\nFILE-RIGHT %s\n", zLeft, zRight); |
| 173 | 175 | }else if( diffFlags & DIFF_WEBPAGE ){ |
| @@ -1205,12 +1207,19 @@ | ||
| 1205 | 1207 | int i; |
| 1206 | 1208 | Blob script; |
| 1207 | 1209 | const char *zTempFile = 0; |
| 1208 | 1210 | char *zCmd; |
| 1209 | 1211 | const char *zTclsh; |
| 1212 | + int bDebug = find_option("tkdebug",0,0)!=0; | |
| 1210 | 1213 | int bDarkMode = find_option("dark",0,0)!=0; |
| 1211 | 1214 | blob_zero(&script); |
| 1215 | + /* Caution: When this routine is called from the merge-info command, | |
| 1216 | + ** the --tcl argument requires an argument. But merge-info does not | |
| 1217 | + ** use -i, so we can take -i as that argument. This routine needs to | |
| 1218 | + ** always have -i after --tcl. | |
| 1219 | + ** CAUTION! | |
| 1220 | + ** vvvvvvv */ | |
| 1212 | 1221 | blob_appendf(&script, "set fossilcmd {| \"%/\" %s -tcl -i -v", |
| 1213 | 1222 | g.nameOfExe, zSubCmd); |
| 1214 | 1223 | find_option("tcl",0,0); |
| 1215 | 1224 | find_option("html",0,0); |
| 1216 | 1225 | find_option("side-by-side","y",0); |
| @@ -1233,10 +1242,11 @@ | ||
| 1233 | 1242 | blob_append(&script, " ", 1); |
| 1234 | 1243 | for(j=0; z[j]; j++) blob_appendf(&script, "\\%03o", (unsigned char)z[j]); |
| 1235 | 1244 | } |
| 1236 | 1245 | } |
| 1237 | 1246 | blob_appendf(&script, "}\nset darkmode %d\n", bDarkMode); |
| 1247 | + blob_appendf(&script, "set debug %d\n", bDebug); | |
| 1238 | 1248 | blob_appendf(&script, "%s", builtin_file("diff.tcl", 0)); |
| 1239 | 1249 | if( zTempFile ){ |
| 1240 | 1250 | blob_write_to_file(&script, zTempFile); |
| 1241 | 1251 | fossil_print("To see diff, run: %s \"%s\"\n", zTclsh, zTempFile); |
| 1242 | 1252 | }else{ |
| 1243 | 1253 |
| --- 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 ){ |
| @@ -1205,12 +1207,19 @@ | |
| 1205 | int i; |
| 1206 | Blob script; |
| 1207 | const char *zTempFile = 0; |
| 1208 | char *zCmd; |
| 1209 | const char *zTclsh; |
| 1210 | int bDarkMode = find_option("dark",0,0)!=0; |
| 1211 | blob_zero(&script); |
| 1212 | blob_appendf(&script, "set fossilcmd {| \"%/\" %s -tcl -i -v", |
| 1213 | g.nameOfExe, zSubCmd); |
| 1214 | find_option("tcl",0,0); |
| 1215 | find_option("html",0,0); |
| 1216 | find_option("side-by-side","y",0); |
| @@ -1233,10 +1242,11 @@ | |
| 1233 | blob_append(&script, " ", 1); |
| 1234 | for(j=0; z[j]; j++) blob_appendf(&script, "\\%03o", (unsigned char)z[j]); |
| 1235 | } |
| 1236 | } |
| 1237 | blob_appendf(&script, "}\nset darkmode %d\n", bDarkMode); |
| 1238 | blob_appendf(&script, "%s", builtin_file("diff.tcl", 0)); |
| 1239 | if( zTempFile ){ |
| 1240 | blob_write_to_file(&script, zTempFile); |
| 1241 | fossil_print("To see diff, run: %s \"%s\"\n", zTclsh, zTempFile); |
| 1242 | }else{ |
| 1243 |
| --- 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 ){ |
| @@ -1205,12 +1207,19 @@ | |
| 1207 | int i; |
| 1208 | Blob script; |
| 1209 | const char *zTempFile = 0; |
| 1210 | char *zCmd; |
| 1211 | const char *zTclsh; |
| 1212 | int bDebug = find_option("tkdebug",0,0)!=0; |
| 1213 | int bDarkMode = find_option("dark",0,0)!=0; |
| 1214 | blob_zero(&script); |
| 1215 | /* Caution: When this routine is called from the merge-info command, |
| 1216 | ** the --tcl argument requires an argument. But merge-info does not |
| 1217 | ** use -i, so we can take -i as that argument. This routine needs to |
| 1218 | ** always have -i after --tcl. |
| 1219 | ** CAUTION! |
| 1220 | ** vvvvvvv */ |
| 1221 | blob_appendf(&script, "set fossilcmd {| \"%/\" %s -tcl -i -v", |
| 1222 | g.nameOfExe, zSubCmd); |
| 1223 | find_option("tcl",0,0); |
| 1224 | find_option("html",0,0); |
| 1225 | find_option("side-by-side","y",0); |
| @@ -1233,10 +1242,11 @@ | |
| 1242 | blob_append(&script, " ", 1); |
| 1243 | for(j=0; z[j]; j++) blob_appendf(&script, "\\%03o", (unsigned char)z[j]); |
| 1244 | } |
| 1245 | } |
| 1246 | blob_appendf(&script, "}\nset darkmode %d\n", bDarkMode); |
| 1247 | blob_appendf(&script, "set debug %d\n", bDebug); |
| 1248 | blob_appendf(&script, "%s", builtin_file("diff.tcl", 0)); |
| 1249 | if( zTempFile ){ |
| 1250 | blob_write_to_file(&script, zTempFile); |
| 1251 | fossil_print("To see diff, run: %s \"%s\"\n", zTclsh, zTempFile); |
| 1252 | }else{ |
| 1253 |
+10
| --- src/diffcmd.c | ||
| +++ src/diffcmd.c | ||
| @@ -164,10 +164,12 @@ | ||
| 164 | 164 | ){ |
| 165 | 165 | u64 diffFlags = pCfg->diffFlags; |
| 166 | 166 | /* Standardize on /dev/null, regardless of platform. */ |
| 167 | 167 | if( pCfg->diffFlags & DIFF_FILE_ADDED ) zLeft = "/dev/null"; |
| 168 | 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]; | |
| 169 | 171 | if( diffFlags & (DIFF_BRIEF|DIFF_RAW) ){ |
| 170 | 172 | /* no-op */ |
| 171 | 173 | }else if( diffFlags & DIFF_DEBUG ){ |
| 172 | 174 | blob_appendf(pOut, "FILE-LEFT %s\nFILE-RIGHT %s\n", zLeft, zRight); |
| 173 | 175 | }else if( diffFlags & DIFF_WEBPAGE ){ |
| @@ -1205,12 +1207,19 @@ | ||
| 1205 | 1207 | int i; |
| 1206 | 1208 | Blob script; |
| 1207 | 1209 | const char *zTempFile = 0; |
| 1208 | 1210 | char *zCmd; |
| 1209 | 1211 | const char *zTclsh; |
| 1212 | + int bDebug = find_option("tkdebug",0,0)!=0; | |
| 1210 | 1213 | int bDarkMode = find_option("dark",0,0)!=0; |
| 1211 | 1214 | blob_zero(&script); |
| 1215 | + /* Caution: When this routine is called from the merge-info command, | |
| 1216 | + ** the --tcl argument requires an argument. But merge-info does not | |
| 1217 | + ** use -i, so we can take -i as that argument. This routine needs to | |
| 1218 | + ** always have -i after --tcl. | |
| 1219 | + ** CAUTION! | |
| 1220 | + ** vvvvvvv */ | |
| 1212 | 1221 | blob_appendf(&script, "set fossilcmd {| \"%/\" %s -tcl -i -v", |
| 1213 | 1222 | g.nameOfExe, zSubCmd); |
| 1214 | 1223 | find_option("tcl",0,0); |
| 1215 | 1224 | find_option("html",0,0); |
| 1216 | 1225 | find_option("side-by-side","y",0); |
| @@ -1233,10 +1242,11 @@ | ||
| 1233 | 1242 | blob_append(&script, " ", 1); |
| 1234 | 1243 | for(j=0; z[j]; j++) blob_appendf(&script, "\\%03o", (unsigned char)z[j]); |
| 1235 | 1244 | } |
| 1236 | 1245 | } |
| 1237 | 1246 | blob_appendf(&script, "}\nset darkmode %d\n", bDarkMode); |
| 1247 | + blob_appendf(&script, "set debug %d\n", bDebug); | |
| 1238 | 1248 | blob_appendf(&script, "%s", builtin_file("diff.tcl", 0)); |
| 1239 | 1249 | if( zTempFile ){ |
| 1240 | 1250 | blob_write_to_file(&script, zTempFile); |
| 1241 | 1251 | fossil_print("To see diff, run: %s \"%s\"\n", zTclsh, zTempFile); |
| 1242 | 1252 | }else{ |
| 1243 | 1253 |
| --- 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 ){ |
| @@ -1205,12 +1207,19 @@ | |
| 1205 | int i; |
| 1206 | Blob script; |
| 1207 | const char *zTempFile = 0; |
| 1208 | char *zCmd; |
| 1209 | const char *zTclsh; |
| 1210 | int bDarkMode = find_option("dark",0,0)!=0; |
| 1211 | blob_zero(&script); |
| 1212 | blob_appendf(&script, "set fossilcmd {| \"%/\" %s -tcl -i -v", |
| 1213 | g.nameOfExe, zSubCmd); |
| 1214 | find_option("tcl",0,0); |
| 1215 | find_option("html",0,0); |
| 1216 | find_option("side-by-side","y",0); |
| @@ -1233,10 +1242,11 @@ | |
| 1233 | blob_append(&script, " ", 1); |
| 1234 | for(j=0; z[j]; j++) blob_appendf(&script, "\\%03o", (unsigned char)z[j]); |
| 1235 | } |
| 1236 | } |
| 1237 | blob_appendf(&script, "}\nset darkmode %d\n", bDarkMode); |
| 1238 | blob_appendf(&script, "%s", builtin_file("diff.tcl", 0)); |
| 1239 | if( zTempFile ){ |
| 1240 | blob_write_to_file(&script, zTempFile); |
| 1241 | fossil_print("To see diff, run: %s \"%s\"\n", zTclsh, zTempFile); |
| 1242 | }else{ |
| 1243 |
| --- 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 ){ |
| @@ -1205,12 +1207,19 @@ | |
| 1207 | int i; |
| 1208 | Blob script; |
| 1209 | const char *zTempFile = 0; |
| 1210 | char *zCmd; |
| 1211 | const char *zTclsh; |
| 1212 | int bDebug = find_option("tkdebug",0,0)!=0; |
| 1213 | int bDarkMode = find_option("dark",0,0)!=0; |
| 1214 | blob_zero(&script); |
| 1215 | /* Caution: When this routine is called from the merge-info command, |
| 1216 | ** the --tcl argument requires an argument. But merge-info does not |
| 1217 | ** use -i, so we can take -i as that argument. This routine needs to |
| 1218 | ** always have -i after --tcl. |
| 1219 | ** CAUTION! |
| 1220 | ** vvvvvvv */ |
| 1221 | blob_appendf(&script, "set fossilcmd {| \"%/\" %s -tcl -i -v", |
| 1222 | g.nameOfExe, zSubCmd); |
| 1223 | find_option("tcl",0,0); |
| 1224 | find_option("html",0,0); |
| 1225 | find_option("side-by-side","y",0); |
| @@ -1233,10 +1242,11 @@ | |
| 1242 | blob_append(&script, " ", 1); |
| 1243 | for(j=0; z[j]; j++) blob_appendf(&script, "\\%03o", (unsigned char)z[j]); |
| 1244 | } |
| 1245 | } |
| 1246 | blob_appendf(&script, "}\nset darkmode %d\n", bDarkMode); |
| 1247 | blob_appendf(&script, "set debug %d\n", bDebug); |
| 1248 | blob_appendf(&script, "%s", builtin_file("diff.tcl", 0)); |
| 1249 | if( zTempFile ){ |
| 1250 | blob_write_to_file(&script, zTempFile); |
| 1251 | fossil_print("To see diff, run: %s \"%s\"\n", zTclsh, zTempFile); |
| 1252 | }else{ |
| 1253 |
+156
-79
| --- src/merge.c | ||
| +++ src/merge.c | ||
| @@ -39,15 +39,16 @@ | ||
| 39 | 39 | } |
| 40 | 40 | /* The undocumented --script FILENAME option causes the Tk script to |
| 41 | 41 | ** be written into the FILENAME instead of being run. This is used |
| 42 | 42 | ** for testing and debugging. */ |
| 43 | 43 | zTempFile = find_option("script",0,1); |
| 44 | - bDebug = find_option("debug",0,0)!=0; | |
| 44 | + bDebug = find_option("tkdebug",0,0)!=0; | |
| 45 | 45 | verify_all_options(); |
| 46 | 46 | |
| 47 | 47 | blob_zero(&script); |
| 48 | 48 | blob_appendf(&script, "set ncontext %d\n", nContext); |
| 49 | + blob_appendf(&script, "set fossilexe {\"%/\"}\n", g.nameOfExe); | |
| 49 | 50 | blob_appendf(&script, "set fossilcmd {| \"%/\" merge-info}\n", |
| 50 | 51 | g.nameOfExe); |
| 51 | 52 | blob_appendf(&script, "set filelist [list"); |
| 52 | 53 | if( g.argc==2 ){ |
| 53 | 54 | /* No files named on the command-line. Use every file mentioned |
| @@ -136,12 +137,19 @@ | ||
| 136 | 137 | ** command associated with file "zFName". zFName must be the filename |
| 137 | 138 | ** relative to the root of the check-in - in other words a "tree name". |
| 138 | 139 | ** |
| 139 | 140 | ** When this routine is called, we know that the mergestat table |
| 140 | 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 | |
| 141 | 149 | */ |
| 142 | -static void merge_info_tcl(const char *zFName, int nContext){ | |
| 150 | +static void merge_info_tcl(const char *zFName, int nContext, int diffMode){ | |
| 143 | 151 | const char *zTreename;/* Name of the file in the tree */ |
| 144 | 152 | Stmt q; /* To query the MERGESTAT table */ |
| 145 | 153 | MergeBuilder mb; /* The merge builder object */ |
| 146 | 154 | Blob pivot,v1,v2,out; /* Blobs for holding content */ |
| 147 | 155 | const char *zFN; /* A filename */ |
| @@ -162,80 +170,116 @@ | ||
| 162 | 170 | return; |
| 163 | 171 | } |
| 164 | 172 | mergebuilder_init_tcl(&mb); |
| 165 | 173 | mb.nContext = nContext; |
| 166 | 174 | |
| 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); | |
| 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 | + | |
| 237 | 281 | blob_write_to_file(&out, "-"); |
| 238 | 282 | |
| 239 | 283 | mb.xDestroy(&mb); |
| 240 | 284 | blob_reset(&pivot); |
| 241 | 285 | blob_reset(&v1); |
| @@ -487,22 +531,27 @@ | ||
| 487 | 531 | ** lines are shown. |
| 488 | 532 | ** -c|--context N Show N lines of context around each change, |
| 489 | 533 | ** with negative N meaning show all content. Only |
| 490 | 534 | ** meaningful in combination with --tcl or --tk. |
| 491 | 535 | ** --dark Use dark mode for the Tcl/Tk/HTML output modes. |
| 536 | +** --tk Bring up a Tcl/Tk GUI that shows the changes | |
| 537 | +** associated with the most recent merge. | |
| 538 | +** | |
| 539 | +** Options used internally by --tk: | |
| 540 | +** --diff12 FILE Bring up a separate --tk diff for just the baseline | |
| 541 | +** and local variants of FILE. | |
| 542 | +** --diff13 FILE Like --diff12 but for baseline versus merge-in | |
| 543 | +** --diff23 FILE Like --diff12 but for local versus merge-in | |
| 492 | 544 | ** --tcl FILE Generate (to stdout) a TCL list containing |
| 493 | 545 | ** information needed to display the changes to |
| 494 | 546 | ** FILE caused by the most recent merge. FILE must |
| 495 | 547 | ** be a pathname relative to the root of the check-out. |
| 496 | -** --tk Bring up a Tcl/Tk GUI that shows the changes | |
| 497 | -** associated with the most recent merge. | |
| 498 | 548 | ** --html Like --tk but emits HTML to stdout. |
| 499 | 549 | ** -b|--browser Like --html but show the result in a web browser. |
| 500 | -** Additional debugging options available only when --tk is used: | |
| 501 | -** --debug Show sub-commands run to implement --tk | |
| 550 | +** Debugging options available only when --tk is used: | |
| 551 | +** --tkdebug Show sub-commands run to implement --tk | |
| 502 | 552 | ** --script FILE Write script used to implement --tk into FILE |
| 503 | - | |
| 504 | 553 | */ |
| 505 | 554 | void merge_info_cmd(void){ |
| 506 | 555 | const char *zCnt; |
| 507 | 556 | const char *zTcl; |
| 508 | 557 | int bTk; |
| @@ -512,25 +561,38 @@ | ||
| 512 | 561 | int bAll; |
| 513 | 562 | int nContext; |
| 514 | 563 | Stmt q; |
| 515 | 564 | const char *zWhere; |
| 516 | 565 | int cnt = 0; |
| 566 | + const char *zDiff2 = 0; | |
| 567 | + int diffMode = 0; | |
| 517 | 568 | |
| 518 | 569 | db_must_be_within_tree(); |
| 519 | - zTcl = find_option("tcl", 0, 1); | |
| 520 | 570 | bTk = find_option("tk", 0, 0)!=0; |
| 521 | 571 | bBrowser = find_option("browser", "b", 0)!=0; |
| 522 | 572 | bHtml = find_option("html", 0, 0)!=0 || bBrowser; |
| 573 | + zTcl = find_option("tcl", 0, 1); | |
| 523 | 574 | zCnt = find_option("context", "c", 1); |
| 524 | 575 | bDark = find_option("dark", 0, 0)!=0; |
| 525 | 576 | bAll = find_option("all", "a", 0)!=0; |
| 577 | + | |
| 578 | + if( (zDiff2 = find_option("diff12", 0, 1))!=0 ){ | |
| 579 | + diffMode = 12; | |
| 580 | + }else | |
| 581 | + if( (zDiff2 = find_option("diff13", 0, 1))!=0 ){ | |
| 582 | + diffMode = 13; | |
| 583 | + }else | |
| 584 | + if( (zDiff2 = find_option("diff23", 0, 1))!=0 ){ | |
| 585 | + diffMode = 23; | |
| 586 | + } | |
| 526 | 587 | if( (bTk + bHtml)==0 ){ |
| 527 | 588 | verify_all_options(); |
| 528 | 589 | if( g.argc>2 ){ |
| 529 | 590 | usage("[OPTIONS]"); |
| 530 | 591 | } |
| 531 | 592 | } |
| 593 | + | |
| 532 | 594 | if( zCnt ){ |
| 533 | 595 | nContext = atoi(zCnt); |
| 534 | 596 | if( nContext<0 ) nContext = 0xfffffff; |
| 535 | 597 | }else{ |
| 536 | 598 | nContext = 6; |
| @@ -548,13 +610,28 @@ | ||
| 548 | 610 | return; |
| 549 | 611 | }else if( bHtml ){ |
| 550 | 612 | merge_info_html(bBrowser, bDark, bAll, nContext); |
| 551 | 613 | return; |
| 552 | 614 | }else if( zTcl ){ |
| 553 | - merge_info_tcl(zTcl, nContext); | |
| 615 | + if( diffMode ) zTcl = zDiff2; | |
| 616 | + merge_info_tcl(zTcl, nContext, diffMode); | |
| 617 | + return; | |
| 618 | + } | |
| 619 | + if( diffMode ){ | |
| 620 | + char *zCmd; | |
| 621 | + zCmd = mprintf("merge-info --diff%d %!$ -c %d%s", | |
| 622 | + diffMode, zDiff2, nContext, bDark ? " --dark" : ""); | |
| 623 | + diff_tk(zCmd, g.argc); | |
| 624 | + fossil_free(zCmd); | |
| 554 | 625 | return; |
| 555 | 626 | } |
| 627 | + | |
| 628 | + verify_all_options(); | |
| 629 | + if( g.argc>2 ){ | |
| 630 | + usage("[OPTIONS]"); | |
| 631 | + } | |
| 632 | + | |
| 556 | 633 | if( bAll ){ |
| 557 | 634 | zWhere = ""; |
| 558 | 635 | }else{ |
| 559 | 636 | zWhere = "WHERE op IN ('MERGE','CONFLICT','ERROR')"; |
| 560 | 637 | } |
| 561 | 638 |
| --- 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,80 +170,116 @@ | |
| 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); |
| @@ -487,22 +531,27 @@ | |
| 487 | ** lines are shown. |
| 488 | ** -c|--context N Show N lines of context around each change, |
| 489 | ** with negative N meaning show all content. Only |
| 490 | ** meaningful in combination with --tcl or --tk. |
| 491 | ** --dark Use dark mode for the Tcl/Tk/HTML output modes. |
| 492 | ** --tcl FILE Generate (to stdout) a TCL list containing |
| 493 | ** information needed to display the changes to |
| 494 | ** FILE caused by the most recent merge. FILE must |
| 495 | ** be a pathname relative to the root of the check-out. |
| 496 | ** --tk Bring up a Tcl/Tk GUI that shows the changes |
| 497 | ** associated with the most recent merge. |
| 498 | ** --html Like --tk but emits HTML to stdout. |
| 499 | ** -b|--browser Like --html but show the result in a web browser. |
| 500 | ** Additional debugging options available only when --tk is used: |
| 501 | ** --debug Show sub-commands run to implement --tk |
| 502 | ** --script FILE Write script used to implement --tk into FILE |
| 503 | |
| 504 | */ |
| 505 | void merge_info_cmd(void){ |
| 506 | const char *zCnt; |
| 507 | const char *zTcl; |
| 508 | int bTk; |
| @@ -512,25 +561,38 @@ | |
| 512 | int bAll; |
| 513 | int nContext; |
| 514 | Stmt q; |
| 515 | const char *zWhere; |
| 516 | int cnt = 0; |
| 517 | |
| 518 | db_must_be_within_tree(); |
| 519 | zTcl = find_option("tcl", 0, 1); |
| 520 | bTk = find_option("tk", 0, 0)!=0; |
| 521 | bBrowser = find_option("browser", "b", 0)!=0; |
| 522 | bHtml = find_option("html", 0, 0)!=0 || bBrowser; |
| 523 | zCnt = find_option("context", "c", 1); |
| 524 | bDark = find_option("dark", 0, 0)!=0; |
| 525 | bAll = find_option("all", "a", 0)!=0; |
| 526 | if( (bTk + bHtml)==0 ){ |
| 527 | verify_all_options(); |
| 528 | if( g.argc>2 ){ |
| 529 | usage("[OPTIONS]"); |
| 530 | } |
| 531 | } |
| 532 | if( zCnt ){ |
| 533 | nContext = atoi(zCnt); |
| 534 | if( nContext<0 ) nContext = 0xfffffff; |
| 535 | }else{ |
| 536 | nContext = 6; |
| @@ -548,13 +610,28 @@ | |
| 548 | return; |
| 549 | }else if( bHtml ){ |
| 550 | merge_info_html(bBrowser, bDark, bAll, nContext); |
| 551 | return; |
| 552 | }else if( zTcl ){ |
| 553 | merge_info_tcl(zTcl, nContext); |
| 554 | return; |
| 555 | } |
| 556 | if( bAll ){ |
| 557 | zWhere = ""; |
| 558 | }else{ |
| 559 | zWhere = "WHERE op IN ('MERGE','CONFLICT','ERROR')"; |
| 560 | } |
| 561 |
| --- 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,80 +170,116 @@ | |
| 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 | |
| 283 | mb.xDestroy(&mb); |
| 284 | blob_reset(&pivot); |
| 285 | blob_reset(&v1); |
| @@ -487,22 +531,27 @@ | |
| 531 | ** lines are shown. |
| 532 | ** -c|--context N Show N lines of context around each change, |
| 533 | ** with negative N meaning show all content. Only |
| 534 | ** meaningful in combination with --tcl or --tk. |
| 535 | ** --dark Use dark mode for the Tcl/Tk/HTML output modes. |
| 536 | ** --tk Bring up a Tcl/Tk GUI that shows the changes |
| 537 | ** associated with the most recent merge. |
| 538 | ** |
| 539 | ** Options used internally by --tk: |
| 540 | ** --diff12 FILE Bring up a separate --tk diff for just the baseline |
| 541 | ** and local variants of FILE. |
| 542 | ** --diff13 FILE Like --diff12 but for baseline versus merge-in |
| 543 | ** --diff23 FILE Like --diff12 but for local versus merge-in |
| 544 | ** --tcl FILE Generate (to stdout) a TCL list containing |
| 545 | ** information needed to display the changes to |
| 546 | ** FILE caused by the most recent merge. FILE must |
| 547 | ** be a pathname relative to the root of the check-out. |
| 548 | ** --html Like --tk but emits HTML to stdout. |
| 549 | ** -b|--browser Like --html but show the result in a web browser. |
| 550 | ** Debugging options available only when --tk is used: |
| 551 | ** --tkdebug Show sub-commands run to implement --tk |
| 552 | ** --script FILE Write script used to implement --tk into FILE |
| 553 | */ |
| 554 | void merge_info_cmd(void){ |
| 555 | const char *zCnt; |
| 556 | const char *zTcl; |
| 557 | int bTk; |
| @@ -512,25 +561,38 @@ | |
| 561 | int bAll; |
| 562 | int nContext; |
| 563 | Stmt q; |
| 564 | const char *zWhere; |
| 565 | int cnt = 0; |
| 566 | const char *zDiff2 = 0; |
| 567 | int diffMode = 0; |
| 568 | |
| 569 | db_must_be_within_tree(); |
| 570 | bTk = find_option("tk", 0, 0)!=0; |
| 571 | bBrowser = find_option("browser", "b", 0)!=0; |
| 572 | bHtml = find_option("html", 0, 0)!=0 || bBrowser; |
| 573 | zTcl = find_option("tcl", 0, 1); |
| 574 | zCnt = find_option("context", "c", 1); |
| 575 | bDark = find_option("dark", 0, 0)!=0; |
| 576 | bAll = find_option("all", "a", 0)!=0; |
| 577 | |
| 578 | if( (zDiff2 = find_option("diff12", 0, 1))!=0 ){ |
| 579 | diffMode = 12; |
| 580 | }else |
| 581 | if( (zDiff2 = find_option("diff13", 0, 1))!=0 ){ |
| 582 | diffMode = 13; |
| 583 | }else |
| 584 | if( (zDiff2 = find_option("diff23", 0, 1))!=0 ){ |
| 585 | diffMode = 23; |
| 586 | } |
| 587 | if( (bTk + bHtml)==0 ){ |
| 588 | verify_all_options(); |
| 589 | if( g.argc>2 ){ |
| 590 | usage("[OPTIONS]"); |
| 591 | } |
| 592 | } |
| 593 | |
| 594 | if( zCnt ){ |
| 595 | nContext = atoi(zCnt); |
| 596 | if( nContext<0 ) nContext = 0xfffffff; |
| 597 | }else{ |
| 598 | nContext = 6; |
| @@ -548,13 +610,28 @@ | |
| 610 | return; |
| 611 | }else if( bHtml ){ |
| 612 | merge_info_html(bBrowser, bDark, bAll, nContext); |
| 613 | return; |
| 614 | }else if( zTcl ){ |
| 615 | if( diffMode ) zTcl = zDiff2; |
| 616 | merge_info_tcl(zTcl, nContext, diffMode); |
| 617 | return; |
| 618 | } |
| 619 | if( diffMode ){ |
| 620 | char *zCmd; |
| 621 | zCmd = mprintf("merge-info --diff%d %!$ -c %d%s", |
| 622 | diffMode, zDiff2, nContext, bDark ? " --dark" : ""); |
| 623 | diff_tk(zCmd, g.argc); |
| 624 | fossil_free(zCmd); |
| 625 | return; |
| 626 | } |
| 627 | |
| 628 | verify_all_options(); |
| 629 | if( g.argc>2 ){ |
| 630 | usage("[OPTIONS]"); |
| 631 | } |
| 632 | |
| 633 | if( bAll ){ |
| 634 | zWhere = ""; |
| 635 | }else{ |
| 636 | zWhere = "WHERE op IN ('MERGE','CONFLICT','ERROR')"; |
| 637 | } |
| 638 |
+156
-79
| --- src/merge.c | ||
| +++ src/merge.c | ||
| @@ -39,15 +39,16 @@ | ||
| 39 | 39 | } |
| 40 | 40 | /* The undocumented --script FILENAME option causes the Tk script to |
| 41 | 41 | ** be written into the FILENAME instead of being run. This is used |
| 42 | 42 | ** for testing and debugging. */ |
| 43 | 43 | zTempFile = find_option("script",0,1); |
| 44 | - bDebug = find_option("debug",0,0)!=0; | |
| 44 | + bDebug = find_option("tkdebug",0,0)!=0; | |
| 45 | 45 | verify_all_options(); |
| 46 | 46 | |
| 47 | 47 | blob_zero(&script); |
| 48 | 48 | blob_appendf(&script, "set ncontext %d\n", nContext); |
| 49 | + blob_appendf(&script, "set fossilexe {\"%/\"}\n", g.nameOfExe); | |
| 49 | 50 | blob_appendf(&script, "set fossilcmd {| \"%/\" merge-info}\n", |
| 50 | 51 | g.nameOfExe); |
| 51 | 52 | blob_appendf(&script, "set filelist [list"); |
| 52 | 53 | if( g.argc==2 ){ |
| 53 | 54 | /* No files named on the command-line. Use every file mentioned |
| @@ -136,12 +137,19 @@ | ||
| 136 | 137 | ** command associated with file "zFName". zFName must be the filename |
| 137 | 138 | ** relative to the root of the check-in - in other words a "tree name". |
| 138 | 139 | ** |
| 139 | 140 | ** When this routine is called, we know that the mergestat table |
| 140 | 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 | |
| 141 | 149 | */ |
| 142 | -static void merge_info_tcl(const char *zFName, int nContext){ | |
| 150 | +static void merge_info_tcl(const char *zFName, int nContext, int diffMode){ | |
| 143 | 151 | const char *zTreename;/* Name of the file in the tree */ |
| 144 | 152 | Stmt q; /* To query the MERGESTAT table */ |
| 145 | 153 | MergeBuilder mb; /* The merge builder object */ |
| 146 | 154 | Blob pivot,v1,v2,out; /* Blobs for holding content */ |
| 147 | 155 | const char *zFN; /* A filename */ |
| @@ -162,80 +170,116 @@ | ||
| 162 | 170 | return; |
| 163 | 171 | } |
| 164 | 172 | mergebuilder_init_tcl(&mb); |
| 165 | 173 | mb.nContext = nContext; |
| 166 | 174 | |
| 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); | |
| 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 | + | |
| 237 | 281 | blob_write_to_file(&out, "-"); |
| 238 | 282 | |
| 239 | 283 | mb.xDestroy(&mb); |
| 240 | 284 | blob_reset(&pivot); |
| 241 | 285 | blob_reset(&v1); |
| @@ -487,22 +531,27 @@ | ||
| 487 | 531 | ** lines are shown. |
| 488 | 532 | ** -c|--context N Show N lines of context around each change, |
| 489 | 533 | ** with negative N meaning show all content. Only |
| 490 | 534 | ** meaningful in combination with --tcl or --tk. |
| 491 | 535 | ** --dark Use dark mode for the Tcl/Tk/HTML output modes. |
| 536 | +** --tk Bring up a Tcl/Tk GUI that shows the changes | |
| 537 | +** associated with the most recent merge. | |
| 538 | +** | |
| 539 | +** Options used internally by --tk: | |
| 540 | +** --diff12 FILE Bring up a separate --tk diff for just the baseline | |
| 541 | +** and local variants of FILE. | |
| 542 | +** --diff13 FILE Like --diff12 but for baseline versus merge-in | |
| 543 | +** --diff23 FILE Like --diff12 but for local versus merge-in | |
| 492 | 544 | ** --tcl FILE Generate (to stdout) a TCL list containing |
| 493 | 545 | ** information needed to display the changes to |
| 494 | 546 | ** FILE caused by the most recent merge. FILE must |
| 495 | 547 | ** be a pathname relative to the root of the check-out. |
| 496 | -** --tk Bring up a Tcl/Tk GUI that shows the changes | |
| 497 | -** associated with the most recent merge. | |
| 498 | 548 | ** --html Like --tk but emits HTML to stdout. |
| 499 | 549 | ** -b|--browser Like --html but show the result in a web browser. |
| 500 | -** Additional debugging options available only when --tk is used: | |
| 501 | -** --debug Show sub-commands run to implement --tk | |
| 550 | +** Debugging options available only when --tk is used: | |
| 551 | +** --tkdebug Show sub-commands run to implement --tk | |
| 502 | 552 | ** --script FILE Write script used to implement --tk into FILE |
| 503 | - | |
| 504 | 553 | */ |
| 505 | 554 | void merge_info_cmd(void){ |
| 506 | 555 | const char *zCnt; |
| 507 | 556 | const char *zTcl; |
| 508 | 557 | int bTk; |
| @@ -512,25 +561,38 @@ | ||
| 512 | 561 | int bAll; |
| 513 | 562 | int nContext; |
| 514 | 563 | Stmt q; |
| 515 | 564 | const char *zWhere; |
| 516 | 565 | int cnt = 0; |
| 566 | + const char *zDiff2 = 0; | |
| 567 | + int diffMode = 0; | |
| 517 | 568 | |
| 518 | 569 | db_must_be_within_tree(); |
| 519 | - zTcl = find_option("tcl", 0, 1); | |
| 520 | 570 | bTk = find_option("tk", 0, 0)!=0; |
| 521 | 571 | bBrowser = find_option("browser", "b", 0)!=0; |
| 522 | 572 | bHtml = find_option("html", 0, 0)!=0 || bBrowser; |
| 573 | + zTcl = find_option("tcl", 0, 1); | |
| 523 | 574 | zCnt = find_option("context", "c", 1); |
| 524 | 575 | bDark = find_option("dark", 0, 0)!=0; |
| 525 | 576 | bAll = find_option("all", "a", 0)!=0; |
| 577 | + | |
| 578 | + if( (zDiff2 = find_option("diff12", 0, 1))!=0 ){ | |
| 579 | + diffMode = 12; | |
| 580 | + }else | |
| 581 | + if( (zDiff2 = find_option("diff13", 0, 1))!=0 ){ | |
| 582 | + diffMode = 13; | |
| 583 | + }else | |
| 584 | + if( (zDiff2 = find_option("diff23", 0, 1))!=0 ){ | |
| 585 | + diffMode = 23; | |
| 586 | + } | |
| 526 | 587 | if( (bTk + bHtml)==0 ){ |
| 527 | 588 | verify_all_options(); |
| 528 | 589 | if( g.argc>2 ){ |
| 529 | 590 | usage("[OPTIONS]"); |
| 530 | 591 | } |
| 531 | 592 | } |
| 593 | + | |
| 532 | 594 | if( zCnt ){ |
| 533 | 595 | nContext = atoi(zCnt); |
| 534 | 596 | if( nContext<0 ) nContext = 0xfffffff; |
| 535 | 597 | }else{ |
| 536 | 598 | nContext = 6; |
| @@ -548,13 +610,28 @@ | ||
| 548 | 610 | return; |
| 549 | 611 | }else if( bHtml ){ |
| 550 | 612 | merge_info_html(bBrowser, bDark, bAll, nContext); |
| 551 | 613 | return; |
| 552 | 614 | }else if( zTcl ){ |
| 553 | - merge_info_tcl(zTcl, nContext); | |
| 615 | + if( diffMode ) zTcl = zDiff2; | |
| 616 | + merge_info_tcl(zTcl, nContext, diffMode); | |
| 617 | + return; | |
| 618 | + } | |
| 619 | + if( diffMode ){ | |
| 620 | + char *zCmd; | |
| 621 | + zCmd = mprintf("merge-info --diff%d %!$ -c %d%s", | |
| 622 | + diffMode, zDiff2, nContext, bDark ? " --dark" : ""); | |
| 623 | + diff_tk(zCmd, g.argc); | |
| 624 | + fossil_free(zCmd); | |
| 554 | 625 | return; |
| 555 | 626 | } |
| 627 | + | |
| 628 | + verify_all_options(); | |
| 629 | + if( g.argc>2 ){ | |
| 630 | + usage("[OPTIONS]"); | |
| 631 | + } | |
| 632 | + | |
| 556 | 633 | if( bAll ){ |
| 557 | 634 | zWhere = ""; |
| 558 | 635 | }else{ |
| 559 | 636 | zWhere = "WHERE op IN ('MERGE','CONFLICT','ERROR')"; |
| 560 | 637 | } |
| 561 | 638 |
| --- 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,80 +170,116 @@ | |
| 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); |
| @@ -487,22 +531,27 @@ | |
| 487 | ** lines are shown. |
| 488 | ** -c|--context N Show N lines of context around each change, |
| 489 | ** with negative N meaning show all content. Only |
| 490 | ** meaningful in combination with --tcl or --tk. |
| 491 | ** --dark Use dark mode for the Tcl/Tk/HTML output modes. |
| 492 | ** --tcl FILE Generate (to stdout) a TCL list containing |
| 493 | ** information needed to display the changes to |
| 494 | ** FILE caused by the most recent merge. FILE must |
| 495 | ** be a pathname relative to the root of the check-out. |
| 496 | ** --tk Bring up a Tcl/Tk GUI that shows the changes |
| 497 | ** associated with the most recent merge. |
| 498 | ** --html Like --tk but emits HTML to stdout. |
| 499 | ** -b|--browser Like --html but show the result in a web browser. |
| 500 | ** Additional debugging options available only when --tk is used: |
| 501 | ** --debug Show sub-commands run to implement --tk |
| 502 | ** --script FILE Write script used to implement --tk into FILE |
| 503 | |
| 504 | */ |
| 505 | void merge_info_cmd(void){ |
| 506 | const char *zCnt; |
| 507 | const char *zTcl; |
| 508 | int bTk; |
| @@ -512,25 +561,38 @@ | |
| 512 | int bAll; |
| 513 | int nContext; |
| 514 | Stmt q; |
| 515 | const char *zWhere; |
| 516 | int cnt = 0; |
| 517 | |
| 518 | db_must_be_within_tree(); |
| 519 | zTcl = find_option("tcl", 0, 1); |
| 520 | bTk = find_option("tk", 0, 0)!=0; |
| 521 | bBrowser = find_option("browser", "b", 0)!=0; |
| 522 | bHtml = find_option("html", 0, 0)!=0 || bBrowser; |
| 523 | zCnt = find_option("context", "c", 1); |
| 524 | bDark = find_option("dark", 0, 0)!=0; |
| 525 | bAll = find_option("all", "a", 0)!=0; |
| 526 | if( (bTk + bHtml)==0 ){ |
| 527 | verify_all_options(); |
| 528 | if( g.argc>2 ){ |
| 529 | usage("[OPTIONS]"); |
| 530 | } |
| 531 | } |
| 532 | if( zCnt ){ |
| 533 | nContext = atoi(zCnt); |
| 534 | if( nContext<0 ) nContext = 0xfffffff; |
| 535 | }else{ |
| 536 | nContext = 6; |
| @@ -548,13 +610,28 @@ | |
| 548 | return; |
| 549 | }else if( bHtml ){ |
| 550 | merge_info_html(bBrowser, bDark, bAll, nContext); |
| 551 | return; |
| 552 | }else if( zTcl ){ |
| 553 | merge_info_tcl(zTcl, nContext); |
| 554 | return; |
| 555 | } |
| 556 | if( bAll ){ |
| 557 | zWhere = ""; |
| 558 | }else{ |
| 559 | zWhere = "WHERE op IN ('MERGE','CONFLICT','ERROR')"; |
| 560 | } |
| 561 |
| --- 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,80 +170,116 @@ | |
| 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 | |
| 283 | mb.xDestroy(&mb); |
| 284 | blob_reset(&pivot); |
| 285 | blob_reset(&v1); |
| @@ -487,22 +531,27 @@ | |
| 531 | ** lines are shown. |
| 532 | ** -c|--context N Show N lines of context around each change, |
| 533 | ** with negative N meaning show all content. Only |
| 534 | ** meaningful in combination with --tcl or --tk. |
| 535 | ** --dark Use dark mode for the Tcl/Tk/HTML output modes. |
| 536 | ** --tk Bring up a Tcl/Tk GUI that shows the changes |
| 537 | ** associated with the most recent merge. |
| 538 | ** |
| 539 | ** Options used internally by --tk: |
| 540 | ** --diff12 FILE Bring up a separate --tk diff for just the baseline |
| 541 | ** and local variants of FILE. |
| 542 | ** --diff13 FILE Like --diff12 but for baseline versus merge-in |
| 543 | ** --diff23 FILE Like --diff12 but for local versus merge-in |
| 544 | ** --tcl FILE Generate (to stdout) a TCL list containing |
| 545 | ** information needed to display the changes to |
| 546 | ** FILE caused by the most recent merge. FILE must |
| 547 | ** be a pathname relative to the root of the check-out. |
| 548 | ** --html Like --tk but emits HTML to stdout. |
| 549 | ** -b|--browser Like --html but show the result in a web browser. |
| 550 | ** Debugging options available only when --tk is used: |
| 551 | ** --tkdebug Show sub-commands run to implement --tk |
| 552 | ** --script FILE Write script used to implement --tk into FILE |
| 553 | */ |
| 554 | void merge_info_cmd(void){ |
| 555 | const char *zCnt; |
| 556 | const char *zTcl; |
| 557 | int bTk; |
| @@ -512,25 +561,38 @@ | |
| 561 | int bAll; |
| 562 | int nContext; |
| 563 | Stmt q; |
| 564 | const char *zWhere; |
| 565 | int cnt = 0; |
| 566 | const char *zDiff2 = 0; |
| 567 | int diffMode = 0; |
| 568 | |
| 569 | db_must_be_within_tree(); |
| 570 | bTk = find_option("tk", 0, 0)!=0; |
| 571 | bBrowser = find_option("browser", "b", 0)!=0; |
| 572 | bHtml = find_option("html", 0, 0)!=0 || bBrowser; |
| 573 | zTcl = find_option("tcl", 0, 1); |
| 574 | zCnt = find_option("context", "c", 1); |
| 575 | bDark = find_option("dark", 0, 0)!=0; |
| 576 | bAll = find_option("all", "a", 0)!=0; |
| 577 | |
| 578 | if( (zDiff2 = find_option("diff12", 0, 1))!=0 ){ |
| 579 | diffMode = 12; |
| 580 | }else |
| 581 | if( (zDiff2 = find_option("diff13", 0, 1))!=0 ){ |
| 582 | diffMode = 13; |
| 583 | }else |
| 584 | if( (zDiff2 = find_option("diff23", 0, 1))!=0 ){ |
| 585 | diffMode = 23; |
| 586 | } |
| 587 | if( (bTk + bHtml)==0 ){ |
| 588 | verify_all_options(); |
| 589 | if( g.argc>2 ){ |
| 590 | usage("[OPTIONS]"); |
| 591 | } |
| 592 | } |
| 593 | |
| 594 | if( zCnt ){ |
| 595 | nContext = atoi(zCnt); |
| 596 | if( nContext<0 ) nContext = 0xfffffff; |
| 597 | }else{ |
| 598 | nContext = 6; |
| @@ -548,13 +610,28 @@ | |
| 610 | return; |
| 611 | }else if( bHtml ){ |
| 612 | merge_info_html(bBrowser, bDark, bAll, nContext); |
| 613 | return; |
| 614 | }else if( zTcl ){ |
| 615 | if( diffMode ) zTcl = zDiff2; |
| 616 | merge_info_tcl(zTcl, nContext, diffMode); |
| 617 | return; |
| 618 | } |
| 619 | if( diffMode ){ |
| 620 | char *zCmd; |
| 621 | zCmd = mprintf("merge-info --diff%d %!$ -c %d%s", |
| 622 | diffMode, zDiff2, nContext, bDark ? " --dark" : ""); |
| 623 | diff_tk(zCmd, g.argc); |
| 624 | fossil_free(zCmd); |
| 625 | return; |
| 626 | } |
| 627 | |
| 628 | verify_all_options(); |
| 629 | if( g.argc>2 ){ |
| 630 | usage("[OPTIONS]"); |
| 631 | } |
| 632 | |
| 633 | if( bAll ){ |
| 634 | zWhere = ""; |
| 635 | }else{ |
| 636 | zWhere = "WHERE op IN ('MERGE','CONFLICT','ERROR')"; |
| 637 | } |
| 638 |
+48
-15
| --- src/merge.tcl | ||
| +++ src/merge.tcl | ||
| @@ -1,23 +1,27 @@ | ||
| 1 | 1 | # Show details of a 3-way merge operation. The left-most column is the |
| 2 | 2 | # common ancestor. The next two columns are edits of that common ancestor. |
| 3 | 3 | # The right-most column is the result of the merge. |
| 4 | 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. | |
| 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 | |
| 8 | 16 | # |
| 9 | 17 | # If the "filelist" global variable is defined, then it is a list of |
| 10 | 18 | # alternating "merge-type names" (ex: UPDATE, MERGE, CONFLICT, ERROR) and |
| 11 | 19 | # filenames. In that case, the initial display shows the changes for |
| 12 | 20 | # the first pair on the list and there is a optionmenu that allows the |
| 13 | 21 | # user to select other fiels on the list. |
| 14 | 22 | # |
| 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 | 23 | # This header comment is stripped off by the "mkbuiltin.c" program. |
| 20 | 24 | # |
| 21 | 25 | package require Tk |
| 22 | 26 | |
| 23 | 27 | array set CFG_light { |
| @@ -93,19 +97,19 @@ | ||
| 93 | 97 | regexp {[a-z]+} $c type |
| 94 | 98 | return $type |
| 95 | 99 | } |
| 96 | 100 | |
| 97 | 101 | proc readMerge {args} { |
| 98 | - global fossilcmd ncontext current_file debug | |
| 102 | + global fossilexe ncontext current_file debug | |
| 99 | 103 | if {$ncontext=="All"} { |
| 100 | - set cmd "$fossilcmd -c -1" | |
| 104 | + set cmd "| $fossilexe merge-info -c -1" | |
| 101 | 105 | } else { |
| 102 | - set cmd "$fossilcmd -c $ncontext" | |
| 106 | + set cmd "| $fossilexe merge-info -c $ncontext" | |
| 103 | 107 | } |
| 104 | 108 | if {[info exists current_file]} { |
| 105 | 109 | regsub {^[A-Z]+ } $current_file {} fn |
| 106 | - append cmd " -tcl [list $fn]" | |
| 110 | + lappend cmd -tcl $fn | |
| 107 | 111 | } |
| 108 | 112 | if {$debug} { |
| 109 | 113 | regsub {^\| +} $cmd {} cmd2 |
| 110 | 114 | puts $cmd2 |
| 111 | 115 | flush stdout |
| @@ -342,10 +346,38 @@ | ||
| 342 | 346 | bind . <$key> "scroll-$axis $args; break" |
| 343 | 347 | bind . <Shift-$key> continue |
| 344 | 348 | } |
| 345 | 349 | |
| 346 | 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 | + | |
| 347 | 379 | set useOptionMenu 1 |
| 348 | 380 | if {[info exists filelist]} { |
| 349 | 381 | set current_file "[lindex $filelist 0] [lindex $filelist 1]" |
| 350 | 382 | if {[llength $filelist]>2} { |
| 351 | 383 | trace add variable current_file write readMerge |
| @@ -579,16 +611,17 @@ | ||
| 579 | 611 | } |
| 580 | 612 | set ::search $w |
| 581 | 613 | } |
| 582 | 614 | ::ttk::button .bb.quit -text {Quit} -command exit |
| 583 | 615 | ::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 | |
| 585 | 618 | if {[winfo exists .bb.files]} { |
| 586 | - pack .bb.files -side left | |
| 619 | + pack .bb.files -side left -fill y | |
| 587 | 620 | } |
| 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 | |
| 590 | 623 | grid rowconfigure . 1 -weight 1 -minsize [winfo reqheight .nameA] |
| 591 | 624 | grid rowconfigure . 2 -weight 100 |
| 592 | 625 | readMerge |
| 593 | 626 | grid .bb -row 0 -columnspan 8 |
| 594 | 627 | grid .nameA -row 1 -column 1 -sticky ew |
| 595 | 628 |
| --- 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 |