@@ -71,8 +71,9 @@ pub struct GitSource<'cfg> {
71
71
/// The Git reference from the manifest file.
72
72
manifest_reference : GitReference ,
73
73
/// The revision which a git source is locked to.
74
- /// This is expected to be set after the Git repository is fetched.
75
- locked_rev : Option < git2:: Oid > ,
74
+ ///
75
+ /// Expected to always be [`Revision::Locked`] after the Git repository is fetched.
76
+ locked_rev : Revision ,
76
77
/// The unique identifier of this source.
77
78
source_id : SourceId ,
78
79
/// The underlying path source to discover packages inside the Git repository.
@@ -103,7 +104,11 @@ impl<'cfg> GitSource<'cfg> {
103
104
104
105
let remote = GitRemote :: new ( source_id. url ( ) ) ;
105
106
let manifest_reference = source_id. git_reference ( ) . unwrap ( ) . clone ( ) ;
106
- let locked_rev = source_id. precise_git_oid ( ) ?;
107
+ let locked_rev = source_id
108
+ . precise_full_git_fragment ( )
109
+ . map ( |s| Revision :: new ( s. into ( ) ) )
110
+ . unwrap_or_else ( || source_id. git_reference ( ) . unwrap ( ) . clone ( ) . into ( ) ) ;
111
+
107
112
let ident = ident_shallow (
108
113
& source_id,
109
114
config
@@ -155,6 +160,49 @@ impl<'cfg> GitSource<'cfg> {
155
160
}
156
161
}
157
162
163
+ /// Indicates a [Git revision] that might be locked or deferred to be resolved.
164
+ ///
165
+ /// [Git revision]: https://git-scm.com/docs/revisions
166
+ #[ derive( Clone , Debug ) ]
167
+ enum Revision {
168
+ /// A [Git reference] that would trigger extra fetches when being resolved.
169
+ ///
170
+ /// [Git reference]: https://git-scm.com/book/en/v2/Git-Internals-Git-References
171
+ Deferred ( GitReference ) ,
172
+ /// A locked revision of the actual Git commit object ID.
173
+ Locked ( git2:: Oid ) ,
174
+ }
175
+
176
+ impl Revision {
177
+ fn new ( rev : & str ) -> Revision {
178
+ let oid = git2:: Oid :: from_str ( rev) . ok ( ) ;
179
+ match oid {
180
+ // Git object ID is supposed to be a hex string of 20 (SHA1) or 32 (SHA256) bytes.
181
+ // Its length must be double to the underlying bytes (40 or 64),
182
+ // otherwise libgit2 would happily zero-pad the returned oid.
183
+ // See rust-lang/cargo#13188
184
+ Some ( oid) if oid. as_bytes ( ) . len ( ) * 2 == rev. len ( ) => Revision :: Locked ( oid) ,
185
+ _ => Revision :: Deferred ( GitReference :: Rev ( rev. to_string ( ) ) ) ,
186
+ }
187
+ }
188
+ }
189
+
190
+ impl From < GitReference > for Revision {
191
+ fn from ( value : GitReference ) -> Self {
192
+ Revision :: Deferred ( value)
193
+ }
194
+ }
195
+
196
+ impl From < Revision > for GitReference {
197
+ fn from ( value : Revision ) -> Self {
198
+ match value {
199
+ Revision :: Deferred ( git_ref) => git_ref,
200
+ Revision :: Locked ( oid) => GitReference :: Rev ( oid. to_string ( ) ) ,
201
+ }
202
+ }
203
+ }
204
+
205
+
158
206
/// Create an identifier from a URL,
159
207
/// essentially turning `proto://host/path/repo` into `repo-<hash-of-url>`.
160
208
fn ident ( id : & SourceId ) -> String {
@@ -252,16 +300,17 @@ impl<'cfg> Source for GitSource<'cfg> {
252
300
let db_path = db_path. into_path_unlocked ( ) ;
253
301
254
302
let db = self . remote . db_at ( & db_path) . ok ( ) ;
255
- let ( db, actual_rev) = match ( self . locked_rev , db) {
303
+
304
+ let ( db, actual_rev) = match ( & self . locked_rev , db) {
256
305
// If we have a locked revision, and we have a preexisting database
257
306
// which has that revision, then no update needs to happen.
258
- ( Some ( rev ) , Some ( db) ) if db. contains ( rev ) => ( db, rev ) ,
307
+ ( Revision :: Locked ( oid ) , Some ( db) ) if db. contains ( * oid ) => ( db, * oid ) ,
259
308
260
309
// If we're in offline mode, we're not locked, and we have a
261
310
// database, then try to resolve our reference with the preexisting
262
311
// repository.
263
- ( None , Some ( db) ) if self . config . offline ( ) => {
264
- let rev = db. resolve ( & self . manifest_reference ) . with_context ( || {
312
+ ( Revision :: Deferred ( git_ref ) , Some ( db) ) if self . config . offline ( ) => {
313
+ let rev = db. resolve ( & git_ref ) . with_context ( || {
265
314
"failed to lookup reference in preexisting repository, and \
266
315
can't check for updates in offline mode (--offline)"
267
316
} ) ?;
@@ -279,6 +328,7 @@ impl<'cfg> Source for GitSource<'cfg> {
279
328
self . remote. url( )
280
329
) ;
281
330
}
331
+
282
332
if !self . quiet {
283
333
self . config . shell ( ) . status (
284
334
"Updating" ,
@@ -288,13 +338,9 @@ impl<'cfg> Source for GitSource<'cfg> {
288
338
289
339
trace ! ( "updating git source `{:?}`" , self . remote) ;
290
340
291
- self . remote . checkout (
292
- & db_path,
293
- db,
294
- & self . manifest_reference ,
295
- locked_rev,
296
- self . config ,
297
- ) ?
341
+ let locked_rev = locked_rev. clone ( ) . into ( ) ;
342
+ self . remote
343
+ . checkout ( & db_path, db, & locked_rev, self . config ) ?
298
344
}
299
345
} ;
300
346
@@ -321,7 +367,7 @@ impl<'cfg> Source for GitSource<'cfg> {
321
367
322
368
self . path_source = Some ( path_source) ;
323
369
self . short_id = Some ( short_id. as_str ( ) . into ( ) ) ;
324
- self . locked_rev = Some ( actual_rev) ;
370
+ self . locked_rev = Revision :: Locked ( actual_rev) ;
325
371
self . path_source . as_mut ( ) . unwrap ( ) . update ( ) ?;
326
372
327
373
// Hopefully this shouldn't incur too much of a performance hit since
@@ -350,7 +396,10 @@ impl<'cfg> Source for GitSource<'cfg> {
350
396
}
351
397
352
398
fn fingerprint ( & self , _pkg : & Package ) -> CargoResult < String > {
353
- Ok ( self . locked_rev . as_ref ( ) . unwrap ( ) . to_string ( ) )
399
+ match & self . locked_rev {
400
+ Revision :: Locked ( oid) => Ok ( oid. to_string ( ) ) ,
401
+ _ => unreachable ! ( "locked_rev must be resolved when computing fingerprint" ) ,
402
+ }
354
403
}
355
404
356
405
fn describe ( & self ) -> String {
0 commit comments