@@ -3429,6 +3429,162 @@ static void doltliteCherryPickFunc(
34293429 }
34303430}
34313431
3432+ static int doltliteTableEntryDiffers (
3433+ const struct TableEntry * a , const struct TableEntry * b
3434+ ){
3435+ if ( !a && !b ) return 0 ;
3436+ if ( !a || !b ) return 1 ;
3437+ if ( prollyHashCompare (& a -> root , & b -> root )!= 0 ) return 1 ;
3438+ if ( prollyHashCompare (& a -> schemaHash , & b -> schemaHash )!= 0 ) return 1 ;
3439+ return 0 ;
3440+ }
3441+
3442+ static int doltliteAppendTableName (
3443+ char * * * pazNames , int * pn , int * pnAlloc , const char * zName
3444+ ){
3445+ int i ;
3446+ if ( !zName ) return SQLITE_OK ;
3447+ for (i = 0 ; i < * pn ; i ++ ){
3448+ if ( strcmp ((* pazNames )[i ], zName )== 0 ) return SQLITE_OK ;
3449+ }
3450+ if ( * pn == * pnAlloc ){
3451+ int n = * pnAlloc ? * pnAlloc * 2 : 8 ;
3452+ char * * a = sqlite3_realloc (* pazNames , n * sizeof (char * ));
3453+ if ( !a ) return SQLITE_NOMEM ;
3454+ * pazNames = a ;
3455+ * pnAlloc = n ;
3456+ }
3457+ (* pazNames )[* pn ] = sqlite3_mprintf ("%s" , zName );
3458+ if ( !(* pazNames )[* pn ] ) return SQLITE_NOMEM ;
3459+ (* pn )++ ;
3460+ return SQLITE_OK ;
3461+ }
3462+
3463+ static void doltliteFreeNameList (char * * az , int n ){
3464+ int i ;
3465+ if ( !az ) return ;
3466+ for (i = 0 ; i < n ; i ++ ) sqlite3_free (az [i ]);
3467+ sqlite3_free (az );
3468+ }
3469+
3470+ static int doltliteCollectChangedNames (
3471+ struct TableEntry * aFrom , int nFrom ,
3472+ struct TableEntry * aTo , int nTo ,
3473+ char * * * pazNames , int * pn , int * pnAlloc
3474+ ){
3475+ int i , rc ;
3476+ for (i = 0 ; i < nFrom ; i ++ ){
3477+ struct TableEntry * p = doltliteFindTableByName (aTo , nTo , aFrom [i ].zName );
3478+ if ( doltliteTableEntryDiffers (& aFrom [i ], p ) ){
3479+ rc = doltliteAppendTableName (pazNames , pn , pnAlloc , aFrom [i ].zName );
3480+ if ( rc != SQLITE_OK ) return rc ;
3481+ }
3482+ }
3483+ for (i = 0 ; i < nTo ; i ++ ){
3484+ struct TableEntry * p = doltliteFindTableByName (aFrom , nFrom , aTo [i ].zName );
3485+ if ( !p ){
3486+ rc = doltliteAppendTableName (pazNames , pn , pnAlloc , aTo [i ].zName );
3487+ if ( rc != SQLITE_OK ) return rc ;
3488+ }
3489+ }
3490+ return SQLITE_OK ;
3491+ }
3492+
3493+ static int doltliteNameInList (char * * az , int n , const char * zName ){
3494+ int i ;
3495+ if ( !zName ) return 0 ;
3496+ for (i = 0 ; i < n ; i ++ ){
3497+ if ( strcmp (az [i ], zName )== 0 ) return 1 ;
3498+ }
3499+ return 0 ;
3500+ }
3501+
3502+ // Compares the catalog of |pCommit| against |pParent|, appending names of
3503+ // tables that differ to *pazTouched. These are the tables the revert of pCommit
3504+ // would modify.
3505+ static int doltliteCollectRevertTouchedTables (
3506+ sqlite3 * db ,
3507+ const ProllyHash * pCommitCat ,
3508+ const ProllyHash * pParentCat ,
3509+ char * * * pazTouched , int * pnTouched , int * pnAlloc
3510+ ){
3511+ struct TableEntry * aCommit = 0 , * aParent = 0 ;
3512+ int nCommit = 0 , nParent = 0 ;
3513+ int rc ;
3514+ rc = doltliteLoadCatalog (db , pCommitCat , & aCommit , & nCommit , 0 );
3515+ if ( rc != SQLITE_OK ) return rc ;
3516+ rc = doltliteLoadCatalog (db , pParentCat , & aParent , & nParent , 0 );
3517+ if ( rc == SQLITE_OK ){
3518+ rc = doltliteCollectChangedNames (aCommit , nCommit , aParent , nParent ,
3519+ pazTouched , pnTouched , pnAlloc );
3520+ }
3521+ doltliteFreeCatalog (aCommit , nCommit );
3522+ doltliteFreeCatalog (aParent , nParent );
3523+ return rc ;
3524+ }
3525+
3526+ // Sets *pConflict=1 if the working set has an uncommitted change to a table
3527+ // the revert would modify. Changes to unrelated tables are allowed and become
3528+ // part of the revert commit, matching Dolt.
3529+ static int doltliteRevertHasDirtyTouchedTables (
3530+ sqlite3 * db ,
3531+ char * * azTouched , int nTouched ,
3532+ int * pConflict
3533+ ){
3534+ ChunkStore * cs = doltliteGetChunkStore (db );
3535+ ProllyHash headCatHash , workingCatHash ;
3536+ u8 * wBuf = 0 ; int nWBuf = 0 ;
3537+ struct TableEntry * aHead = 0 , * aWorking = 0 ;
3538+ int nHead = 0 , nWorking = 0 ;
3539+ int i , rc ;
3540+
3541+ * pConflict = 0 ;
3542+
3543+ if ( !cs ) return SQLITE_OK ;
3544+ if ( !doltliteHasUncommittedChanges (db ) ) return SQLITE_OK ;
3545+
3546+ rc = doltliteGetHeadCatalogHash (db , & headCatHash );
3547+ if ( rc != SQLITE_OK ) return rc ;
3548+
3549+ rc = doltliteFlushAndSerializeCatalog (db , & wBuf , & nWBuf );
3550+ if ( rc != SQLITE_OK ) return rc ;
3551+ rc = chunkStorePut (cs , wBuf , nWBuf , & workingCatHash );
3552+ sqlite3_free (wBuf );
3553+ if ( rc != SQLITE_OK ) return rc ;
3554+
3555+ rc = doltliteLoadCatalog (db , & workingCatHash , & aWorking , & nWorking , 0 );
3556+ if ( rc != SQLITE_OK ) return rc ;
3557+
3558+ if ( !prollyHashIsEmpty (& headCatHash ) ){
3559+ rc = doltliteLoadCatalog (db , & headCatHash , & aHead , & nHead , 0 );
3560+ if ( rc != SQLITE_OK ) goto cleanup ;
3561+ }
3562+
3563+ for (i = 0 ; i < nWorking ; i ++ ){
3564+ struct TableEntry * pH = doltliteFindTableByName (aHead , nHead ,
3565+ aWorking [i ].zName );
3566+ if ( !doltliteTableEntryDiffers (pH , & aWorking [i ]) ) continue ;
3567+
3568+ if ( doltliteNameInList (azTouched , nTouched , aWorking [i ].zName ) ){
3569+ * pConflict = 1 ;
3570+ goto cleanup ;
3571+ }
3572+ }
3573+
3574+ for (i = 0 ; i < nHead ; i ++ ){
3575+ if ( doltliteFindTableByName (aWorking , nWorking , aHead [i ].zName ) ) continue ;
3576+ if ( doltliteNameInList (azTouched , nTouched , aHead [i ].zName ) ){
3577+ * pConflict = 1 ;
3578+ goto cleanup ;
3579+ }
3580+ }
3581+
3582+ cleanup :
3583+ doltliteFreeCatalog (aHead , nHead );
3584+ doltliteFreeCatalog (aWorking , nWorking );
3585+ return rc ;
3586+ }
3587+
34323588static void doltliteRevertFunc (
34333589 sqlite3_context * context ,
34343590 int argc ,
@@ -3438,10 +3594,15 @@ static void doltliteRevertFunc(
34383594 ChunkStore * cs = doltliteGetChunkStore (db );
34393595 const char * zRef ;
34403596 ProllyHash revertHash , ourHead ;
3597+ ProllyHash liveOurCatalog ;
34413598 DoltliteCommit revertCommit , parentCommit , ourCommit ;
34423599 int nConflicts = 0 ;
34433600 int rc ;
34443601 char hexBuf [PROLLY_HASH_SIZE * 2 + 1 ];
3602+ char * * azTouched = 0 ;
3603+ int nTouched = 0 ;
3604+ int nTouchedAlloc = 0 ;
3605+ int conflictWithDirty = 0 ;
34453606
34463607 memset (& revertCommit , 0 , sizeof (revertCommit ));
34473608 memset (& parentCommit , 0 , sizeof (parentCommit ));
@@ -3471,12 +3632,6 @@ static void doltliteRevertFunc(
34713632 return ;
34723633 }
34733634
3474- if ( doltliteHasUncommittedChanges (db ) ){
3475- sqlite3_result_error (context ,
3476- "cannot revert with uncommitted changes" , -1 );
3477- return ;
3478- }
3479-
34803635 rc = doltliteResolveRef (db ,zRef , & revertHash );
34813636 if ( rc != SQLITE_OK ){
34823637 sqlite3_result_error (context , "invalid commit hash" , -1 );
@@ -3510,19 +3665,41 @@ static void doltliteRevertFunc(
35103665 return ;
35113666 }
35123667
3668+ rc = doltliteCollectRevertTouchedTables (db , & revertCommit .catalogHash ,
3669+ & parentCommit .catalogHash , & azTouched , & nTouched , & nTouchedAlloc );
3670+ if ( rc != SQLITE_OK ) goto revert_error ;
3671+
3672+ rc = doltliteRevertHasDirtyTouchedTables (db , azTouched , nTouched ,
3673+ & conflictWithDirty );
3674+ if ( rc != SQLITE_OK ) goto revert_error ;
3675+ if ( conflictWithDirty ){
3676+ doltliteCommitClear (& revertCommit );
3677+ doltliteCommitClear (& parentCommit );
3678+ doltliteCommitClear (& ourCommit );
3679+ doltliteFreeNameList (azTouched , nTouched );
3680+ sqlite3_result_error (context ,
3681+ "cannot revert with uncommitted changes" , -1 );
3682+ return ;
3683+ }
3684+ rc = doltliteFlushCatalogToHash (db , & liveOurCatalog );
3685+ if ( rc != SQLITE_OK ) goto revert_error ;
3686+
35133687 {
35143688 char msg [512 ];
35153689 sqlite3_snprintf (sizeof (msg ), msg , "Revert \"%s\"" ,
35163690 revertCommit .zMessage ? revertCommit .zMessage : zRef );
35173691
35183692 rc = applyMergedCatalogAndCommit (db , context ,
3519- & revertCommit .catalogHash , & ourCommit . catalogHash ,
3693+ & revertCommit .catalogHash , & liveOurCatalog ,
35203694 & parentCommit .catalogHash , & ourHead , msg , & nConflicts , hexBuf );
35213695 }
35223696
35233697 doltliteCommitClear (& revertCommit );
35243698 doltliteCommitClear (& parentCommit );
35253699 doltliteCommitClear (& ourCommit );
3700+ doltliteFreeNameList (azTouched , nTouched );
3701+ azTouched = 0 ;
3702+ nTouched = 0 ;
35263703
35273704 if ( rc == SQLITE_BUSY ){
35283705 sqlite3_result_error (context ,
@@ -3540,6 +3717,14 @@ static void doltliteRevertFunc(
35403717 }else if ( hexBuf [0 ] ){
35413718 sqlite3_result_text (context , hexBuf , -1 , SQLITE_TRANSIENT );
35423719 }
3720+ return ;
3721+
3722+ revert_error :
3723+ doltliteCommitClear (& revertCommit );
3724+ doltliteCommitClear (& parentCommit );
3725+ doltliteCommitClear (& ourCommit );
3726+ doltliteFreeNameList (azTouched , nTouched );
3727+ sqlite3_result_error (context , "revert failed" , -1 );
35433728}
35443729
35453730static int doltliteRebaseCollectReplaySet (
0 commit comments