1
1
use camino:: Utf8PathBuf ;
2
+ use git2:: { Error as Git2Error , FetchOptions , Oid , Repository } ;
2
3
use serde:: Deserialize ;
4
+ use std:: fs;
3
5
4
6
use crate :: Resolver ;
5
7
6
- #[ derive( Deserialize ) ]
8
+ #[ derive( Deserialize , Hash , Debug , Clone ) ]
7
9
pub struct GitDesc {
8
10
remote : String ,
9
11
refspec : String ,
@@ -14,7 +16,34 @@ pub struct GitResolver {
14
16
pub default_clone_path : Utf8PathBuf ,
15
17
}
16
18
17
- pub struct GitResolutionError ;
19
+ #[ derive( Debug ) ]
20
+ pub enum GitResolutionError {
21
+ GitError ( Git2Error ) ,
22
+ IoError ( std:: io:: Error ) ,
23
+ InvalidOid ,
24
+ }
25
+
26
+ impl From < Git2Error > for GitResolutionError {
27
+ fn from ( error : Git2Error ) -> Self {
28
+ GitResolutionError :: GitError ( error)
29
+ }
30
+ }
31
+
32
+ impl From < std:: io:: Error > for GitResolutionError {
33
+ fn from ( error : std:: io:: Error ) -> Self {
34
+ GitResolutionError :: IoError ( error)
35
+ }
36
+ }
37
+
38
+ impl std:: fmt:: Display for GitResolutionError {
39
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
40
+ match self {
41
+ GitResolutionError :: GitError ( e) => write ! ( f, "Git error: {}" , e) ,
42
+ GitResolutionError :: IoError ( e) => write ! ( f, "IO error: {}" , e) ,
43
+ GitResolutionError :: InvalidOid => write ! ( f, "Invalid OID for refspec" ) ,
44
+ }
45
+ }
46
+ }
18
47
19
48
impl Resolver for GitResolver {
20
49
type Config = ( ) ;
@@ -23,72 +52,65 @@ impl Resolver for GitResolver {
23
52
type ResolutionError = GitResolutionError ;
24
53
25
54
fn from_config ( _: & Self :: Config ) -> Self {
26
- todo ! ( "" )
55
+ todo ! ( )
27
56
}
28
57
29
58
fn resolve ( & self , desc : & GitDesc ) -> Result < Utf8PathBuf , GitResolutionError > {
30
- // check to see if the dep is already in the dep directory
31
- // check to see if the repo is already cached on the local fs
32
- // if it is, then check to see if the repo is valid with respect to the CID or refspec
33
- // if the repo is not cached, clone it
34
- // copy to dep directory
35
- // resolve the repo path
36
- todo ! ( "" )
59
+ let clone_path = if let Some ( ref local_path) = desc. local_path {
60
+ Utf8PathBuf :: from ( local_path)
61
+ } else {
62
+ self . default_clone_path
63
+ . join ( format ! ( "{}_{}" , & desc. remote, & desc. refspec) )
64
+ } ;
65
+
66
+ if clone_path. exists ( ) {
67
+ let repo = Repository :: open ( & clone_path) . map_err ( GitResolutionError :: from) ?;
68
+
69
+ if !repo. find_reference ( & desc. refspec ) . is_ok ( ) {
70
+ fs:: remove_dir_all ( & clone_path) ?;
71
+ self . clone_repo ( & desc. remote , & clone_path, & desc. refspec ) ?;
72
+ }
73
+ } else {
74
+ self . clone_repo ( & desc. remote , & clone_path, & desc. refspec ) ?;
75
+ }
76
+
77
+ Ok ( clone_path)
37
78
}
38
79
}
39
80
40
- // use git2::{FetchOptions, Oid, Repository};
41
- // use std::error::Error;
42
- // use std::path::Path;
43
-
44
- // /// Fetch and checkout the specified refspec from the remote repository without
45
- // /// fetching any additional history.
46
- // pub fn fetch_and_checkout<P: AsRef<Path>>(
47
- // remote: &str,
48
- // target_directory: P,
49
- // refspec: &str,
50
- // ) -> Result<(), Box<dyn Error>> {
51
- // // We initialize the repo here so that we can be sure that we created a directory that
52
- // // needs to be clean up in case of an error. If the init fails, there won't be anything
53
- // // to clean up.
54
- // let repo = Repository::init(&target_directory)?;
55
- // let res = _fetch_and_checkout(remote, repo, refspec);
56
- // if res.is_err() {
57
- // std::fs::remove_dir_all(target_directory).expect("Failed to clean up directory");
58
- // }
59
-
60
- // res
61
- // }
62
-
63
- // fn _fetch_and_checkout(
64
- // remote: &str,
65
- // repo: Repository,
66
- // refspec: &str,
67
- // ) -> Result<(), Box<dyn Error>> {
68
- // let mut remote = repo.remote("origin", remote)?;
69
-
70
- // let mut fetch_options = FetchOptions::new();
71
-
72
- // fetch_options.depth(1);
73
-
74
- // // Fetch the specified SHA1 with depth 1
75
- // if let Err(e) = remote.fetch(&[refspec], Some(&mut fetch_options), None) {
76
- // if let (git2::ErrorClass::Net, git2::ErrorCode::GenericError) = (e.class(), e.code()) {
77
- // // That's a pretty cryptic error for the common case of the refspec not existing.
78
- // // We keep the cryptic error (because it might have other causes) but add a hint.
79
- // return Err(format!("{}\nMake sure revision {} exists in remote", e, refspec).into());
80
- // } else {
81
- // return Err(e.into());
82
- // }
83
- // }
84
-
85
- // // Find the fetched commit by SHA1
86
- // let oid = Oid::from_str(refspec)?;
87
- // let commit = repo.find_commit(oid)?;
88
-
89
- // // Checkout the commit
90
- // repo.checkout_tree(commit.as_object(), None)?;
91
- // repo.set_head_detached(oid)?;
92
-
93
- // Ok(())
94
- // }
81
+ impl GitResolver {
82
+ fn clone_repo (
83
+ & self ,
84
+ remote : & str ,
85
+ target_directory : & Utf8PathBuf ,
86
+ refspec : & str ,
87
+ ) -> Result < ( ) , GitResolutionError > {
88
+ let repo = Repository :: init ( target_directory) ?;
89
+
90
+ self . fetch_and_checkout ( remote, & repo, refspec) ?;
91
+
92
+ Ok ( ( ) )
93
+ }
94
+
95
+ fn fetch_and_checkout (
96
+ & self ,
97
+ remote : & str ,
98
+ repo : & Repository ,
99
+ refspec : & str ,
100
+ ) -> Result < ( ) , GitResolutionError > {
101
+ let mut remote = repo. remote ( "origin" , remote) ?;
102
+
103
+ let mut fetch_options = FetchOptions :: new ( ) ;
104
+ fetch_options. depth ( 1 ) ;
105
+
106
+ remote. fetch ( & [ refspec] , Some ( & mut fetch_options) , None ) ?;
107
+
108
+ let oid = Oid :: from_str ( refspec) . map_err ( |_| GitResolutionError :: InvalidOid ) ?;
109
+ let commit = repo. find_commit ( oid) ?;
110
+
111
+ repo. checkout_tree ( commit. as_object ( ) , None ) ?;
112
+ repo. set_head_detached ( oid) ?;
113
+
114
+ Ok ( ( ) )
115
+ }
116
+ }
0 commit comments