@@ -84,7 +84,6 @@ func updateUserAccess(accessMap map[int64]*userAccess, user *user_model.User, mo
8484	}
8585}
8686
87- // FIXME: do cross-comparison so reduce deletions and additions to the minimum? 
8887func  refreshAccesses (ctx  context.Context , repo  * repo_model.Repository , accessMap  map [int64 ]* userAccess ) (err  error ) {
8988	minMode  :=  perm .AccessModeRead 
9089	if  err  :=  repo .LoadOwner (ctx ); err  !=  nil  {
@@ -97,30 +96,76 @@ func refreshAccesses(ctx context.Context, repo *repo_model.Repository, accessMap
9796		minMode  =  perm .AccessModeWrite 
9897	}
9998
100- 	newAccesses  :=  make ([]Access , 0 , len (accessMap ))
99+ 	// Query existing accesses for cross-comparison 
100+ 	existingAccesses , err  :=  db .Find [Access ](ctx , & Access {RepoID : repo .ID })
101+ 	if  err  !=  nil  {
102+ 		return  fmt .Errorf ("find existing accesses: %w" , err )
103+ 	}
104+ 	existingMap  :=  make (map [int64 ]perm.AccessMode , len (existingAccesses ))
105+ 	for  _ , a  :=  range  existingAccesses  {
106+ 		existingMap [a .UserID ] =  a .Mode 
107+ 	}
108+ 
109+ 	var  toDelete  []int64 
110+ 	var  toInsert  []Access 
111+ 	var  toUpdate  []struct  {
112+ 		UserID  int64 
113+ 		Mode    perm.AccessMode 
114+ 	}
115+ 
116+ 	// Determine changes 
101117	for  userID , ua  :=  range  accessMap  {
102118		if  ua .Mode  <  minMode  &&  ! ua .User .IsRestricted  {
103- 			continue 
119+ 			// Should not have access 
120+ 			if  _ , exists  :=  existingMap [userID ]; exists  {
121+ 				toDelete  =  append (toDelete , userID )
122+ 			}
123+ 		} else  {
124+ 			desiredMode  :=  ua .Mode 
125+ 			if  existingMode , exists  :=  existingMap [userID ]; exists  {
126+ 				if  existingMode  !=  desiredMode  {
127+ 					toUpdate  =  append (toUpdate , struct  {
128+ 						UserID  int64 
129+ 						Mode    perm.AccessMode 
130+ 					}{UserID : userID , Mode : desiredMode })
131+ 				}
132+ 			} else  {
133+ 				toInsert  =  append (toInsert , Access {
134+ 					UserID : userID ,
135+ 					RepoID : repo .ID ,
136+ 					Mode :   desiredMode ,
137+ 				})
138+ 			}
104139		}
140+ 		delete (existingMap , userID )
141+ 	}
105142
106- 		newAccesses  =  append (newAccesses , Access {
107- 			UserID : userID ,
108- 			RepoID : repo .ID ,
109- 			Mode :   ua .Mode ,
110- 		})
143+ 	// Remaining in existingMap should be deleted 
144+ 	for  userID  :=  range  existingMap  {
145+ 		toDelete  =  append (toDelete , userID )
111146	}
112147
113- 	// Delete old accesses and insert new ones for repository. 
114- 	if  _ , err  =  db .DeleteByBean (ctx , & Access {RepoID : repo .ID }); err  !=  nil  {
115- 		return  fmt .Errorf ("delete old accesses: %w" , err )
148+ 	// Execute deletions 
149+ 	if  len (toDelete ) >  0  {
150+ 		if  _ , err  =  db .GetEngine (ctx ).In ("user_id" , toDelete ).And ("repo_id = ?" , repo .ID ).Delete (& Access {}); err  !=  nil  {
151+ 			return  fmt .Errorf ("delete accesses: %w" , err )
152+ 		}
116153	}
117- 	if  len (newAccesses ) ==  0  {
118- 		return  nil 
154+ 
155+ 	// Execute updates 
156+ 	for  _ , u  :=  range  toUpdate  {
157+ 		if  _ , err  =  db .GetEngine (ctx ).Where ("user_id = ? AND repo_id = ?" , u .UserID , repo .ID ).Cols ("mode" ).Update (& Access {Mode : u .Mode }); err  !=  nil  {
158+ 			return  fmt .Errorf ("update access for user %d: %w" , u .UserID , err )
159+ 		}
119160	}
120161
121- 	if  err  =  db .Insert (ctx , newAccesses ); err  !=  nil  {
122- 		return  fmt .Errorf ("insert new accesses: %w" , err )
162+ 	// Execute insertions 
163+ 	if  len (toInsert ) >  0  {
164+ 		if  err  =  db .Insert (ctx , toInsert ); err  !=  nil  {
165+ 			return  fmt .Errorf ("insert new accesses: %w" , err )
166+ 		}
123167	}
168+ 
124169	return  nil 
125170}
126171
0 commit comments