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