@@ -20,14 +20,16 @@ def __init__(
20
20
name : str ,
21
21
force_rebuild : bool = False ,
22
22
no_suffix_src : bool = False ,
23
+ shallow_clone : bool = True ,
23
24
) -> None :
24
25
self ._url = url
25
26
self ._ref = ref
26
27
self ._directory = directory
27
28
self ._name = name
28
29
self ._force_rebuild = force_rebuild
29
30
self ._no_suffix_src = no_suffix_src
30
- self ._rebuild_needed = self ._git_clone ()
31
+ self ._shallow_clone = shallow_clone
32
+ self ._rebuild_needed = self ._setup_repo ()
31
33
32
34
@property
33
35
def src_dir (self ) -> Path :
@@ -128,42 +130,109 @@ def install(self) -> None:
128
130
"""Installs the project."""
129
131
run (f"cmake --install { self .build_dir } " )
130
132
131
- def _git_clone (self ) -> bool :
133
+ def _can_shallow_clone_ref (self , ref : str ) -> bool :
134
+ """Check if we can do a shallow clone with this ref using git ls-remote."""
135
+ try :
136
+ result = run (f"git ls-remote --heads --tags { self ._url } { ref } " )
137
+ output = result .stdout .decode ().strip ()
138
+
139
+ if output :
140
+ # Found the ref as a branch or tag
141
+ log .debug (
142
+ f"Ref { ref } found as branch/tag via ls-remote, can shallow clone"
143
+ )
144
+ return True
145
+ else :
146
+ # Not found as branch/tag, likely a SHA commit
147
+ log .debug (
148
+ f"Ref { ref } not found as branch/tag via ls-remote, likely SHA commit"
149
+ )
150
+ return False
151
+ except Exception as e :
152
+ log .debug (
153
+ f"Could not check ref { ref } via ls-remote: { e } , assuming SHA commit"
154
+ )
155
+ return False
156
+
157
+ def _git_clone (self ) -> None :
158
+ """Clone the git repository."""
159
+ try :
160
+ log .debug (f"Cloning { self ._url } into { self .src_dir } at commit { self ._ref } " )
161
+ git_clone_cmd = f"git clone --recursive { self ._url } { self .src_dir } "
162
+ if self ._shallow_clone :
163
+ if self ._can_shallow_clone_ref (self ._ref ):
164
+ # Shallow clone for branches and tags only
165
+ git_clone_cmd = f"git clone --recursive --depth 1 --branch { self ._ref } { self ._url } { self .src_dir } "
166
+ else :
167
+ log .debug (f"Cannot shallow clone SHA { self ._ref } , using full clone" )
168
+
169
+ run (git_clone_cmd )
170
+ run (f"git checkout { self ._ref } " , cwd = self .src_dir )
171
+ log .debug (f"Cloned { self ._url } into { self .src_dir } at commit { self ._ref } " )
172
+ except Exception as e :
173
+ log .error (f"Failed to clone repository { self ._url } : { e } " )
174
+ raise
175
+
176
+ def _git_fetch (self ) -> None :
177
+ """Fetch the latest changes from the remote repository."""
178
+ try :
179
+ log .debug (f"Fetching latest changes for { self ._url } in { self .src_dir } " )
180
+ run ("git fetch" , cwd = self .src_dir )
181
+ run ("git reset --hard" , cwd = self .src_dir )
182
+ run (f"git checkout { self ._ref } " , cwd = self .src_dir )
183
+ log .debug (f"Fetched latest changes for { self ._url } in { self .src_dir } " )
184
+ except Exception as e :
185
+ log .error (f"Failed to fetch updates for repository { self ._url } : { e } " )
186
+ raise
187
+
188
+ def _setup_repo (self ) -> bool :
132
189
"""Clone a git repository into a specified directory at a specific commit.
133
190
Returns:
134
191
bool: True if the repository was cloned or updated, False if it was already up-to-date.
135
192
"""
136
- log .debug (f"Cloning { self ._url } into { self .src_dir } at commit { self ._ref } " )
137
- if self .src_dir .exists () and Path (self .src_dir , ".git" ).exists ():
193
+ if not self .src_dir .exists ():
194
+ self ._git_clone ()
195
+ return True
196
+ elif Path (self .src_dir , ".git" ).exists ():
138
197
log .debug (
139
198
f"Repository { self ._url } already exists at { self .src_dir } , checking for updates."
140
199
)
141
- run ("git fetch" , cwd = self .src_dir )
142
- target_commit = (
143
- run (f"git rev-parse { self ._ref } " , cwd = self .src_dir )
200
+ current_commit = (
201
+ run ("git rev-parse HEAD^{commit}" , cwd = self .src_dir )
144
202
.stdout .decode ()
145
203
.strip ()
146
204
)
147
- current_commit = (
148
- run ("git rev-parse HEAD" , cwd = self .src_dir ).stdout .decode ().strip ()
149
- )
150
- if current_commit != target_commit :
151
- log .debug (
152
- f"Current commit { current_commit } does not match target { target_commit } , checking out { self ._ref } ."
205
+ try :
206
+ target_commit = (
207
+ run (f"git rev-parse { self ._ref } ^{{commit}}" , cwd = self .src_dir )
208
+ .stdout .decode ()
209
+ .strip ()
210
+ )
211
+ if current_commit != target_commit :
212
+ log .debug (
213
+ f"Current commit { current_commit } does not match target { target_commit } , checking out { self ._ref } ."
214
+ )
215
+ run ("git reset --hard" , cwd = self .src_dir )
216
+ run (f"git checkout { self ._ref } " , cwd = self .src_dir )
217
+ return True
218
+ except Exception :
219
+ log .error (
220
+ f"Failed to resolve target commit { self ._ref } . Fetching updates."
153
221
)
154
- run ("git reset --hard" , cwd = self .src_dir )
155
- run (f"git checkout { self ._ref } " , cwd = self .src_dir )
222
+ if self ._shallow_clone :
223
+ log .debug (f"Cloning a clean shallow copy." )
224
+ shutil .rmtree (self .src_dir )
225
+ self ._git_clone ()
226
+ return True
227
+ else :
228
+ self ._git_fetch ()
229
+ return True
156
230
else :
157
231
log .debug (
158
232
f"Current commit { current_commit } matches target { target_commit } , no update needed."
159
233
)
160
234
return False
161
- elif not self .src_dir .exists ():
162
- run (f"git clone --recursive { self ._url } { self .src_dir } " )
163
- run (f"git checkout { self ._ref } " , cwd = self .src_dir )
164
235
else :
165
236
raise Exception (
166
237
f"The directory { self .src_dir } exists but is not a git repository."
167
238
)
168
- log .debug (f"Cloned { self ._url } into { self .src_dir } at commit { self ._ref } " )
169
- return True
0 commit comments