Fossil SCM
The "fossil merge-info --tcl FILE" command generates content for the merge.tcl GUI using MERGESTAT data. Incremental check-in.
Commit
cbd24a2594c590a005aaa3e3c363545a73c0ace8d980dad7b2ce53545dacbd15
Parent
625ff9d574307c3…
2 files changed
+172
-2
+4
-3
+172
-2
| --- src/merge.c | ||
| +++ src/merge.c | ||
| @@ -20,26 +20,196 @@ | ||
| 20 | 20 | */ |
| 21 | 21 | #include "config.h" |
| 22 | 22 | #include "merge.h" |
| 23 | 23 | #include <assert.h> |
| 24 | 24 | |
| 25 | + | |
| 26 | +/* | |
| 27 | +** Bring up a Tcl/Tk GUI to show details of the most recent merge. | |
| 28 | +*/ | |
| 29 | +static void merge_info_tk(int bDark, int nContext){ | |
| 30 | + fossil_fatal("Not yet implemented"); | |
| 31 | +} | |
| 32 | + | |
| 33 | +/* | |
| 34 | +** Generate a TCL list on standard output that can be fed into the | |
| 35 | +** merge.tcl script to show the details of the most recent merge | |
| 36 | +** command associated with file "zFName". | |
| 37 | +** | |
| 38 | +** When this routine is called, we know that the mergestat table | |
| 39 | +** exists, but we do not know if zFName is mentioned in that table. | |
| 40 | +*/ | |
| 41 | +static void merge_info_tcl(const char *zFName, int nContext){ | |
| 42 | + char *zFile; /* Normalized filename */ | |
| 43 | + char *zTreename; /* Name of the file in the tree */ | |
| 44 | + Blob fname; /* Filename relative to root */ | |
| 45 | + Stmt q; /* To query the MERGESTAT table */ | |
| 46 | + MergeBuilder mb; /* The merge builder object */ | |
| 47 | + Blob pivot,v1,v2,out; /* Blobs for holding content */ | |
| 48 | + const char *zFN; /* A filename */ | |
| 49 | + int rid; /* RID value */ | |
| 50 | + int sz; /* File size value */ | |
| 51 | + | |
| 52 | + zFile = mprintf("%/", zFName); | |
| 53 | + file_tree_name(zFile, &fname, 0, 1); | |
| 54 | + zTreename = blob_str(&fname); | |
| 55 | + db_prepare(&q, | |
| 56 | + /* 0 1 2 3 4 5 6 7 */ | |
| 57 | + "SELECT fnp, ridp, fn, ridv, sz, fnm, ridm, fnr" | |
| 58 | + " FROM mergestat" | |
| 59 | + " WHERE fnp=%Q OR fnr=%Q", | |
| 60 | + zTreename, zTreename | |
| 61 | + ); | |
| 62 | + if( db_step(&q)!=SQLITE_ROW ){ | |
| 63 | + db_finalize(&q); | |
| 64 | + fossil_print("ERROR {don't know anything about file: %s}\n", zTreename); | |
| 65 | + return; | |
| 66 | + } | |
| 67 | + mergebuilder_init_tcl(&mb); | |
| 68 | + mb.nContext = nContext; | |
| 69 | + | |
| 70 | + /* Set up the pivot */ | |
| 71 | + zFN = db_column_text(&q, 0); | |
| 72 | + if( zFN==0 ){ | |
| 73 | + /* No pivot because the file was added */ | |
| 74 | + mb.zPivot = "(no baseline)"; | |
| 75 | + blob_zero(&pivot); | |
| 76 | + }else{ | |
| 77 | + mb.zPivot = mprintf("%s (baseline)", file_tail(zFN)); | |
| 78 | + rid = db_column_int(&q, 1); | |
| 79 | + content_get(rid, &pivot); | |
| 80 | + } | |
| 81 | + mb.pPivot = &pivot; | |
| 82 | + | |
| 83 | + /* Set up the merge-in as V1 */ | |
| 84 | + zFN = db_column_text(&q, 5); | |
| 85 | + if( zFN==0 ){ | |
| 86 | + /* File deleted in the merged-in branch */ | |
| 87 | + mb.zV1 = "(deleted file)"; | |
| 88 | + blob_zero(&v1); | |
| 89 | + }else{ | |
| 90 | + mb.zV1 = mprintf("%s (merge-in)", file_tail(zFN)); | |
| 91 | + rid = db_column_int(&q, 6); | |
| 92 | + content_get(rid, &v1); | |
| 93 | + } | |
| 94 | + mb.pV1 = &v1; | |
| 95 | + | |
| 96 | + /* Set up the local content as V2 */ | |
| 97 | + zFN = db_column_text(&q, 2); | |
| 98 | + if( zFN==0 ){ | |
| 99 | + /* File added by merge */ | |
| 100 | + mb.zV2 = "(no original)"; | |
| 101 | + blob_zero(&v2); | |
| 102 | + }else{ | |
| 103 | + mb.zV2 = mprintf("%s (local)", file_tail(zFN)); | |
| 104 | + rid = db_column_int(&q, 3); | |
| 105 | + sz = db_column_int(&q, 4); | |
| 106 | + if( rid==0 && sz>0 ){ | |
| 107 | + /* The origin file had been edited so we'll have to pull its | |
| 108 | + ** original content out of the undo buffer */ | |
| 109 | + Stmt q2; | |
| 110 | + db_prepare(&q2, | |
| 111 | + "SELECT content FROM undo" | |
| 112 | + " WHERE pathname=%Q AND octet_length(content)=%d", | |
| 113 | + zFN, sz | |
| 114 | + ); | |
| 115 | + blob_zero(&v2); | |
| 116 | + if( db_step(&q2)==SQLITE_ROW ){ | |
| 117 | + db_column_blob(&q, 0, &v2); | |
| 118 | + }else{ | |
| 119 | + mb.zV2 = "(local content missing)"; | |
| 120 | + } | |
| 121 | + db_finalize(&q2); | |
| 122 | + }else{ | |
| 123 | + /* The origin file was unchanged when the merge first occurred */ | |
| 124 | + content_get(rid, &v2); | |
| 125 | + } | |
| 126 | + } | |
| 127 | + mb.pV2 = &v2; | |
| 128 | + | |
| 129 | + /* Set up the output */ | |
| 130 | + zFN = db_column_text(&q, 7); | |
| 131 | + if( zFN==0 ){ | |
| 132 | + mb.zOut = "(Merge Result)"; | |
| 133 | + }else{ | |
| 134 | + mb.zOut = mprintf("%s (after merge)", file_tail(zFN)); | |
| 135 | + } | |
| 136 | + blob_zero(&out); | |
| 137 | + mb.pOut = &out; | |
| 138 | + | |
| 139 | + merge_three_blobs(&mb); | |
| 140 | + blob_write_to_file(&out, "-"); | |
| 141 | + | |
| 142 | + mb.xDestroy(&mb); | |
| 143 | + blob_reset(&pivot); | |
| 144 | + blob_reset(&v1); | |
| 145 | + blob_reset(&v2); | |
| 146 | + blob_reset(&out); | |
| 147 | + db_finalize(&q); | |
| 148 | +} | |
| 149 | + | |
| 25 | 150 | /* |
| 26 | 151 | ** COMMAND: merge-info |
| 152 | +** | |
| 153 | +** Usage: %fossil merge-info [OPTIONS] | |
| 27 | 154 | ** |
| 28 | 155 | ** Display information about the most recent merge operation. |
| 29 | 156 | ** |
| 30 | 157 | ** Right now, this command basically just dumps the localdb.mergestat |
| 31 | 158 | ** table. The plan moving forward is that it can generate data for |
| 32 | 159 | ** a Tk-based GUI to show the details of the merge. This command is |
| 33 | 160 | ** a work-in-progress. |
| 161 | +** | |
| 162 | +** Options: | |
| 163 | +** -c|--context N Show N lines of context around each change, | |
| 164 | +** with negative N meaning show all content. Only | |
| 165 | +** meaningful in combination with --tcl or --tk. | |
| 166 | +** --dark Use dark mode for the Tcl/Tk-based GUI | |
| 167 | +** --tcl FILE Generate (to stdout) a TCL list containing | |
| 168 | +** information needed to display the changes to | |
| 169 | +** FILE caused by the most recent merge. | |
| 170 | +** --tk Bring up a Tcl/Tk GUI that shows the changes | |
| 171 | +** associated with the most recent merge. | |
| 172 | +** | |
| 34 | 173 | */ |
| 35 | 174 | void merge_info_cmd(void){ |
| 175 | + const char *zCnt; | |
| 176 | + const char *zTcl; | |
| 177 | + int bTk; | |
| 178 | + int bDark = 0; | |
| 179 | + int nContext; | |
| 36 | 180 | Stmt q; |
| 37 | - verify_all_options(); | |
| 181 | + | |
| 38 | 182 | db_must_be_within_tree(); |
| 39 | - | |
| 183 | + zTcl = find_option("tcl", 0, 1); | |
| 184 | + bTk = find_option("tk", 0, 0)!=0; | |
| 185 | + zCnt = find_option("context", "c", 1); | |
| 186 | + bDark = find_option("dark", 0, 0)!=0; | |
| 187 | + verify_all_options(); | |
| 188 | + if( g.argc>2 ){ | |
| 189 | + usage("[OPTIONS]"); | |
| 190 | + } | |
| 191 | + if( zCnt ){ | |
| 192 | + nContext = atoi(zCnt); | |
| 193 | + if( nContext<0 ) nContext = 0xfffffff; | |
| 194 | + }else{ | |
| 195 | + nContext = 6; | |
| 196 | + } | |
| 40 | 197 | if( !db_table_exists("localdb","mergestat") ){ |
| 198 | + if( zTcl ){ | |
| 199 | + fossil_print("ERROR {no merge data available}\n"); | |
| 200 | + }else{ | |
| 201 | + fossil_print("No merge data is available\n"); | |
| 202 | + } | |
| 203 | + return; | |
| 204 | + } | |
| 205 | + if( bTk ){ | |
| 206 | + merge_info_tk(bDark, nContext); | |
| 207 | + return; | |
| 208 | + } | |
| 209 | + if( zTcl ){ | |
| 210 | + merge_info_tcl(zTcl, nContext); | |
| 41 | 211 | return; |
| 42 | 212 | } |
| 43 | 213 | db_prepare(&q, |
| 44 | 214 | /* 0 1 2 3 4 */ |
| 45 | 215 | "SELECT op, fn, fnr, nc, msg FROM mergestat ORDER BY coalesce(fnr,fn)" |
| 46 | 216 |
| --- src/merge.c | |
| +++ src/merge.c | |
| @@ -20,26 +20,196 @@ | |
| 20 | */ |
| 21 | #include "config.h" |
| 22 | #include "merge.h" |
| 23 | #include <assert.h> |
| 24 | |
| 25 | /* |
| 26 | ** COMMAND: merge-info |
| 27 | ** |
| 28 | ** Display information about the most recent merge operation. |
| 29 | ** |
| 30 | ** Right now, this command basically just dumps the localdb.mergestat |
| 31 | ** table. The plan moving forward is that it can generate data for |
| 32 | ** a Tk-based GUI to show the details of the merge. This command is |
| 33 | ** a work-in-progress. |
| 34 | */ |
| 35 | void merge_info_cmd(void){ |
| 36 | Stmt q; |
| 37 | verify_all_options(); |
| 38 | db_must_be_within_tree(); |
| 39 | |
| 40 | if( !db_table_exists("localdb","mergestat") ){ |
| 41 | return; |
| 42 | } |
| 43 | db_prepare(&q, |
| 44 | /* 0 1 2 3 4 */ |
| 45 | "SELECT op, fn, fnr, nc, msg FROM mergestat ORDER BY coalesce(fnr,fn)" |
| 46 |
| --- src/merge.c | |
| +++ src/merge.c | |
| @@ -20,26 +20,196 @@ | |
| 20 | */ |
| 21 | #include "config.h" |
| 22 | #include "merge.h" |
| 23 | #include <assert.h> |
| 24 | |
| 25 | |
| 26 | /* |
| 27 | ** Bring up a Tcl/Tk GUI to show details of the most recent merge. |
| 28 | */ |
| 29 | static void merge_info_tk(int bDark, int nContext){ |
| 30 | fossil_fatal("Not yet implemented"); |
| 31 | } |
| 32 | |
| 33 | /* |
| 34 | ** Generate a TCL list on standard output that can be fed into the |
| 35 | ** merge.tcl script to show the details of the most recent merge |
| 36 | ** command associated with file "zFName". |
| 37 | ** |
| 38 | ** When this routine is called, we know that the mergestat table |
| 39 | ** exists, but we do not know if zFName is mentioned in that table. |
| 40 | */ |
| 41 | static void merge_info_tcl(const char *zFName, int nContext){ |
| 42 | char *zFile; /* Normalized filename */ |
| 43 | char *zTreename; /* Name of the file in the tree */ |
| 44 | Blob fname; /* Filename relative to root */ |
| 45 | Stmt q; /* To query the MERGESTAT table */ |
| 46 | MergeBuilder mb; /* The merge builder object */ |
| 47 | Blob pivot,v1,v2,out; /* Blobs for holding content */ |
| 48 | const char *zFN; /* A filename */ |
| 49 | int rid; /* RID value */ |
| 50 | int sz; /* File size value */ |
| 51 | |
| 52 | zFile = mprintf("%/", zFName); |
| 53 | file_tree_name(zFile, &fname, 0, 1); |
| 54 | zTreename = blob_str(&fname); |
| 55 | db_prepare(&q, |
| 56 | /* 0 1 2 3 4 5 6 7 */ |
| 57 | "SELECT fnp, ridp, fn, ridv, sz, fnm, ridm, fnr" |
| 58 | " FROM mergestat" |
| 59 | " WHERE fnp=%Q OR fnr=%Q", |
| 60 | zTreename, zTreename |
| 61 | ); |
| 62 | if( db_step(&q)!=SQLITE_ROW ){ |
| 63 | db_finalize(&q); |
| 64 | fossil_print("ERROR {don't know anything about file: %s}\n", zTreename); |
| 65 | return; |
| 66 | } |
| 67 | mergebuilder_init_tcl(&mb); |
| 68 | mb.nContext = nContext; |
| 69 | |
| 70 | /* Set up the pivot */ |
| 71 | zFN = db_column_text(&q, 0); |
| 72 | if( zFN==0 ){ |
| 73 | /* No pivot because the file was added */ |
| 74 | mb.zPivot = "(no baseline)"; |
| 75 | blob_zero(&pivot); |
| 76 | }else{ |
| 77 | mb.zPivot = mprintf("%s (baseline)", file_tail(zFN)); |
| 78 | rid = db_column_int(&q, 1); |
| 79 | content_get(rid, &pivot); |
| 80 | } |
| 81 | mb.pPivot = &pivot; |
| 82 | |
| 83 | /* Set up the merge-in as V1 */ |
| 84 | zFN = db_column_text(&q, 5); |
| 85 | if( zFN==0 ){ |
| 86 | /* File deleted in the merged-in branch */ |
| 87 | mb.zV1 = "(deleted file)"; |
| 88 | blob_zero(&v1); |
| 89 | }else{ |
| 90 | mb.zV1 = mprintf("%s (merge-in)", file_tail(zFN)); |
| 91 | rid = db_column_int(&q, 6); |
| 92 | content_get(rid, &v1); |
| 93 | } |
| 94 | mb.pV1 = &v1; |
| 95 | |
| 96 | /* Set up the local content as V2 */ |
| 97 | zFN = db_column_text(&q, 2); |
| 98 | if( zFN==0 ){ |
| 99 | /* File added by merge */ |
| 100 | mb.zV2 = "(no original)"; |
| 101 | blob_zero(&v2); |
| 102 | }else{ |
| 103 | mb.zV2 = mprintf("%s (local)", file_tail(zFN)); |
| 104 | rid = db_column_int(&q, 3); |
| 105 | sz = db_column_int(&q, 4); |
| 106 | if( rid==0 && sz>0 ){ |
| 107 | /* The origin file had been edited so we'll have to pull its |
| 108 | ** original content out of the undo buffer */ |
| 109 | Stmt q2; |
| 110 | db_prepare(&q2, |
| 111 | "SELECT content FROM undo" |
| 112 | " WHERE pathname=%Q AND octet_length(content)=%d", |
| 113 | zFN, sz |
| 114 | ); |
| 115 | blob_zero(&v2); |
| 116 | if( db_step(&q2)==SQLITE_ROW ){ |
| 117 | db_column_blob(&q, 0, &v2); |
| 118 | }else{ |
| 119 | mb.zV2 = "(local content missing)"; |
| 120 | } |
| 121 | db_finalize(&q2); |
| 122 | }else{ |
| 123 | /* The origin file was unchanged when the merge first occurred */ |
| 124 | content_get(rid, &v2); |
| 125 | } |
| 126 | } |
| 127 | mb.pV2 = &v2; |
| 128 | |
| 129 | /* Set up the output */ |
| 130 | zFN = db_column_text(&q, 7); |
| 131 | if( zFN==0 ){ |
| 132 | mb.zOut = "(Merge Result)"; |
| 133 | }else{ |
| 134 | mb.zOut = mprintf("%s (after merge)", file_tail(zFN)); |
| 135 | } |
| 136 | blob_zero(&out); |
| 137 | mb.pOut = &out; |
| 138 | |
| 139 | merge_three_blobs(&mb); |
| 140 | blob_write_to_file(&out, "-"); |
| 141 | |
| 142 | mb.xDestroy(&mb); |
| 143 | blob_reset(&pivot); |
| 144 | blob_reset(&v1); |
| 145 | blob_reset(&v2); |
| 146 | blob_reset(&out); |
| 147 | db_finalize(&q); |
| 148 | } |
| 149 | |
| 150 | /* |
| 151 | ** COMMAND: merge-info |
| 152 | ** |
| 153 | ** Usage: %fossil merge-info [OPTIONS] |
| 154 | ** |
| 155 | ** Display information about the most recent merge operation. |
| 156 | ** |
| 157 | ** Right now, this command basically just dumps the localdb.mergestat |
| 158 | ** table. The plan moving forward is that it can generate data for |
| 159 | ** a Tk-based GUI to show the details of the merge. This command is |
| 160 | ** a work-in-progress. |
| 161 | ** |
| 162 | ** Options: |
| 163 | ** -c|--context N Show N lines of context around each change, |
| 164 | ** with negative N meaning show all content. Only |
| 165 | ** meaningful in combination with --tcl or --tk. |
| 166 | ** --dark Use dark mode for the Tcl/Tk-based GUI |
| 167 | ** --tcl FILE Generate (to stdout) a TCL list containing |
| 168 | ** information needed to display the changes to |
| 169 | ** FILE caused by the most recent merge. |
| 170 | ** --tk Bring up a Tcl/Tk GUI that shows the changes |
| 171 | ** associated with the most recent merge. |
| 172 | ** |
| 173 | */ |
| 174 | void merge_info_cmd(void){ |
| 175 | const char *zCnt; |
| 176 | const char *zTcl; |
| 177 | int bTk; |
| 178 | int bDark = 0; |
| 179 | int nContext; |
| 180 | Stmt q; |
| 181 | |
| 182 | db_must_be_within_tree(); |
| 183 | zTcl = find_option("tcl", 0, 1); |
| 184 | bTk = find_option("tk", 0, 0)!=0; |
| 185 | zCnt = find_option("context", "c", 1); |
| 186 | bDark = find_option("dark", 0, 0)!=0; |
| 187 | verify_all_options(); |
| 188 | if( g.argc>2 ){ |
| 189 | usage("[OPTIONS]"); |
| 190 | } |
| 191 | if( zCnt ){ |
| 192 | nContext = atoi(zCnt); |
| 193 | if( nContext<0 ) nContext = 0xfffffff; |
| 194 | }else{ |
| 195 | nContext = 6; |
| 196 | } |
| 197 | if( !db_table_exists("localdb","mergestat") ){ |
| 198 | if( zTcl ){ |
| 199 | fossil_print("ERROR {no merge data available}\n"); |
| 200 | }else{ |
| 201 | fossil_print("No merge data is available\n"); |
| 202 | } |
| 203 | return; |
| 204 | } |
| 205 | if( bTk ){ |
| 206 | merge_info_tk(bDark, nContext); |
| 207 | return; |
| 208 | } |
| 209 | if( zTcl ){ |
| 210 | merge_info_tcl(zTcl, nContext); |
| 211 | return; |
| 212 | } |
| 213 | db_prepare(&q, |
| 214 | /* 0 1 2 3 4 */ |
| 215 | "SELECT op, fn, fnr, nc, msg FROM mergestat ORDER BY coalesce(fnr,fn)" |
| 216 |
+4
-3
| --- src/merge3.c | ||
| +++ src/merge3.c | ||
| @@ -128,18 +128,18 @@ | ||
| 128 | 128 | blob_append(pOut, mergeMarker[iMark], -1); |
| 129 | 129 | if( ln>0 ) blob_appendf(pOut, " (line %d)", ln); |
| 130 | 130 | ensure_line_end(pOut, useCrLf); |
| 131 | 131 | } |
| 132 | 132 | |
| 133 | +#if INTERFACE | |
| 133 | 134 | /* |
| 134 | 135 | ** This is an abstract class for constructing a merge. |
| 135 | 136 | ** Subclasses of this object format the merge output in different ways. |
| 136 | 137 | ** |
| 137 | 138 | ** To subclass, create an instance of the MergeBuilder object and fill |
| 138 | 139 | ** in appropriate method implementations. |
| 139 | 140 | */ |
| 140 | -typedef struct MergeBuilder MergeBuilder; | |
| 141 | 141 | struct MergeBuilder { |
| 142 | 142 | void (*xStart)(MergeBuilder*); |
| 143 | 143 | void (*xSame)(MergeBuilder*, unsigned int); |
| 144 | 144 | void (*xChngV1)(MergeBuilder*, unsigned int, unsigned int); |
| 145 | 145 | void (*xChngV2)(MergeBuilder*, unsigned int, unsigned int); |
| @@ -164,10 +164,11 @@ | ||
| 164 | 164 | unsigned int lnV1; /* Lines read from v1 */ |
| 165 | 165 | unsigned int lnV2; /* Lines read from v2 */ |
| 166 | 166 | unsigned int lnOut; /* Lines written to out */ |
| 167 | 167 | unsigned int nConflict; /* Number of conflicts seen */ |
| 168 | 168 | }; |
| 169 | +#endif /* INTERFACE */ | |
| 169 | 170 | |
| 170 | 171 | |
| 171 | 172 | /************************* Generic MergeBuilder ******************************/ |
| 172 | 173 | /* These are generic methods for MergeBuilder. They just output debugging |
| 173 | 174 | ** information. But some of them are useful as base methods for other useful |
| @@ -545,11 +546,11 @@ | ||
| 545 | 546 | } |
| 546 | 547 | p->lnPivot += nPivot; |
| 547 | 548 | p->lnV1 += nV1; |
| 548 | 549 | p->lnV2 += nV2; |
| 549 | 550 | } |
| 550 | -static void mergebuilder_init_tcl(MergeBuilder *p){ | |
| 551 | +void mergebuilder_init_tcl(MergeBuilder *p){ | |
| 551 | 552 | mergebuilder_init(p); |
| 552 | 553 | p->xStart = tclStart; |
| 553 | 554 | p->xSame = tclSame; |
| 554 | 555 | p->xChngV1 = tclChngV1; |
| 555 | 556 | p->xChngV2 = tclChngV2; |
| @@ -618,11 +619,11 @@ | ||
| 618 | 619 | ** The return is 0 upon complete success. If any input file is binary, |
| 619 | 620 | ** -1 is returned and pOut is unmodified. If there are merge |
| 620 | 621 | ** conflicts, the merge proceeds as best as it can and the number |
| 621 | 622 | ** of conflicts is returns |
| 622 | 623 | */ |
| 623 | -static int merge_three_blobs(MergeBuilder *p){ | |
| 624 | +int merge_three_blobs(MergeBuilder *p){ | |
| 624 | 625 | int *aC1; /* Changes from pPivot to pV1 */ |
| 625 | 626 | int *aC2; /* Changes from pPivot to pV2 */ |
| 626 | 627 | int i1, i2; /* Index into aC1[] and aC2[] */ |
| 627 | 628 | int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */ |
| 628 | 629 | int limit1, limit2; /* Sizes of aC1[] and aC2[] */ |
| 629 | 630 |
| --- src/merge3.c | |
| +++ src/merge3.c | |
| @@ -128,18 +128,18 @@ | |
| 128 | blob_append(pOut, mergeMarker[iMark], -1); |
| 129 | if( ln>0 ) blob_appendf(pOut, " (line %d)", ln); |
| 130 | ensure_line_end(pOut, useCrLf); |
| 131 | } |
| 132 | |
| 133 | /* |
| 134 | ** This is an abstract class for constructing a merge. |
| 135 | ** Subclasses of this object format the merge output in different ways. |
| 136 | ** |
| 137 | ** To subclass, create an instance of the MergeBuilder object and fill |
| 138 | ** in appropriate method implementations. |
| 139 | */ |
| 140 | typedef struct MergeBuilder MergeBuilder; |
| 141 | struct MergeBuilder { |
| 142 | void (*xStart)(MergeBuilder*); |
| 143 | void (*xSame)(MergeBuilder*, unsigned int); |
| 144 | void (*xChngV1)(MergeBuilder*, unsigned int, unsigned int); |
| 145 | void (*xChngV2)(MergeBuilder*, unsigned int, unsigned int); |
| @@ -164,10 +164,11 @@ | |
| 164 | unsigned int lnV1; /* Lines read from v1 */ |
| 165 | unsigned int lnV2; /* Lines read from v2 */ |
| 166 | unsigned int lnOut; /* Lines written to out */ |
| 167 | unsigned int nConflict; /* Number of conflicts seen */ |
| 168 | }; |
| 169 | |
| 170 | |
| 171 | /************************* Generic MergeBuilder ******************************/ |
| 172 | /* These are generic methods for MergeBuilder. They just output debugging |
| 173 | ** information. But some of them are useful as base methods for other useful |
| @@ -545,11 +546,11 @@ | |
| 545 | } |
| 546 | p->lnPivot += nPivot; |
| 547 | p->lnV1 += nV1; |
| 548 | p->lnV2 += nV2; |
| 549 | } |
| 550 | static void mergebuilder_init_tcl(MergeBuilder *p){ |
| 551 | mergebuilder_init(p); |
| 552 | p->xStart = tclStart; |
| 553 | p->xSame = tclSame; |
| 554 | p->xChngV1 = tclChngV1; |
| 555 | p->xChngV2 = tclChngV2; |
| @@ -618,11 +619,11 @@ | |
| 618 | ** The return is 0 upon complete success. If any input file is binary, |
| 619 | ** -1 is returned and pOut is unmodified. If there are merge |
| 620 | ** conflicts, the merge proceeds as best as it can and the number |
| 621 | ** of conflicts is returns |
| 622 | */ |
| 623 | static int merge_three_blobs(MergeBuilder *p){ |
| 624 | int *aC1; /* Changes from pPivot to pV1 */ |
| 625 | int *aC2; /* Changes from pPivot to pV2 */ |
| 626 | int i1, i2; /* Index into aC1[] and aC2[] */ |
| 627 | int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */ |
| 628 | int limit1, limit2; /* Sizes of aC1[] and aC2[] */ |
| 629 |
| --- src/merge3.c | |
| +++ src/merge3.c | |
| @@ -128,18 +128,18 @@ | |
| 128 | blob_append(pOut, mergeMarker[iMark], -1); |
| 129 | if( ln>0 ) blob_appendf(pOut, " (line %d)", ln); |
| 130 | ensure_line_end(pOut, useCrLf); |
| 131 | } |
| 132 | |
| 133 | #if INTERFACE |
| 134 | /* |
| 135 | ** This is an abstract class for constructing a merge. |
| 136 | ** Subclasses of this object format the merge output in different ways. |
| 137 | ** |
| 138 | ** To subclass, create an instance of the MergeBuilder object and fill |
| 139 | ** in appropriate method implementations. |
| 140 | */ |
| 141 | struct MergeBuilder { |
| 142 | void (*xStart)(MergeBuilder*); |
| 143 | void (*xSame)(MergeBuilder*, unsigned int); |
| 144 | void (*xChngV1)(MergeBuilder*, unsigned int, unsigned int); |
| 145 | void (*xChngV2)(MergeBuilder*, unsigned int, unsigned int); |
| @@ -164,10 +164,11 @@ | |
| 164 | unsigned int lnV1; /* Lines read from v1 */ |
| 165 | unsigned int lnV2; /* Lines read from v2 */ |
| 166 | unsigned int lnOut; /* Lines written to out */ |
| 167 | unsigned int nConflict; /* Number of conflicts seen */ |
| 168 | }; |
| 169 | #endif /* INTERFACE */ |
| 170 | |
| 171 | |
| 172 | /************************* Generic MergeBuilder ******************************/ |
| 173 | /* These are generic methods for MergeBuilder. They just output debugging |
| 174 | ** information. But some of them are useful as base methods for other useful |
| @@ -545,11 +546,11 @@ | |
| 546 | } |
| 547 | p->lnPivot += nPivot; |
| 548 | p->lnV1 += nV1; |
| 549 | p->lnV2 += nV2; |
| 550 | } |
| 551 | void mergebuilder_init_tcl(MergeBuilder *p){ |
| 552 | mergebuilder_init(p); |
| 553 | p->xStart = tclStart; |
| 554 | p->xSame = tclSame; |
| 555 | p->xChngV1 = tclChngV1; |
| 556 | p->xChngV2 = tclChngV2; |
| @@ -618,11 +619,11 @@ | |
| 619 | ** The return is 0 upon complete success. If any input file is binary, |
| 620 | ** -1 is returned and pOut is unmodified. If there are merge |
| 621 | ** conflicts, the merge proceeds as best as it can and the number |
| 622 | ** of conflicts is returns |
| 623 | */ |
| 624 | int merge_three_blobs(MergeBuilder *p){ |
| 625 | int *aC1; /* Changes from pPivot to pV1 */ |
| 626 | int *aC2; /* Changes from pPivot to pV2 */ |
| 627 | int i1, i2; /* Index into aC1[] and aC2[] */ |
| 628 | int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */ |
| 629 | int limit1, limit2; /* Sizes of aC1[] and aC2[] */ |
| 630 |