1
1
use errors:: * ;
2
2
3
- use git2:: { Repository , RepositoryInitOptions , Remote , PushOptions , RemoteCallbacks , Cred } ;
4
- use std:: path:: { Path } ;
5
3
use std:: fs:: { self } ;
4
+ use std:: path:: { Path } ;
5
+
6
+ use std:: collections:: HashMap ;
7
+
8
+ use git2:: { Repository , RepositoryInitOptions , Remote , FetchOptions , Direction , FetchPrune , PushOptions , RemoteCallbacks , Cred } ;
6
9
7
10
use config:: RepositoryMapping ;
8
11
9
12
const ATTEMPTS : u8 = 3 ;
10
13
14
+ #[ derive( Debug ) ]
15
+ enum RefAction {
16
+ Update ,
17
+ Create ,
18
+ Delete ,
19
+ }
20
+
11
21
pub trait GitRepository {
12
22
fn repository_name ( & self ) -> & str ;
13
23
@@ -49,24 +59,12 @@ fn attempt_open<T: GitRepository>(repo: &T, attempts: u8) -> Result<Repository>
49
59
. mkdir ( true )
50
60
. mkpath ( true ) ;
51
61
52
- // let repo = init_bare(repo.repository_name())
53
-
54
62
Ok ( Repository :: init_opts ( repo. repository_name ( ) , & repo_opts) ?)
55
63
}
56
64
57
65
const ALL_HEADS : & ' static str = "+refs/heads/*:refs/heads/*" ;
58
66
const ALL_TAGS : & ' static str = "+refs/tags/*:refs/tags/*" ;
59
67
60
- fn disconnect_head ( repo : & Repository ) -> Result < ( ) > {
61
- let head = repo. head ( ) ?;
62
-
63
- let oid = head. target ( ) . ok_or ( Error :: from_kind ( ErrorKind :: RepositoryOpenError ) ) ?;
64
-
65
- repo. set_head_detached ( oid) ?;
66
-
67
- Ok ( ( ) )
68
- }
69
-
70
68
const REMOTE_FETCH : & ' static str = "fetch" ;
71
69
72
70
fn open_remote_fetch < ' a > ( repo : & ' a Repository , uri : & str ) -> Result < Remote < ' a > > {
@@ -82,45 +80,19 @@ fn open_remote_fetch<'a>(repo: &'a Repository, uri: &str) -> Result<Remote<'a>>
82
80
}
83
81
}
84
82
85
- const REMOTE_PUSH : & ' static str = "push" ;
86
-
87
83
fn open_remote_push < ' a > ( repo : & ' a Repository , uri : & str ) -> Result < Remote < ' a > > {
88
- if let Ok ( remote) = repo. find_remote ( REMOTE_PUSH ) {
89
- Ok ( remote)
90
- } else {
91
- repo. remote ( REMOTE_PUSH , uri) ?;
92
-
93
- // repo.remote_add_push(REMOTE_PUSH, ALL_HEADS)?;
94
- // repo.remote_add_push(REMOTE_PUSH, ALL_TAGS)?;
95
-
96
- open_remote_push ( repo, uri)
97
- }
98
- }
99
-
100
- fn push_glob ( repository : & Repository , remote : & mut Remote , glob : & str , push_options : Option < & mut PushOptions > ) -> Result < ( ) > {
101
- let mut refs = repository. references_glob ( glob) ?;
102
-
103
- let mut push_refs : Vec < & str > = Vec :: new ( ) ;
104
-
105
- for reference in refs. names ( ) {
106
- if let Ok ( name) = reference {
107
- push_refs. push ( name) ;
108
- }
109
- }
110
-
111
- for name in & push_refs {
112
- println ! ( "{:?}" , name) ;
113
- }
114
-
115
- Ok ( ( ) )
84
+ Ok ( repo. remote_anonymous ( uri) ?)
116
85
}
117
86
118
87
pub fn grapple < T : GitRepository > ( payload : & T , mapping : & RepositoryMapping ) -> Result < ( ) > {
119
88
let repo = open ( payload) ?;
120
89
121
90
let mut fetch = open_remote_fetch ( & repo, payload. clone_uri ( ) ) ?;
122
91
123
- fetch. fetch ( & [ ] , None , None ) ?;
92
+ let mut fetch_options = FetchOptions :: new ( ) ;
93
+ fetch_options. prune ( FetchPrune :: On ) ;
94
+
95
+ fetch. fetch ( & [ ] , Some ( & mut fetch_options) , None ) ?;
124
96
125
97
let mut push = open_remote_push ( & repo, & mapping. push_uri ) ?;
126
98
@@ -135,8 +107,67 @@ pub fn grapple<T: GitRepository>(payload: &T, mapping: &RepositoryMapping) -> Re
135
107
let mut push_options = PushOptions :: new ( ) ;
136
108
push_options. remote_callbacks ( callbacks) ;
137
109
138
- push_glob ( & repo, & mut push, "refs/heads/*" , Some ( & mut push_options) ) ?;
139
- push_glob ( & repo, & mut push, "refs/tags/*" , Some ( & mut push_options) ) ?;
110
+ fetch. connect ( Direction :: Fetch ) ?;
111
+
112
+ let fetch_list = fetch. list ( ) ?;
113
+
114
+ let mut ref_map = HashMap :: new ( ) ;
115
+
116
+ println ! ( "Fetch refs:" ) ;
117
+ for reference in fetch_list. iter ( ) . filter ( |r| r. name ( ) . starts_with ( "refs/" ) ) {
118
+ ref_map. insert ( reference. name ( ) , reference. oid ( ) ) ;
119
+ }
120
+
121
+
122
+ let mut callbacks = RemoteCallbacks :: new ( ) ;
123
+ callbacks. credentials (
124
+ |_, username, _| Cred :: ssh_key (
125
+ username. unwrap_or ( "grapple" ) ,
126
+ Some ( Path :: new ( & mapping. deploy_public_key ) ) ,
127
+ Path :: new ( & mapping. deploy_private_key ) ,
128
+ None ) ) ;
129
+
130
+ push. connect_auth ( Direction :: Push , Some ( callbacks) , None ) ?;
131
+
132
+ let push_list = push. list ( ) ?;
133
+
134
+ let mut action_list = Vec :: new ( ) ;
135
+
136
+ println ! ( "Push refs:" ) ;
137
+ for reference in push_list. iter ( ) . filter ( |r| r. name ( ) . starts_with ( "refs/" ) ) {
138
+ let ref_name = reference. name ( ) ;
139
+ let push_oid = reference. oid ( ) ;
140
+ match ref_map. remove ( ref_name) {
141
+ Some ( fetch_oid) if fetch_oid != push_oid => action_list. push ( ( ref_name, RefAction :: Update ) ) ,
142
+ Some ( _) => ( ) ,
143
+ None => action_list. push ( ( ref_name, RefAction :: Delete ) ) ,
144
+ }
145
+ }
146
+
147
+ for ( ref_name, _) in ref_map. iter ( ) {
148
+ action_list. push ( ( ref_name, RefAction :: Create ) ) ;
149
+ }
150
+
151
+
152
+ let mut fetch = open_remote_fetch ( & repo, payload. clone_uri ( ) ) ?;
153
+
154
+ let mut push = open_remote_push ( & repo, & mapping. push_uri ) ?;
155
+
156
+ for ( ref_name, action) in action_list {
157
+ match action {
158
+ RefAction :: Create | RefAction :: Update => {
159
+ // TODO: collect errors
160
+ let refspec = format ! ( "+{}:{}" , ref_name, ref_name) ;
161
+ fetch. fetch ( & [ & refspec] , Some ( & mut fetch_options) , None ) ?;
162
+ push. push ( & [ & refspec] , Some ( & mut push_options) ) ?;
163
+ } ,
164
+ RefAction :: Delete => {
165
+ let refspec = format ! ( ":{}" , ref_name) ;
166
+ println ! ( "Delete: {}" , ref_name) ;
167
+ push. push ( & [ & refspec] , Some ( & mut push_options) ) ?;
168
+ } ,
169
+ }
170
+ }
140
171
141
172
Ok ( ( ) )
142
173
}
0 commit comments