-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(cargo-update): --precise
accept arbitrary git revisions
#13250
Changes from all commits
4be3614
2734097
ce551d7
093f7c8
e6f2dfd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -68,11 +68,10 @@ use url::Url; | |
pub struct GitSource<'cfg> { | ||
/// The git remote which we're going to fetch from. | ||
remote: GitRemote, | ||
/// The Git reference from the manifest file. | ||
manifest_reference: GitReference, | ||
/// The revision which a git source is locked to. | ||
/// This is expected to be set after the Git repository is fetched. | ||
locked_rev: Option<git2::Oid>, | ||
/// | ||
/// Expected to always be [`Revision::Locked`] after the Git repository is fetched. | ||
locked_rev: Revision, | ||
/// The unique identifier of this source. | ||
source_id: SourceId, | ||
/// The underlying path source to discover packages inside the Git repository. | ||
|
@@ -102,8 +101,12 @@ impl<'cfg> GitSource<'cfg> { | |
assert!(source_id.is_git(), "id is not git, id={}", source_id); | ||
|
||
let remote = GitRemote::new(source_id.url()); | ||
let manifest_reference = source_id.git_reference().unwrap().clone(); | ||
let locked_rev = source_id.precise_git_oid()?; | ||
// Fallback to git ref from mainfest if there is no locked revision. | ||
let locked_rev = source_id | ||
.precise_git_fragment() | ||
.map(|s| Revision::new(s.into())) | ||
.unwrap_or_else(|| source_id.git_reference().unwrap().clone().into()); | ||
|
||
let ident = ident_shallow( | ||
&source_id, | ||
config | ||
|
@@ -114,7 +117,6 @@ impl<'cfg> GitSource<'cfg> { | |
|
||
let source = GitSource { | ||
remote, | ||
manifest_reference, | ||
locked_rev, | ||
source_id, | ||
path_source: None, | ||
|
@@ -155,6 +157,48 @@ impl<'cfg> GitSource<'cfg> { | |
} | ||
} | ||
|
||
/// Indicates a [Git revision] that might be locked or deferred to be resolved. | ||
/// | ||
/// [Git revision]: https://git-scm.com/docs/revisions | ||
#[derive(Clone, Debug)] | ||
enum Revision { | ||
/// A [Git reference] that would trigger extra fetches when being resolved. | ||
/// | ||
/// [Git reference]: https://git-scm.com/book/en/v2/Git-Internals-Git-References | ||
Deferred(GitReference), | ||
/// A locked revision of the actual Git commit object ID. | ||
Locked(git2::Oid), | ||
} | ||
|
||
impl Revision { | ||
fn new(rev: &str) -> Revision { | ||
let oid = git2::Oid::from_str(rev).ok(); | ||
match oid { | ||
// Git object ID is supposed to be a hex string of 20 (SHA1) or 32 (SHA256) bytes. | ||
// Its length must be double to the underlying bytes (40 or 64), | ||
// otherwise libgit2 would happily zero-pad the returned oid. | ||
// See rust-lang/cargo#13188 | ||
Some(oid) if oid.as_bytes().len() * 2 == rev.len() => Revision::Locked(oid), | ||
_ => Revision::Deferred(GitReference::Rev(rev.to_string())), | ||
Comment on lines
+174
to
+182
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you add a test for when a tag looks like an Oid? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added in e6f2dfd |
||
} | ||
} | ||
} | ||
|
||
impl From<GitReference> for Revision { | ||
fn from(value: GitReference) -> Self { | ||
Revision::Deferred(value) | ||
} | ||
} | ||
|
||
impl From<Revision> for GitReference { | ||
fn from(value: Revision) -> Self { | ||
match value { | ||
Revision::Deferred(git_ref) => git_ref, | ||
Revision::Locked(oid) => GitReference::Rev(oid.to_string()), | ||
} | ||
} | ||
} | ||
|
||
/// Create an identifier from a URL, | ||
/// essentially turning `proto://host/path/repo` into `repo-<hash-of-url>`. | ||
fn ident(id: &SourceId) -> String { | ||
|
@@ -191,9 +235,12 @@ impl<'cfg> Debug for GitSource<'cfg> { | |
// TODO(-Znext-lockfile-bump): set it to true when stabilizing | ||
// lockfile v4, because we want Source ID serialization to be | ||
// consistent with lockfile. | ||
match self.manifest_reference.pretty_ref(false) { | ||
Some(s) => write!(f, " ({})", s), | ||
None => Ok(()), | ||
match &self.locked_rev { | ||
Revision::Deferred(git_ref) => match git_ref.pretty_ref(false) { | ||
Some(s) => write!(f, " ({})", s), | ||
None => Ok(()), | ||
}, | ||
Revision::Locked(oid) => write!(f, " ({oid})"), | ||
} | ||
} | ||
} | ||
|
@@ -252,16 +299,17 @@ impl<'cfg> Source for GitSource<'cfg> { | |
let db_path = db_path.into_path_unlocked(); | ||
|
||
let db = self.remote.db_at(&db_path).ok(); | ||
let (db, actual_rev) = match (self.locked_rev, db) { | ||
|
||
let (db, actual_rev) = match (&self.locked_rev, db) { | ||
// If we have a locked revision, and we have a preexisting database | ||
// which has that revision, then no update needs to happen. | ||
(Some(rev), Some(db)) if db.contains(rev) => (db, rev), | ||
(Revision::Locked(oid), Some(db)) if db.contains(*oid) => (db, *oid), | ||
|
||
// If we're in offline mode, we're not locked, and we have a | ||
// database, then try to resolve our reference with the preexisting | ||
// repository. | ||
(None, Some(db)) if self.config.offline() => { | ||
let rev = db.resolve(&self.manifest_reference).with_context(|| { | ||
(Revision::Deferred(git_ref), Some(db)) if self.config.offline() => { | ||
let rev = db.resolve(&git_ref).with_context(|| { | ||
"failed to lookup reference in preexisting repository, and \ | ||
can't check for updates in offline mode (--offline)" | ||
})?; | ||
|
@@ -279,6 +327,7 @@ impl<'cfg> Source for GitSource<'cfg> { | |
self.remote.url() | ||
); | ||
} | ||
|
||
if !self.quiet { | ||
self.config.shell().status( | ||
"Updating", | ||
|
@@ -288,13 +337,9 @@ impl<'cfg> Source for GitSource<'cfg> { | |
|
||
trace!("updating git source `{:?}`", self.remote); | ||
|
||
self.remote.checkout( | ||
&db_path, | ||
db, | ||
&self.manifest_reference, | ||
locked_rev, | ||
self.config, | ||
)? | ||
let locked_rev = locked_rev.clone().into(); | ||
self.remote | ||
.checkout(&db_path, db, &locked_rev, self.config)? | ||
} | ||
}; | ||
|
||
|
@@ -321,7 +366,7 @@ impl<'cfg> Source for GitSource<'cfg> { | |
|
||
self.path_source = Some(path_source); | ||
self.short_id = Some(short_id.as_str().into()); | ||
self.locked_rev = Some(actual_rev); | ||
self.locked_rev = Revision::Locked(actual_rev); | ||
self.path_source.as_mut().unwrap().update()?; | ||
|
||
// Hopefully this shouldn't incur too much of a performance hit since | ||
|
@@ -350,7 +395,10 @@ impl<'cfg> Source for GitSource<'cfg> { | |
} | ||
|
||
fn fingerprint(&self, _pkg: &Package) -> CargoResult<String> { | ||
Ok(self.locked_rev.as_ref().unwrap().to_string()) | ||
match &self.locked_rev { | ||
Revision::Locked(oid) => Ok(oid.to_string()), | ||
_ => unreachable!("locked_rev must be resolved when computing fingerprint"), | ||
} | ||
} | ||
|
||
fn describe(&self) -> String { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -757,13 +757,11 @@ fn update_with_shared_deps() { | |
.with_status(101) | ||
.with_stderr( | ||
"\ | ||
[UPDATING] git repository [..] | ||
[ERROR] Unable to update [..] | ||
|
||
Caused by: | ||
precise value for git is not a git revision: 0.1.2 | ||
|
||
Caused by: | ||
unable to parse OID - contains invalid characters; class=Invalid (3) | ||
revspec '0.1.2' not found; class=Reference (4); code=NotFound (-3) | ||
Comment on lines
+760
to
+764
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The documentation only says SHA and doesn't specify if shorts are allowed. I almost wonder if the reference to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It worked in 1.45.0 before #8363 merged and was documented since then. The doc mentions git revisions, which could be like anything. This could be considered as an old regression that nobody reported until recent. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. BTW the test was added to prevent panic when revision is missing: 1ad6f78. |
||
", | ||
) | ||
.run(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a side comment, to be pedantically correct this should probably be using
Object::short_id
to avoid collisions, but that seems pretty unlikely.