@@ -3,6 +3,7 @@ use git_repository as git;
3
3
use git_repository:: prelude:: ObjectIdExt ;
4
4
use git_repository:: refs:: transaction:: PreviousValue ;
5
5
use std:: convert:: TryFrom ;
6
+ use std:: sync:: atomic:: AtomicBool ;
6
7
7
8
mod delegate;
8
9
use delegate:: Delegate ;
@@ -12,7 +13,7 @@ use delegate::Delegate;
12
13
#[ allow( missing_docs) ]
13
14
pub enum Error {
14
15
#[ error( "Failed to fetch crates.io index repository" ) ]
15
- Fetch ( #[ from] git2:: Error ) ,
16
+ FetchGit2 ( #[ from] git2:: Error ) ,
16
17
#[ error( "Couldn't update marker reference" ) ]
17
18
ReferenceEdit ( #[ from] git:: reference:: edit:: Error ) ,
18
19
#[ error( "Failed to parse rev-spec to determine which revisions to diff" ) ]
@@ -29,6 +30,23 @@ pub enum Error {
29
30
file_name : bstr:: BString ,
30
31
line : bstr:: BString ,
31
32
} ,
33
+ #[ error( transparent) ]
34
+ FindRemote ( #[ from] git:: remote:: find:: existing:: Error ) ,
35
+ #[ error( transparent) ]
36
+ FindReference ( #[ from] git:: reference:: find:: existing:: Error ) ,
37
+ #[ error( transparent) ]
38
+ Connect ( #[ from] git:: remote:: connect:: Error ) ,
39
+ #[ error( transparent) ]
40
+ PrepareFetch ( #[ from] git:: remote:: fetch:: prepare:: Error ) ,
41
+ #[ error( transparent) ]
42
+ Fetch ( #[ from] git:: remote:: fetch:: Error ) ,
43
+ #[ error( transparent) ]
44
+ InitAnonymousRemote ( #[ from] git:: remote:: init:: Error ) ,
45
+ #[ error( "Could not find local tracking branch for remote branch {name:?} in any of {} fetched refs" , mappings. len( ) ) ]
46
+ NoMatchingBranch {
47
+ name : String ,
48
+ mappings : Vec < git:: remote:: fetch:: Mapping > ,
49
+ } ,
32
50
}
33
51
34
52
/// Find changes without modifying the underling repository
@@ -63,13 +81,16 @@ impl Index {
63
81
. ok ( )
64
82
. and_then ( |r| r. try_id ( ) . map ( |id| id. detach ( ) ) )
65
83
. unwrap_or_else ( || git:: hash:: ObjectId :: empty_tree ( repo. object_hash ( ) ) ) ;
84
+ let remote_name = self
85
+ . remote_name
86
+ . expect ( "always set for this old portion of the code" ) ;
66
87
let to = {
67
88
let repo = git2:: Repository :: open ( repo. git_dir ( ) ) ?;
68
- repo. find_remote ( self . remote_name ) . and_then ( |mut r| {
89
+ repo. find_remote ( remote_name) . and_then ( |mut r| {
69
90
r. fetch (
70
91
& [ format ! (
71
92
"+refs/heads/{branch}:refs/remotes/{remote}/{branch}" ,
72
- remote = self . remote_name,
93
+ remote = remote_name,
73
94
branch = self . branch_name,
74
95
) ] ,
75
96
options,
@@ -79,7 +100,7 @@ impl Index {
79
100
git:: hash:: ObjectId :: try_from (
80
101
repo. refname_to_id ( & format ! (
81
102
"refs/remotes/{}/{}" ,
82
- self . remote_name, self . branch_name
103
+ remote_name, self . branch_name
83
104
) ) ?
84
105
. as_bytes ( ) ,
85
106
)
@@ -89,6 +110,110 @@ impl Index {
89
110
Ok ( ( self . changes_between_commits ( from, to) ?, to) )
90
111
}
91
112
113
+ /// Return all `Change`s that are observed between the last time `peek_changes*(…)` was called
114
+ /// and the latest state of the `crates.io` index repository, which is obtained by fetching
115
+ /// the remote called `origin` or whatever is configured for the current `HEAD` branch and lastly
116
+ /// what it should be based on knowledge about he crates index.
117
+ /// The `last_seen_reference()` will not be created or updated.
118
+ /// The second field in the returned tuple is the commit object to which the changes were provided.
119
+ /// If one would set the `last_seen_reference()` to that object, the effect is exactly the same
120
+ /// as if `fetch_changes(…)` had been called.
121
+ ///
122
+ /// # Resource Usage
123
+ ///
124
+ /// As this method fetches the git repository, loose objects or small packs may be created. Over time,
125
+ /// these will accumulate and either slow down subsequent operations, or cause them to fail due to exhaustion
126
+ /// of the maximum number of open file handles as configured with `ulimit`.
127
+ ///
128
+ /// Thus it is advised for the caller to run `git gc` occasionally based on their own requirements and usage patterns.
129
+ // TODO: update this once it's clear how auto-gc works in `gitoxide`.
130
+ pub fn peek_changes_with_options2 (
131
+ & self ,
132
+ progress : impl git:: Progress ,
133
+ should_interrupt : & AtomicBool ,
134
+ ) -> Result < ( Vec < Change > , git:: hash:: ObjectId ) , Error > {
135
+ let repo = & self . repo ;
136
+ let from = repo
137
+ . find_reference ( self . seen_ref_name )
138
+ . ok ( )
139
+ . and_then ( |r| r. try_id ( ) . map ( |id| id. detach ( ) ) )
140
+ . unwrap_or_else ( || git:: hash:: ObjectId :: empty_tree ( repo. object_hash ( ) ) ) ;
141
+ let to = {
142
+ let remote = self
143
+ . remote_name
144
+ . and_then ( |name| {
145
+ self . repo . find_remote ( name) . ok ( ) . or_else ( || {
146
+ self . repo
147
+ . head ( )
148
+ . ok ( )
149
+ . and_then ( |head| {
150
+ head. into_remote ( git:: remote:: Direction :: Fetch )
151
+ . map ( |r| r. ok ( ) )
152
+ . flatten ( )
153
+ } )
154
+ . or_else ( || {
155
+ self . repo
156
+ . find_default_remote ( git:: remote:: Direction :: Fetch )
157
+ . map ( |r| r. ok ( ) )
158
+ . flatten ( )
159
+ } )
160
+ } )
161
+ } )
162
+ . map ( Ok )
163
+ . unwrap_or_else ( || {
164
+ self . repo
165
+ . head ( ) ?
166
+ . into_remote ( git:: remote:: Direction :: Fetch )
167
+ . map ( |r| r. map_err ( Error :: from) )
168
+ . or_else ( || {
169
+ self . repo
170
+ . find_default_remote ( git:: remote:: Direction :: Fetch )
171
+ . map ( |r| r. map_err ( Error :: from) )
172
+ } )
173
+ . unwrap_or_else ( || {
174
+ let spec = format ! (
175
+ "+refs/heads/{branch}:refs/remotes/{remote}/{branch}" ,
176
+ remote = self . remote_name. unwrap_or( "origin" ) ,
177
+ branch = self . branch_name,
178
+ ) ;
179
+ self . repo
180
+ . remote_at ( "https://github.com/rust-lang/crates.io-index" )
181
+ . map_err ( Into :: into)
182
+ . map ( |r| {
183
+ r. with_refspec ( spec. as_str ( ) , git:: remote:: Direction :: Fetch )
184
+ . expect ( "valid refspec" )
185
+ } )
186
+ } )
187
+ } ) ?;
188
+ let res: git:: remote:: fetch:: Outcome < ' _ > = remote
189
+ . connect ( git:: remote:: Direction :: Fetch , progress) ?
190
+ . prepare_fetch ( Default :: default ( ) ) ?
191
+ . receive ( should_interrupt) ?;
192
+ let branch_name = format ! ( "refs/heads/{}" , self . branch_name) ;
193
+ let local_tracking = res
194
+ . ref_map
195
+ . mappings
196
+ . iter ( )
197
+ . find_map ( |m| match & m. remote {
198
+ git:: remote:: fetch:: Source :: Ref ( r) => ( r. unpack ( ) . 0 == branch_name)
199
+ . then ( || m. local . as_ref ( ) )
200
+ . flatten ( ) ,
201
+ _ => None ,
202
+ } )
203
+ . ok_or_else ( || Error :: NoMatchingBranch {
204
+ name : branch_name,
205
+ mappings : res. ref_map . mappings . clone ( ) ,
206
+ } ) ?;
207
+ self . repo
208
+ . find_reference ( local_tracking)
209
+ . expect ( "local tracking branch exists if we see it here" )
210
+ . id ( )
211
+ . detach ( )
212
+ } ;
213
+
214
+ Ok ( ( self . changes_between_commits ( from, to) ?, to) )
215
+ }
216
+
92
217
/// Similar to `changes()`, but requires `from` and `to` objects to be provided. They may point
93
218
/// to either `Commit`s or `Tree`s.
94
219
pub fn changes_between_commits (
0 commit comments