@@ -45,7 +45,13 @@ class LocalRepositoryCache(IRepositoryCache):
4545 """Cache for project repos stored on local disk."""
4646
4747 def get (
48- self , cache : ServiceCache , git_url : str , branch : Optional [str ], user : User , shallow : bool = True
48+ self ,
49+ cache : ServiceCache ,
50+ git_url : str ,
51+ branch : Optional [str ],
52+ user : User ,
53+ shallow : bool = True ,
54+ commit_sha : Optional [str ] = None ,
4955 ) -> Project :
5056 """Get a project from cache (clone if necessary)."""
5157 if git_url is None :
@@ -58,12 +64,12 @@ def get(
5864 )
5965 except ValueError :
6066 # project not found in DB
61- return self ._clone_project (cache , git_url , branch , user , shallow )
67+ return self ._clone_project (cache , git_url , branch , user , shallow , commit_sha )
6268
6369 if not project .abs_path .exists ():
6470 # cache folder doesn't exist anymore
6571 project .delete ()
66- return self ._clone_project (cache , git_url , branch , user , shallow )
72+ return self ._clone_project (cache , git_url , branch , user , shallow , commit_sha )
6773
6874 if not shallow and project .is_shallow :
6975 self ._unshallow_project (project , user )
@@ -100,7 +106,13 @@ def _update_project_access_date(self, project: Project):
100106 project .save ()
101107
102108 def _clone_project (
103- self , cache : ServiceCache , git_url : str , branch : Optional [str ], user : User , shallow : bool = True
109+ self ,
110+ cache : ServiceCache ,
111+ git_url : str ,
112+ branch : Optional [str ],
113+ user : User ,
114+ shallow : bool = True ,
115+ commit_sha : Optional [str ] = None ,
104116 ) -> Project :
105117 """Clone a project to cache."""
106118 git_url = normalize_git_url (git_url )
@@ -124,6 +136,7 @@ def _clone_project(
124136 "branch" : branch ,
125137 "git_url" : git_url ,
126138 "user_id" : user .user_id ,
139+ "commit_sha" : commit_sha ,
127140 }
128141 project = cache .make_project (user , project_data , persist = False )
129142
@@ -139,6 +152,7 @@ def _clone_project(
139152 (Project .user_id == user .user_id )
140153 & (Project .git_url == git_url )
141154 & (Project .branch == branch )
155+ & (Project .commit_sha == commit_sha )
142156 & (Project .project_id != project .project_id )
143157 )
144158 except ValueError :
@@ -170,7 +184,7 @@ def _clone_project(
170184 "user.email" : user .email ,
171185 "pull.rebase" : False ,
172186 },
173- checkout_revision = project .branch ,
187+ checkout_revision = commit_sha or project .branch ,
174188 )
175189 ).output
176190 project .save ()
@@ -186,6 +200,9 @@ def _clone_project(
186200
187201 def _unshallow_project (self , project : Project , user : User ):
188202 """Turn a shallow clone into a full clone."""
203+ if project .commit_sha is not None :
204+ # NOTE: A project in a detached head state at a specific commit SHA does not make sense to be unshallowed
205+ return
189206 try :
190207 with project .write_lock (), Repository (project .abs_path ) as repository :
191208 try :
@@ -208,6 +225,10 @@ def _maybe_update_cache(self, project: Project, user: User):
208225 if project .fetch_age < PROJECT_FETCH_TIME :
209226 return
210227
228+ if project .commit_sha is not None :
229+ # NOTE: A project in a detached head state at a specific commit SHA cannot be updated
230+ return
231+
211232 try :
212233 with project .write_lock (), Repository (project .abs_path ) as repository :
213234 try :
0 commit comments