Fossil SCM
Merge the changes to show unresolved conflicts in "fossil status" and to prevent committing unresolved conflicts.
Commit
7d34d1748a1c72fb4328374a33e084a12351b633
Parent
8d758d37153b1fe…
2 files changed
+20
-3
+49
-12
+20
-3
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -87,11 +87,15 @@ | ||
| 87 | 87 | }else if( isChnged==2 ){ |
| 88 | 88 | blob_appendf(report, "UPDATED_BY_MERGE %s\n", zDisplayName); |
| 89 | 89 | }else if( isChnged==3 ){ |
| 90 | 90 | blob_appendf(report, "ADDED_BY_MERGE %s\n", zDisplayName); |
| 91 | 91 | }else if( isChnged==1 ){ |
| 92 | - blob_appendf(report, "EDITED %s\n", zDisplayName); | |
| 92 | + if( file_contains_merge_marker(zFullName) ){ | |
| 93 | + blob_appendf(report, "CONFLICT %s\n", zDisplayName); | |
| 94 | + }else{ | |
| 95 | + blob_appendf(report, "EDITED %s\n", zDisplayName); | |
| 96 | + } | |
| 93 | 97 | }else if( isRenamed ){ |
| 94 | 98 | blob_appendf(report, "RENAMED %s\n", zDisplayName); |
| 95 | 99 | } |
| 96 | 100 | free(zFullName); |
| 97 | 101 | } |
| @@ -976,10 +980,11 @@ | ||
| 976 | 980 | ** --force|-f allow forking with this commit |
| 977 | 981 | ** --message-file|-M FILE read the commit comment from given file |
| 978 | 982 | ** --nosign do not attempt to sign this commit with gpg |
| 979 | 983 | ** --private do not sync changes and their descendants |
| 980 | 984 | ** --tag TAG-NAME assign given tag TAG-NAME to the checkin |
| 985 | +** --conflict allow unresolved merge conflicts | |
| 981 | 986 | ** |
| 982 | 987 | ** See also: branch, changes, checkout, extra, sync |
| 983 | 988 | */ |
| 984 | 989 | void commit_cmd(void){ |
| 985 | 990 | int hasChanges; /* True if unsaved changes exist */ |
| @@ -993,10 +998,11 @@ | ||
| 993 | 998 | int noSign = 0; /* True to omit signing the manifest using GPG */ |
| 994 | 999 | int isAMerge = 0; /* True if checking in a merge */ |
| 995 | 1000 | int forceFlag = 0; /* Force a fork */ |
| 996 | 1001 | int forceDelta = 0; /* Force a delta-manifest */ |
| 997 | 1002 | int forceBaseline = 0; /* Force a baseline-manifest */ |
| 1003 | + int allowConflict = 0; /* Allow unresolve merge conflicts */ | |
| 998 | 1004 | char *zManifestFile; /* Name of the manifest file */ |
| 999 | 1005 | int useCksum; /* True if checksums should be computed and verified */ |
| 1000 | 1006 | int outputManifest; /* True to output "manifest" and "manifest.uuid" */ |
| 1001 | 1007 | int testRun; /* True for a test run. Debugging only */ |
| 1002 | 1008 | const char *zBranch; /* Create a new branch with this name */ |
| @@ -1012,10 +1018,11 @@ | ||
| 1012 | 1018 | Blob muuid; /* Manifest uuid */ |
| 1013 | 1019 | Blob cksum1, cksum2; /* Before and after commit checksums */ |
| 1014 | 1020 | Blob cksum1b; /* Checksum recorded in the manifest */ |
| 1015 | 1021 | int szD; /* Size of the delta manifest */ |
| 1016 | 1022 | int szB; /* Size of the baseline manifest */ |
| 1023 | + int nConflict = 0; /* Number of unresolved merge conflicts */ | |
| 1017 | 1024 | |
| 1018 | 1025 | url_proxy_options(); |
| 1019 | 1026 | noSign = find_option("nosign",0,0)!=0; |
| 1020 | 1027 | forceDelta = find_option("delta",0,0)!=0; |
| 1021 | 1028 | forceBaseline = find_option("baseline",0,0)!=0; |
| @@ -1040,10 +1047,11 @@ | ||
| 1040 | 1047 | if( zBranch==0 ) zBranch = "private"; |
| 1041 | 1048 | if( zBrClr==0 && zColor==0 ) zBrClr = "#fec084"; /* Orange */ |
| 1042 | 1049 | } |
| 1043 | 1050 | zDateOvrd = find_option("date-override",0,1); |
| 1044 | 1051 | zUserOvrd = find_option("user-override",0,1); |
| 1052 | + allowConflict = find_option("conflict",0,0)!=0; | |
| 1045 | 1053 | db_must_be_within_tree(); |
| 1046 | 1054 | noSign = db_get_boolean("omitsign", 0)|noSign; |
| 1047 | 1055 | if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; } |
| 1048 | 1056 | useCksum = db_get_boolean("repo-cksum", 1); |
| 1049 | 1057 | outputManifest = db_get_boolean("manifest", 0); |
| @@ -1194,42 +1202,51 @@ | ||
| 1194 | 1202 | /* Step 1: Insert records for all modified files into the blob |
| 1195 | 1203 | ** table. If there were arguments passed to this command, only |
| 1196 | 1204 | ** the identified fils are inserted (if they have been modified). |
| 1197 | 1205 | */ |
| 1198 | 1206 | db_prepare(&q, |
| 1199 | - "SELECT id, %Q || pathname, mrid, %s FROM vfile " | |
| 1207 | + "SELECT id, %Q || pathname, mrid, %s, chnged FROM vfile " | |
| 1200 | 1208 | "WHERE chnged==1 AND NOT deleted AND is_selected(id)", |
| 1201 | 1209 | g.zLocalRoot, glob_expr("pathname", db_get("crnl-glob","")) |
| 1202 | 1210 | ); |
| 1203 | 1211 | while( db_step(&q)==SQLITE_ROW ){ |
| 1204 | 1212 | int id, rid; |
| 1205 | 1213 | const char *zFullname; |
| 1206 | 1214 | Blob content; |
| 1207 | - int crnlOk; | |
| 1215 | + int crnlOk, chnged; | |
| 1208 | 1216 | |
| 1209 | 1217 | id = db_column_int(&q, 0); |
| 1210 | 1218 | zFullname = db_column_text(&q, 1); |
| 1211 | 1219 | rid = db_column_int(&q, 2); |
| 1212 | 1220 | crnlOk = db_column_int(&q, 3); |
| 1221 | + chnged = db_column_int(&q, 4); | |
| 1213 | 1222 | |
| 1214 | 1223 | blob_zero(&content); |
| 1215 | 1224 | if( file_wd_islink(zFullname) ){ |
| 1216 | 1225 | /* Instead of file content, put link destination path */ |
| 1217 | 1226 | blob_read_link(&content, zFullname); |
| 1218 | 1227 | }else{ |
| 1219 | 1228 | blob_read_from_file(&content, zFullname); |
| 1220 | 1229 | } |
| 1221 | 1230 | if( !crnlOk ) cr_warning(&content, zFullname); |
| 1231 | + if( chnged==1 && contains_merge_marker(&content) ){ | |
| 1232 | + nConflict++; | |
| 1233 | + fossil_print("possible unresolved merge conflict in %s\n", | |
| 1234 | + zFullname+strlen(g.zLocalRoot)); | |
| 1235 | + } | |
| 1222 | 1236 | nrid = content_put(&content); |
| 1223 | 1237 | blob_reset(&content); |
| 1224 | 1238 | if( rid>0 ){ |
| 1225 | 1239 | content_deltify(rid, nrid, 0); |
| 1226 | 1240 | } |
| 1227 | 1241 | db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id); |
| 1228 | 1242 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); |
| 1229 | 1243 | } |
| 1230 | 1244 | db_finalize(&q); |
| 1245 | + if( nConflict && !allowConflict ){ | |
| 1246 | + fossil_fatal("abort due to unresolve merge conflicts"); | |
| 1247 | + } | |
| 1231 | 1248 | |
| 1232 | 1249 | /* Create the new manifest */ |
| 1233 | 1250 | if( blob_size(&comment)==0 ){ |
| 1234 | 1251 | blob_append(&comment, "(no comment)", -1); |
| 1235 | 1252 | } |
| 1236 | 1253 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -87,11 +87,15 @@ | |
| 87 | }else if( isChnged==2 ){ |
| 88 | blob_appendf(report, "UPDATED_BY_MERGE %s\n", zDisplayName); |
| 89 | }else if( isChnged==3 ){ |
| 90 | blob_appendf(report, "ADDED_BY_MERGE %s\n", zDisplayName); |
| 91 | }else if( isChnged==1 ){ |
| 92 | blob_appendf(report, "EDITED %s\n", zDisplayName); |
| 93 | }else if( isRenamed ){ |
| 94 | blob_appendf(report, "RENAMED %s\n", zDisplayName); |
| 95 | } |
| 96 | free(zFullName); |
| 97 | } |
| @@ -976,10 +980,11 @@ | |
| 976 | ** --force|-f allow forking with this commit |
| 977 | ** --message-file|-M FILE read the commit comment from given file |
| 978 | ** --nosign do not attempt to sign this commit with gpg |
| 979 | ** --private do not sync changes and their descendants |
| 980 | ** --tag TAG-NAME assign given tag TAG-NAME to the checkin |
| 981 | ** |
| 982 | ** See also: branch, changes, checkout, extra, sync |
| 983 | */ |
| 984 | void commit_cmd(void){ |
| 985 | int hasChanges; /* True if unsaved changes exist */ |
| @@ -993,10 +998,11 @@ | |
| 993 | int noSign = 0; /* True to omit signing the manifest using GPG */ |
| 994 | int isAMerge = 0; /* True if checking in a merge */ |
| 995 | int forceFlag = 0; /* Force a fork */ |
| 996 | int forceDelta = 0; /* Force a delta-manifest */ |
| 997 | int forceBaseline = 0; /* Force a baseline-manifest */ |
| 998 | char *zManifestFile; /* Name of the manifest file */ |
| 999 | int useCksum; /* True if checksums should be computed and verified */ |
| 1000 | int outputManifest; /* True to output "manifest" and "manifest.uuid" */ |
| 1001 | int testRun; /* True for a test run. Debugging only */ |
| 1002 | const char *zBranch; /* Create a new branch with this name */ |
| @@ -1012,10 +1018,11 @@ | |
| 1012 | Blob muuid; /* Manifest uuid */ |
| 1013 | Blob cksum1, cksum2; /* Before and after commit checksums */ |
| 1014 | Blob cksum1b; /* Checksum recorded in the manifest */ |
| 1015 | int szD; /* Size of the delta manifest */ |
| 1016 | int szB; /* Size of the baseline manifest */ |
| 1017 | |
| 1018 | url_proxy_options(); |
| 1019 | noSign = find_option("nosign",0,0)!=0; |
| 1020 | forceDelta = find_option("delta",0,0)!=0; |
| 1021 | forceBaseline = find_option("baseline",0,0)!=0; |
| @@ -1040,10 +1047,11 @@ | |
| 1040 | if( zBranch==0 ) zBranch = "private"; |
| 1041 | if( zBrClr==0 && zColor==0 ) zBrClr = "#fec084"; /* Orange */ |
| 1042 | } |
| 1043 | zDateOvrd = find_option("date-override",0,1); |
| 1044 | zUserOvrd = find_option("user-override",0,1); |
| 1045 | db_must_be_within_tree(); |
| 1046 | noSign = db_get_boolean("omitsign", 0)|noSign; |
| 1047 | if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; } |
| 1048 | useCksum = db_get_boolean("repo-cksum", 1); |
| 1049 | outputManifest = db_get_boolean("manifest", 0); |
| @@ -1194,42 +1202,51 @@ | |
| 1194 | /* Step 1: Insert records for all modified files into the blob |
| 1195 | ** table. If there were arguments passed to this command, only |
| 1196 | ** the identified fils are inserted (if they have been modified). |
| 1197 | */ |
| 1198 | db_prepare(&q, |
| 1199 | "SELECT id, %Q || pathname, mrid, %s FROM vfile " |
| 1200 | "WHERE chnged==1 AND NOT deleted AND is_selected(id)", |
| 1201 | g.zLocalRoot, glob_expr("pathname", db_get("crnl-glob","")) |
| 1202 | ); |
| 1203 | while( db_step(&q)==SQLITE_ROW ){ |
| 1204 | int id, rid; |
| 1205 | const char *zFullname; |
| 1206 | Blob content; |
| 1207 | int crnlOk; |
| 1208 | |
| 1209 | id = db_column_int(&q, 0); |
| 1210 | zFullname = db_column_text(&q, 1); |
| 1211 | rid = db_column_int(&q, 2); |
| 1212 | crnlOk = db_column_int(&q, 3); |
| 1213 | |
| 1214 | blob_zero(&content); |
| 1215 | if( file_wd_islink(zFullname) ){ |
| 1216 | /* Instead of file content, put link destination path */ |
| 1217 | blob_read_link(&content, zFullname); |
| 1218 | }else{ |
| 1219 | blob_read_from_file(&content, zFullname); |
| 1220 | } |
| 1221 | if( !crnlOk ) cr_warning(&content, zFullname); |
| 1222 | nrid = content_put(&content); |
| 1223 | blob_reset(&content); |
| 1224 | if( rid>0 ){ |
| 1225 | content_deltify(rid, nrid, 0); |
| 1226 | } |
| 1227 | db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id); |
| 1228 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); |
| 1229 | } |
| 1230 | db_finalize(&q); |
| 1231 | |
| 1232 | /* Create the new manifest */ |
| 1233 | if( blob_size(&comment)==0 ){ |
| 1234 | blob_append(&comment, "(no comment)", -1); |
| 1235 | } |
| 1236 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -87,11 +87,15 @@ | |
| 87 | }else if( isChnged==2 ){ |
| 88 | blob_appendf(report, "UPDATED_BY_MERGE %s\n", zDisplayName); |
| 89 | }else if( isChnged==3 ){ |
| 90 | blob_appendf(report, "ADDED_BY_MERGE %s\n", zDisplayName); |
| 91 | }else if( isChnged==1 ){ |
| 92 | if( file_contains_merge_marker(zFullName) ){ |
| 93 | blob_appendf(report, "CONFLICT %s\n", zDisplayName); |
| 94 | }else{ |
| 95 | blob_appendf(report, "EDITED %s\n", zDisplayName); |
| 96 | } |
| 97 | }else if( isRenamed ){ |
| 98 | blob_appendf(report, "RENAMED %s\n", zDisplayName); |
| 99 | } |
| 100 | free(zFullName); |
| 101 | } |
| @@ -976,10 +980,11 @@ | |
| 980 | ** --force|-f allow forking with this commit |
| 981 | ** --message-file|-M FILE read the commit comment from given file |
| 982 | ** --nosign do not attempt to sign this commit with gpg |
| 983 | ** --private do not sync changes and their descendants |
| 984 | ** --tag TAG-NAME assign given tag TAG-NAME to the checkin |
| 985 | ** --conflict allow unresolved merge conflicts |
| 986 | ** |
| 987 | ** See also: branch, changes, checkout, extra, sync |
| 988 | */ |
| 989 | void commit_cmd(void){ |
| 990 | int hasChanges; /* True if unsaved changes exist */ |
| @@ -993,10 +998,11 @@ | |
| 998 | int noSign = 0; /* True to omit signing the manifest using GPG */ |
| 999 | int isAMerge = 0; /* True if checking in a merge */ |
| 1000 | int forceFlag = 0; /* Force a fork */ |
| 1001 | int forceDelta = 0; /* Force a delta-manifest */ |
| 1002 | int forceBaseline = 0; /* Force a baseline-manifest */ |
| 1003 | int allowConflict = 0; /* Allow unresolve merge conflicts */ |
| 1004 | char *zManifestFile; /* Name of the manifest file */ |
| 1005 | int useCksum; /* True if checksums should be computed and verified */ |
| 1006 | int outputManifest; /* True to output "manifest" and "manifest.uuid" */ |
| 1007 | int testRun; /* True for a test run. Debugging only */ |
| 1008 | const char *zBranch; /* Create a new branch with this name */ |
| @@ -1012,10 +1018,11 @@ | |
| 1018 | Blob muuid; /* Manifest uuid */ |
| 1019 | Blob cksum1, cksum2; /* Before and after commit checksums */ |
| 1020 | Blob cksum1b; /* Checksum recorded in the manifest */ |
| 1021 | int szD; /* Size of the delta manifest */ |
| 1022 | int szB; /* Size of the baseline manifest */ |
| 1023 | int nConflict = 0; /* Number of unresolved merge conflicts */ |
| 1024 | |
| 1025 | url_proxy_options(); |
| 1026 | noSign = find_option("nosign",0,0)!=0; |
| 1027 | forceDelta = find_option("delta",0,0)!=0; |
| 1028 | forceBaseline = find_option("baseline",0,0)!=0; |
| @@ -1040,10 +1047,11 @@ | |
| 1047 | if( zBranch==0 ) zBranch = "private"; |
| 1048 | if( zBrClr==0 && zColor==0 ) zBrClr = "#fec084"; /* Orange */ |
| 1049 | } |
| 1050 | zDateOvrd = find_option("date-override",0,1); |
| 1051 | zUserOvrd = find_option("user-override",0,1); |
| 1052 | allowConflict = find_option("conflict",0,0)!=0; |
| 1053 | db_must_be_within_tree(); |
| 1054 | noSign = db_get_boolean("omitsign", 0)|noSign; |
| 1055 | if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; } |
| 1056 | useCksum = db_get_boolean("repo-cksum", 1); |
| 1057 | outputManifest = db_get_boolean("manifest", 0); |
| @@ -1194,42 +1202,51 @@ | |
| 1202 | /* Step 1: Insert records for all modified files into the blob |
| 1203 | ** table. If there were arguments passed to this command, only |
| 1204 | ** the identified fils are inserted (if they have been modified). |
| 1205 | */ |
| 1206 | db_prepare(&q, |
| 1207 | "SELECT id, %Q || pathname, mrid, %s, chnged FROM vfile " |
| 1208 | "WHERE chnged==1 AND NOT deleted AND is_selected(id)", |
| 1209 | g.zLocalRoot, glob_expr("pathname", db_get("crnl-glob","")) |
| 1210 | ); |
| 1211 | while( db_step(&q)==SQLITE_ROW ){ |
| 1212 | int id, rid; |
| 1213 | const char *zFullname; |
| 1214 | Blob content; |
| 1215 | int crnlOk, chnged; |
| 1216 | |
| 1217 | id = db_column_int(&q, 0); |
| 1218 | zFullname = db_column_text(&q, 1); |
| 1219 | rid = db_column_int(&q, 2); |
| 1220 | crnlOk = db_column_int(&q, 3); |
| 1221 | chnged = db_column_int(&q, 4); |
| 1222 | |
| 1223 | blob_zero(&content); |
| 1224 | if( file_wd_islink(zFullname) ){ |
| 1225 | /* Instead of file content, put link destination path */ |
| 1226 | blob_read_link(&content, zFullname); |
| 1227 | }else{ |
| 1228 | blob_read_from_file(&content, zFullname); |
| 1229 | } |
| 1230 | if( !crnlOk ) cr_warning(&content, zFullname); |
| 1231 | if( chnged==1 && contains_merge_marker(&content) ){ |
| 1232 | nConflict++; |
| 1233 | fossil_print("possible unresolved merge conflict in %s\n", |
| 1234 | zFullname+strlen(g.zLocalRoot)); |
| 1235 | } |
| 1236 | nrid = content_put(&content); |
| 1237 | blob_reset(&content); |
| 1238 | if( rid>0 ){ |
| 1239 | content_deltify(rid, nrid, 0); |
| 1240 | } |
| 1241 | db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d WHERE id=%d", nrid,nrid,id); |
| 1242 | db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid); |
| 1243 | } |
| 1244 | db_finalize(&q); |
| 1245 | if( nConflict && !allowConflict ){ |
| 1246 | fossil_fatal("abort due to unresolve merge conflicts"); |
| 1247 | } |
| 1248 | |
| 1249 | /* Create the new manifest */ |
| 1250 | if( blob_size(&comment)==0 ){ |
| 1251 | blob_append(&comment, "(no comment)", -1); |
| 1252 | } |
| 1253 |
+49
-12
| --- src/merge3.c | ||
| +++ src/merge3.c | ||
| @@ -133,10 +133,20 @@ | ||
| 133 | 133 | i += 3; |
| 134 | 134 | } |
| 135 | 135 | return i; |
| 136 | 136 | } |
| 137 | 137 | |
| 138 | +/* | |
| 139 | +** Text of boundary markers for merge conflicts. | |
| 140 | +*/ | |
| 141 | +static char const * const mergeMarker[] = { | |
| 142 | + /*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/ | |
| 143 | + "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<\n", | |
| 144 | + "======= COMMON ANCESTOR content follows ============================\n", | |
| 145 | + "======= MERGED IN content follows ==================================\n", | |
| 146 | + ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" | |
| 147 | +}; | |
| 138 | 148 | |
| 139 | 149 | |
| 140 | 150 | /* |
| 141 | 151 | ** Do a three-way merge. Initialize pOut to contain the result. |
| 142 | 152 | ** |
| @@ -154,18 +164,10 @@ | ||
| 154 | 164 | int *aC2; /* Changes from pPivot to pV2 */ |
| 155 | 165 | int i1, i2; /* Index into aC1[] and aC2[] */ |
| 156 | 166 | int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */ |
| 157 | 167 | int limit1, limit2; /* Sizes of aC1[] and aC2[] */ |
| 158 | 168 | int nConflict = 0; /* Number of merge conflicts seen so far */ |
| 159 | - static const char zBegin[] = | |
| 160 | - "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<\n"; | |
| 161 | - static const char zMid1[] = | |
| 162 | - "======= COMMON ANCESTOR content follows ============================\n"; | |
| 163 | - static const char zMid2[] = | |
| 164 | - "======= MERGED IN content follows ==================================\n"; | |
| 165 | - static const char zEnd[] = | |
| 166 | - ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"; | |
| 167 | 169 | |
| 168 | 170 | blob_zero(pOut); /* Merge results stored in pOut */ |
| 169 | 171 | |
| 170 | 172 | /* Compute the edits that occur from pPivot => pV1 (into aC1) |
| 171 | 173 | ** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is |
| @@ -266,17 +268,17 @@ | ||
| 266 | 268 | nConflict++; |
| 267 | 269 | while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){ |
| 268 | 270 | sz++; |
| 269 | 271 | } |
| 270 | 272 | DEBUG( printf("CONFLICT %d\n", sz); ) |
| 271 | - blob_appendf(pOut, zBegin); | |
| 273 | + blob_appendf(pOut, mergeMarker[0]); | |
| 272 | 274 | i1 = output_one_side(pOut, pV1, aC1, i1, sz); |
| 273 | - blob_appendf(pOut, zMid1); | |
| 275 | + blob_appendf(pOut, mergeMarker[1]); | |
| 274 | 276 | blob_copy_lines(pOut, pPivot, sz); |
| 275 | - blob_appendf(pOut, zMid2); | |
| 277 | + blob_appendf(pOut, mergeMarker[2]); | |
| 276 | 278 | i2 = output_one_side(pOut, pV2, aC2, i2, sz); |
| 277 | - blob_appendf(pOut, zEnd); | |
| 279 | + blob_appendf(pOut, mergeMarker[3]); | |
| 278 | 280 | } |
| 279 | 281 | |
| 280 | 282 | /* If we are finished with an edit triple, advance to the next |
| 281 | 283 | ** triple. |
| 282 | 284 | */ |
| @@ -301,10 +303,45 @@ | ||
| 301 | 303 | |
| 302 | 304 | free(aC1); |
| 303 | 305 | free(aC2); |
| 304 | 306 | return nConflict; |
| 305 | 307 | } |
| 308 | + | |
| 309 | +/* | |
| 310 | +** Return true if the input string contains a merge marker on a line by | |
| 311 | +** itself. | |
| 312 | +*/ | |
| 313 | +int contains_merge_marker(Blob *p){ | |
| 314 | + int i, j; | |
| 315 | + int len = (int)strlen(mergeMarker[0]); | |
| 316 | + const char *z = blob_buffer(p); | |
| 317 | + int n = blob_size(p) - len + 1; | |
| 318 | + assert( len==(int)strlen(mergeMarker[1]) ); | |
| 319 | + assert( len==(int)strlen(mergeMarker[2]) ); | |
| 320 | + assert( len==(int)strlen(mergeMarker[3]) ); | |
| 321 | + assert( sizeof(mergeMarker)/sizeof(mergeMarker[0])==4 ); | |
| 322 | + for(i=0; i<n; ){ | |
| 323 | + for(j=0; j<4; j++){ | |
| 324 | + if( memcmp(&z[i], mergeMarker[j], len)==0 ) return 1; | |
| 325 | + } | |
| 326 | + while( i<n && z[i]!='\n' ){ i++; } | |
| 327 | + while( i<n && z[i]=='\n' ){ i++; } | |
| 328 | + } | |
| 329 | + return 0; | |
| 330 | +} | |
| 331 | + | |
| 332 | +/* | |
| 333 | +** Return true if the named file contains an unresolved merge marker line. | |
| 334 | +*/ | |
| 335 | +int file_contains_merge_marker(const char *zFullpath){ | |
| 336 | + Blob file; | |
| 337 | + int rc; | |
| 338 | + blob_read_from_file(&file, zFullpath); | |
| 339 | + rc = contains_merge_marker(&file); | |
| 340 | + blob_reset(&file); | |
| 341 | + return rc; | |
| 342 | +} | |
| 306 | 343 | |
| 307 | 344 | /* |
| 308 | 345 | ** COMMAND: test-3-way-merge |
| 309 | 346 | ** |
| 310 | 347 | ** Usage: %fossil test-3-way-merge PIVOT V1 V2 MERGED |
| 311 | 348 |
| --- src/merge3.c | |
| +++ src/merge3.c | |
| @@ -133,10 +133,20 @@ | |
| 133 | i += 3; |
| 134 | } |
| 135 | return i; |
| 136 | } |
| 137 | |
| 138 | |
| 139 | |
| 140 | /* |
| 141 | ** Do a three-way merge. Initialize pOut to contain the result. |
| 142 | ** |
| @@ -154,18 +164,10 @@ | |
| 154 | int *aC2; /* Changes from pPivot to pV2 */ |
| 155 | int i1, i2; /* Index into aC1[] and aC2[] */ |
| 156 | int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */ |
| 157 | int limit1, limit2; /* Sizes of aC1[] and aC2[] */ |
| 158 | int nConflict = 0; /* Number of merge conflicts seen so far */ |
| 159 | static const char zBegin[] = |
| 160 | "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<\n"; |
| 161 | static const char zMid1[] = |
| 162 | "======= COMMON ANCESTOR content follows ============================\n"; |
| 163 | static const char zMid2[] = |
| 164 | "======= MERGED IN content follows ==================================\n"; |
| 165 | static const char zEnd[] = |
| 166 | ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"; |
| 167 | |
| 168 | blob_zero(pOut); /* Merge results stored in pOut */ |
| 169 | |
| 170 | /* Compute the edits that occur from pPivot => pV1 (into aC1) |
| 171 | ** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is |
| @@ -266,17 +268,17 @@ | |
| 266 | nConflict++; |
| 267 | while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){ |
| 268 | sz++; |
| 269 | } |
| 270 | DEBUG( printf("CONFLICT %d\n", sz); ) |
| 271 | blob_appendf(pOut, zBegin); |
| 272 | i1 = output_one_side(pOut, pV1, aC1, i1, sz); |
| 273 | blob_appendf(pOut, zMid1); |
| 274 | blob_copy_lines(pOut, pPivot, sz); |
| 275 | blob_appendf(pOut, zMid2); |
| 276 | i2 = output_one_side(pOut, pV2, aC2, i2, sz); |
| 277 | blob_appendf(pOut, zEnd); |
| 278 | } |
| 279 | |
| 280 | /* If we are finished with an edit triple, advance to the next |
| 281 | ** triple. |
| 282 | */ |
| @@ -301,10 +303,45 @@ | |
| 301 | |
| 302 | free(aC1); |
| 303 | free(aC2); |
| 304 | return nConflict; |
| 305 | } |
| 306 | |
| 307 | /* |
| 308 | ** COMMAND: test-3-way-merge |
| 309 | ** |
| 310 | ** Usage: %fossil test-3-way-merge PIVOT V1 V2 MERGED |
| 311 |
| --- src/merge3.c | |
| +++ src/merge3.c | |
| @@ -133,10 +133,20 @@ | |
| 133 | i += 3; |
| 134 | } |
| 135 | return i; |
| 136 | } |
| 137 | |
| 138 | /* |
| 139 | ** Text of boundary markers for merge conflicts. |
| 140 | */ |
| 141 | static char const * const mergeMarker[] = { |
| 142 | /*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/ |
| 143 | "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<\n", |
| 144 | "======= COMMON ANCESTOR content follows ============================\n", |
| 145 | "======= MERGED IN content follows ==================================\n", |
| 146 | ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" |
| 147 | }; |
| 148 | |
| 149 | |
| 150 | /* |
| 151 | ** Do a three-way merge. Initialize pOut to contain the result. |
| 152 | ** |
| @@ -154,18 +164,10 @@ | |
| 164 | int *aC2; /* Changes from pPivot to pV2 */ |
| 165 | int i1, i2; /* Index into aC1[] and aC2[] */ |
| 166 | int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */ |
| 167 | int limit1, limit2; /* Sizes of aC1[] and aC2[] */ |
| 168 | int nConflict = 0; /* Number of merge conflicts seen so far */ |
| 169 | |
| 170 | blob_zero(pOut); /* Merge results stored in pOut */ |
| 171 | |
| 172 | /* Compute the edits that occur from pPivot => pV1 (into aC1) |
| 173 | ** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is |
| @@ -266,17 +268,17 @@ | |
| 268 | nConflict++; |
| 269 | while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){ |
| 270 | sz++; |
| 271 | } |
| 272 | DEBUG( printf("CONFLICT %d\n", sz); ) |
| 273 | blob_appendf(pOut, mergeMarker[0]); |
| 274 | i1 = output_one_side(pOut, pV1, aC1, i1, sz); |
| 275 | blob_appendf(pOut, mergeMarker[1]); |
| 276 | blob_copy_lines(pOut, pPivot, sz); |
| 277 | blob_appendf(pOut, mergeMarker[2]); |
| 278 | i2 = output_one_side(pOut, pV2, aC2, i2, sz); |
| 279 | blob_appendf(pOut, mergeMarker[3]); |
| 280 | } |
| 281 | |
| 282 | /* If we are finished with an edit triple, advance to the next |
| 283 | ** triple. |
| 284 | */ |
| @@ -301,10 +303,45 @@ | |
| 303 | |
| 304 | free(aC1); |
| 305 | free(aC2); |
| 306 | return nConflict; |
| 307 | } |
| 308 | |
| 309 | /* |
| 310 | ** Return true if the input string contains a merge marker on a line by |
| 311 | ** itself. |
| 312 | */ |
| 313 | int contains_merge_marker(Blob *p){ |
| 314 | int i, j; |
| 315 | int len = (int)strlen(mergeMarker[0]); |
| 316 | const char *z = blob_buffer(p); |
| 317 | int n = blob_size(p) - len + 1; |
| 318 | assert( len==(int)strlen(mergeMarker[1]) ); |
| 319 | assert( len==(int)strlen(mergeMarker[2]) ); |
| 320 | assert( len==(int)strlen(mergeMarker[3]) ); |
| 321 | assert( sizeof(mergeMarker)/sizeof(mergeMarker[0])==4 ); |
| 322 | for(i=0; i<n; ){ |
| 323 | for(j=0; j<4; j++){ |
| 324 | if( memcmp(&z[i], mergeMarker[j], len)==0 ) return 1; |
| 325 | } |
| 326 | while( i<n && z[i]!='\n' ){ i++; } |
| 327 | while( i<n && z[i]=='\n' ){ i++; } |
| 328 | } |
| 329 | return 0; |
| 330 | } |
| 331 | |
| 332 | /* |
| 333 | ** Return true if the named file contains an unresolved merge marker line. |
| 334 | */ |
| 335 | int file_contains_merge_marker(const char *zFullpath){ |
| 336 | Blob file; |
| 337 | int rc; |
| 338 | blob_read_from_file(&file, zFullpath); |
| 339 | rc = contains_merge_marker(&file); |
| 340 | blob_reset(&file); |
| 341 | return rc; |
| 342 | } |
| 343 | |
| 344 | /* |
| 345 | ** COMMAND: test-3-way-merge |
| 346 | ** |
| 347 | ** Usage: %fossil test-3-way-merge PIVOT V1 V2 MERGED |
| 348 |