2
2
3
3
use snafu:: prelude:: * ;
4
4
5
+ use camino:: { Utf8Path , Utf8PathBuf } ;
5
6
use filetime:: FileTime ;
6
7
use git2:: { Diff , Oid , Repository } ;
7
8
use std:: collections:: { HashMap , HashSet } ;
8
- use std:: path :: { Path , PathBuf } ;
9
+ use std:: convert :: TryInto ;
9
10
use std:: sync:: { Arc , RwLock } ;
10
11
use std:: { env, fs} ;
11
12
@@ -30,6 +31,10 @@ pub enum Error {
30
31
PathError {
31
32
source : std:: path:: StripPrefixError ,
32
33
} ,
34
+ #[ snafu( display( "Path contains invalide Unicode:\n {}" , source) ) ]
35
+ PathEncodingError {
36
+ source : camino:: FromPathBufError ,
37
+ } ,
33
38
UnresolvedError { } ,
34
39
}
35
40
@@ -43,7 +48,7 @@ impl std::fmt::Debug for Error {
43
48
44
49
pub type Result < T , E = Error > = std:: result:: Result < T , E > ;
45
50
46
- pub type FileSet = HashSet < PathBuf > ;
51
+ pub type FileSet = HashSet < Utf8PathBuf > ;
47
52
48
53
/// Options passed to `reset_mtimes()`
49
54
#[ derive( Clone , Debug ) ]
@@ -144,14 +149,18 @@ pub fn get_repo() -> Result<Repository> {
144
149
}
145
150
146
151
/// Convert a path relative to the current working directory to be relative to the repository root
147
- pub fn resolve_repo_path ( repo : & Repository , path : & String ) -> Result < PathBuf > {
148
- let cwd = env:: current_dir ( ) . context ( IoSnafu ) ?;
149
- let root = repo. workdir ( ) . context ( UnresolvedSnafu ) ?. to_path_buf ( ) ;
150
- let prefix = cwd. strip_prefix ( & root) . context ( PathSnafu ) ?;
151
- let resolved_path = if Path :: new ( & path) . is_absolute ( ) {
152
- PathBuf :: from ( path. clone ( ) )
152
+ pub fn resolve_repo_path ( repo : & Repository , path : impl Into < Utf8PathBuf > ) -> Result < Utf8PathBuf > {
153
+ let path: Utf8PathBuf = path. into ( ) ;
154
+ let cwd: Utf8PathBuf = env:: current_dir ( )
155
+ . context ( IoSnafu ) ?
156
+ . try_into ( )
157
+ . context ( PathEncodingSnafu ) ?;
158
+ let root = repo. workdir ( ) . context ( UnresolvedSnafu ) ?;
159
+ let prefix: Utf8PathBuf = cwd. strip_prefix ( root) . context ( PathSnafu ) ?. into ( ) ;
160
+ let resolved_path = if path. is_absolute ( ) {
161
+ path
153
162
} else {
154
- prefix. join ( path. clone ( ) )
163
+ prefix. join ( path)
155
164
} ;
156
165
Ok ( resolved_path)
157
166
}
@@ -204,7 +213,7 @@ fn gather_workdir_files(repo: &Repository) -> Result<FileSet> {
204
213
tree. walk ( git2:: TreeWalkMode :: PostOrder , |dir, entry| {
205
214
if let Some ( name) = entry. name ( ) {
206
215
let file = format ! ( "{}{}" , dir, name) ;
207
- let path = Path :: new ( & file) ;
216
+ let path = Utf8Path :: new ( & file) ;
208
217
if path. is_dir ( ) {
209
218
return git2:: TreeWalkResult :: Skip ;
210
219
}
@@ -216,7 +225,7 @@ fn gather_workdir_files(repo: &Repository) -> Result<FileSet> {
216
225
Ok ( workdir_files)
217
226
}
218
227
219
- fn diff_affects_oid ( diff : & Diff , oid : & Oid , touchable_path : & mut PathBuf ) -> bool {
228
+ fn diff_affects_oid ( diff : & Diff , oid : & Oid , touchable_path : & mut Utf8PathBuf ) -> bool {
220
229
diff. deltas ( ) . any ( |delta| {
221
230
delta. new_file ( ) . id ( ) == * oid
222
231
&& delta
@@ -227,18 +236,14 @@ fn diff_affects_oid(diff: &Diff, oid: &Oid, touchable_path: &mut PathBuf) -> boo
227
236
} )
228
237
}
229
238
230
- fn touch_if_older ( path : PathBuf , time : i64 , verbose : bool ) -> Result < bool > {
239
+ fn touch_if_older ( path : Utf8PathBuf , time : i64 , verbose : bool ) -> Result < bool > {
231
240
let commit_time = FileTime :: from_unix_time ( time, 0 ) ;
232
241
let metadata = fs:: metadata ( & path) . context ( IoSnafu ) ?;
233
242
let file_mtime = FileTime :: from_last_modification_time ( & metadata) ;
234
243
if file_mtime != commit_time {
235
244
filetime:: set_file_mtime ( & path, commit_time) . context ( IoSnafu ) ?;
236
245
if verbose {
237
- if let Ok ( pathstring) = path. clone ( ) . into_os_string ( ) . into_string ( ) {
238
- println ! ( "Rewound the clock: {pathstring}" ) ;
239
- } else {
240
- return UnresolvedSnafu { } . fail ( ) ;
241
- }
246
+ println ! ( "Rewound the clock: {path}" ) ;
242
247
}
243
248
return Ok ( true ) ;
244
249
}
@@ -247,7 +252,7 @@ fn touch_if_older(path: PathBuf, time: i64, verbose: bool) -> Result<bool> {
247
252
248
253
fn process_touchables ( repo : & Repository , touchables : FileSet , opts : & Options ) -> Result < FileSet > {
249
254
let touched = Arc :: new ( RwLock :: new ( FileSet :: new ( ) ) ) ;
250
- let mut touchable_oids: HashMap < Oid , PathBuf > = HashMap :: new ( ) ;
255
+ let mut touchable_oids: HashMap < Oid , Utf8PathBuf > = HashMap :: new ( ) ;
251
256
let mut revwalk = repo. revwalk ( ) . context ( LibGitSnafu ) ?;
252
257
// See https://github.com/arkark/git-hist/blob/main/src/app/git.rs
253
258
revwalk. push_head ( ) . context ( LibGitSnafu ) ?;
@@ -261,9 +266,9 @@ fn process_touchables(repo: &Repository, touchables: FileSet, opts: &Options) ->
261
266
. tree ( )
262
267
. context ( LibGitSnafu ) ?;
263
268
touchables. iter ( ) . for_each ( |path| {
264
- let touchable_path = Path :: new ( & path) . to_path_buf ( ) ;
269
+ let touchable_path: Utf8PathBuf = path. into ( ) ;
265
270
let current_oid = latest_tree
266
- . get_path ( & touchable_path)
271
+ . get_path ( & touchable_path. clone ( ) . into_std_path_buf ( ) )
267
272
. and_then ( |entry| {
268
273
if let Some ( git2:: ObjectType :: Blob ) = entry. kind ( ) {
269
274
Ok ( entry)
0 commit comments