Index: f-apps/f-merge.c ================================================================== --- f-apps/f-merge.c +++ f-apps/f-merge.c @@ -54,11 +54,10 @@ case FSL_MERGE_FCHANGE_MERGED: rc="m"; break; case FSL_MERGE_FCHANGE_CONFLICT_MERGED: rc="!m"; break; case FSL_MERGE_FCHANGE_CONFLICT_ADDED_UNMANAGED: rc="!+"; break; case FSL_MERGE_FCHANGE_CONFLICT_SYMLINK: rc="!s"; break; case FSL_MERGE_FCHANGE_CONFLICT_BINARY: rc="!b"; break; - case FSL_MERGE_FCHANGE_CONFLICT_ANCESTOR: rc="!a"; break; case FSL_MERGE_FCHANGE_RENAMED: rc="r"; break; case FSL_MERGE_FCHANGE_count: case FSL_MERGE_FCHANGE_NONE: rc="!!!"; fsl__fatal(FSL_RC_NYI,"Cannot happen: FSL_MERGE_FCHANGE_NONE"); break; @@ -216,13 +215,10 @@ "Merged with conflicts"}, {FSL_MERGE_FCHANGE_CONFLICT_SYMLINK, 0, "Symlink cannot be merged"}, {FSL_MERGE_FCHANGE_CONFLICT_BINARY, 0, "Binary content cannot be merged"}, - {FSL_MERGE_FCHANGE_CONFLICT_ANCESTOR, 0, - "No common ancestor found. " - "Normally means a new file was merged in"}, {FSL_MERGE_FCHANGE_NONE,NULL,NULL} }; f_out("\nLEGEND:\n"); for(struct legend * l = &leg[0]; l->desc; ++l){ if(App.mCounts[l->fct]){ Index: include/fossil-scm/checkout.h ================================================================== --- include/fossil-scm/checkout.h +++ include/fossil-scm/checkout.h @@ -2591,26 +2591,10 @@ fossil(1) simply skips over, with a warning, binaries during a merge, so we do the same (for lack of a better option). */ FSL_MERGE_FCHANGE_CONFLICT_BINARY, /** - Indicates that the given file cannot be merged because no common - ancestor can be found for it. This condition is not strictly fatal - but does indicate a problem with the input data. Merging will - continue unless the fsl_merge_f() to which this is reported returns - non-0 to cancel it. This particular status is reported very early - in the fsl_ckout_merge() process, before any files have been - written by the merge, thus the callback can effectively abort the - merge without side-effects by returning non-0 if this status is - reported to it. If the fsl_ckout_merge() call has no callback set, - this case will go silently undiagnosed! - - Potential TODO: add a flag to fsl_merge_opt to tell it to treat any - of these cases as merge-fatal. -*/ -FSL_MERGE_FCHANGE_CONFLICT_ANCESTOR, -/** File was renamed in the updated-to version. If a file is both modified and renamed, it will be reported twice: once for each type of change. The new name is reported via fsl_merge_state::filename and the previous name via fsl_merge_state::priorName. */ Index: src/merge.c ================================================================== --- src/merge.c +++ src/merge.c @@ -63,11 +63,11 @@ " SELECT %" FSL_ID_T_PFMT ", mtime, 1, 1 " " FROM event WHERE objid=%" FSL_ID_T_PFMT " AND type='ci' LIMIT 1", rid, rid ); } - return rc; + return rc; } /** Set a secondary file of a merge. The primary file must be set first. There must be at least one secondary but there can be more @@ -812,43 +812,12 @@ #define MCB2(FCT,FN) MCB(FCT,FSL_CKUP_RM_NOT,FN) /************************************************************************ ** All of the information needed to do the merge is now contained in the ** FV table. Starting here, we begin to actually carry out the merge. ** - ** First, find files in M and V but not in P and report conflicts. - ** The file in M will be ignored. It will be treated as if it - ** does not exist. - */ - rc = fsl_cx_prepare(f, &q, "SELECT idm FROM " FSL__TABLE_FVM - " WHERE idp=0 AND idv>0 AND idm>0"); - if(rc) goto end; - while( 0==rc && FSL_RC_STEP_ROW==(rc = fsl_stmt_step(&q)) ){ - fsl_id_t const idm = fsl_stmt_g_id(&q, 0); - rc = fsl_cx_exec(f, "UPDATE " FSL__TABLE_FVM - " SET idm=0 WHERE idm=%" FSL_ID_T_PFMT, idm); - if(0==rc && opt->callback){ - fsl_buffer * const bName = fsl__cx_scratchpad(f); - rc = fsl_db_get_buffer(db, bName, false, - "SELECT pathname FROM vfile WHERE id=%" FSL_ID_T_PFMT, - idm); - if(0==rc){ - MCB2(FSL_MERGE_FCHANGE_CONFLICT_ANCESTOR, - fsl_buffer_cstr(bName)); - } - fsl__cx_scratchpad_yield(f, bName); - } - } - fsl_stmt_finalize(&q); - switch(rc){ - case 0: - case FSL_RC_STEP_DONE: rc = 0; break; - default: goto end; - } - - /* - ** Find files that have changed from P->M but not P->V. - ** Copy the M content over into V. + ** First, find files that have changed from P->M but not P->V. Copy + ** the M content over into V. */ rc = fsl_cx_prepare(f, &q, "SELECT idv, ridm, fn, islinkm FROM " FSL__TABLE_FVM " WHERE idp>0 AND idv>0 AND idm>0" " AND ridm!=ridp AND ridv=ridp AND NOT chnged" @@ -890,16 +859,28 @@ default: goto end; } /* ** Do a three-way merge on files that have changes on both P->M and P->V. + ** + ** Proceed even if the file doesn't exist on P, as if the common + ** ancestor of M and V is an empty file. In this case, merge + ** conflict marks will be added to the file and the user will be + ** forced to handle them like any other conflict. The alternative is + ** that we warn the user via opt->callback and let them decide on + ** whether to keep the copy from P->V (historical default behavior) + ** or error out. As of 2023-04, fossil treats this as a normal + ** merge conflict with an empty common ancestor version, so we'll do + ** the same. */ rc = fsl_cx_prepare(f, &q, "SELECT ridm, idv, ridp, ridv," " FSL_GLOB('binary-glob'," FSL__TABLE_FVM ".fn)," " fn, isexe, islinkv, islinkm FROM " FSL__TABLE_FVM - " WHERE idp>0 AND idv>0 AND idm>0" + " WHERE /*idp>0 AND*/ idv>0 AND idm>0" + /* -----^^^^^^^^^^^^^ https://fossil-scm.org/home/info/7c75e47b3c130ff1 + With that clause, entries with no common ancestor are filtered out. */ " AND ridm!=ridp AND (ridv!=ridp OR chnged)" ); if(0==rc){ rc = fsl_buffer_append( absPath, f->ckout.dir, (fsl_int_t)f->ckout.dirLen); } @@ -944,11 +925,11 @@ } absPath->used = f->ckout.dirLen; rc = fsl_buffer_append(absPath, zName, (fsl_int_t)nName); if(rc) break; zFullPath = fsl_buffer_cstr(absPath); - rc = fsl_content_get(f, ridp, &p); + if(ridp) rc = fsl_content_get(f, ridp, &p); if(0==rc) rc = fsl_content_get(f, ridm, &m); if(0==rc){ //unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0; //if(keepMergeFlag!=0) mergeFlags |= MERGE_KEEP_FILES; //rc = merge_3way(&p, zFullPath, &m, &r, mergeFlags);