1- use anyhow:: bail;
2- use gix:: bstr:: { BString , ByteSlice } ;
1+ use gix:: bstr:: { BStr , BString , ByteSlice } ;
32
43pub fn log ( mut repo : gix:: Repository , out : & mut dyn std:: io:: Write , path : Option < BString > ) -> anyhow:: Result < ( ) > {
54 repo. object_cache_size_if_unset ( repo. compute_object_cache_size_for_tree_diffs ( & * * repo. index_or_empty ( ) ?) ) ;
@@ -25,8 +24,20 @@ fn log_all(repo: gix::Repository, out: &mut dyn std::io::Write) -> Result<(), an
2524 Ok ( ( ) )
2625}
2726
28- fn log_file ( _repo : gix:: Repository , _out : & mut dyn std:: io:: Write , _path : BString ) -> anyhow:: Result < ( ) > {
29- bail ! ( "File-based lookup isn't yet implemented in a way that is competitively fast" ) ;
27+ fn log_file ( repo : gix:: Repository , out : & mut dyn std:: io:: Write , path : BString ) -> anyhow:: Result < ( ) > {
28+ let path = gix:: path:: to_unix_separators_on_windows ( path. as_bstr ( ) ) . into_owned ( ) ;
29+ let head = repo. head ( ) ?. peel_to_commit ( ) ?;
30+ let cache = repo. commit_graph_if_enabled ( ) ?;
31+ let topo = gix:: traverse:: commit:: topo:: Builder :: from_iters ( & repo. objects , [ head. id ] , None :: < Vec < gix:: ObjectId > > )
32+ . build ( ) ?;
33+
34+ for info in topo {
35+ let info = info?;
36+ if commit_changes_path ( & repo, cache. as_ref ( ) , & info, path. as_ref ( ) ) ? {
37+ write_info ( & repo, & mut * out, & info) ?;
38+ }
39+ }
40+ Ok ( ( ) )
3041}
3142
3243fn write_info (
@@ -48,3 +59,41 @@ fn write_info(
4859
4960 Ok ( ( ) )
5061}
62+
63+ fn commit_changes_path (
64+ repo : & gix:: Repository ,
65+ cache : Option < & gix:: commitgraph:: Graph > ,
66+ info : & gix:: traverse:: commit:: Info ,
67+ path : & BStr ,
68+ ) -> anyhow:: Result < bool > {
69+ let commit = repo. find_commit ( info. id ) ?;
70+ let commit_tree = commit. tree ( ) ?;
71+ let commit_entry = lookup_path_entry ( & commit_tree, path) ?;
72+
73+ if info. parent_ids . is_empty ( ) {
74+ return Ok ( commit_entry. is_some ( ) ) ;
75+ }
76+
77+ for ( index, parent_id) in info. parent_ids . iter ( ) . enumerate ( ) {
78+ if index == 0 && cache. and_then ( |graph| graph. maybe_contains_path_by_id ( info. id , path) ) == Some ( false ) {
79+ continue ;
80+ }
81+
82+ let parent = repo. find_commit ( * parent_id) ?;
83+ let parent_tree = parent. tree ( ) ?;
84+ let parent_entry = lookup_path_entry ( & parent_tree, path) ?;
85+ if commit_entry != parent_entry {
86+ return Ok ( true ) ;
87+ }
88+ }
89+
90+ Ok ( false )
91+ }
92+
93+ fn lookup_path_entry (
94+ tree : & gix:: Tree < ' _ > ,
95+ path : & BStr ,
96+ ) -> anyhow:: Result < Option < ( gix:: objs:: tree:: EntryMode , gix:: ObjectId ) > > {
97+ let entry = tree. lookup_entry ( path. split ( |b| * b == b'/' ) ) ?;
98+ Ok ( entry. map ( |entry| ( entry. mode ( ) , entry. object_id ( ) ) ) )
99+ }
0 commit comments