@@ -6,6 +6,7 @@ use std::path::PathBuf;
6
6
use crate :: OutputFormat ;
7
7
use git_repository as git;
8
8
use git_repository:: prelude:: ObjectIdExt ;
9
+ use git_repository:: Tree ;
9
10
10
11
mod entries {
11
12
use git_repository as git;
@@ -17,28 +18,32 @@ mod entries {
17
18
use git:: traverse:: tree:: visit:: Action ;
18
19
use git_repository:: bstr:: { ByteSlice , ByteVec } ;
19
20
20
- pub struct Traverse < ' a > {
21
+ #[ cfg_attr( feature = "serde1" , derive( serde:: Serialize ) ) ]
22
+ #[ derive( Default ) ]
23
+ pub struct Statistics {
21
24
pub num_trees : usize ,
22
25
pub num_links : usize ,
23
26
pub num_blobs : usize ,
24
27
pub num_blobs_exec : usize ,
25
28
pub num_submodules : usize ,
29
+ #[ cfg_attr( feature = "serde1" , serde( skip_serializing_if = "Option::is_none" ) ) ]
30
+ pub bytes : Option < u64 > ,
31
+ #[ cfg_attr( feature = "serde1" , serde( skip) ) ]
26
32
pub num_bytes : u64 ,
27
- repo : Option < git:: Repository > ,
28
- out : & ' a mut dyn std:: io:: Write ,
33
+ }
34
+
35
+ pub struct Traverse < ' repo , ' a > {
36
+ pub stats : Statistics ,
37
+ repo : Option < & ' repo git:: Repository > ,
38
+ out : Option < & ' a mut dyn std:: io:: Write > ,
29
39
path : BString ,
30
40
path_deque : VecDeque < BString > ,
31
41
}
32
42
33
- impl < ' a > Traverse < ' a > {
34
- pub fn new ( repo : Option < git:: Repository > , out : & ' a mut dyn std:: io:: Write ) -> Self {
43
+ impl < ' repo , ' a > Traverse < ' repo , ' a > {
44
+ pub fn new ( repo : Option < & ' repo git:: Repository > , out : Option < & ' a mut dyn std:: io:: Write > ) -> Self {
35
45
Traverse {
36
- num_trees : 0 ,
37
- num_links : 0 ,
38
- num_blobs : 0 ,
39
- num_blobs_exec : 0 ,
40
- num_submodules : 0 ,
41
- num_bytes : 0 ,
46
+ stats : Default :: default ( ) ,
42
47
repo,
43
48
out,
44
49
path : BString :: default ( ) ,
@@ -62,7 +67,7 @@ mod entries {
62
67
}
63
68
}
64
69
65
- impl < ' a > git:: traverse:: tree:: Visit for Traverse < ' a > {
70
+ impl < ' repo , ' a > git:: traverse:: tree:: Visit for Traverse < ' repo , ' a > {
66
71
fn pop_front_tracked_path_and_set_current ( & mut self ) {
67
72
self . path = self . path_deque . pop_front ( ) . expect ( "every parent is set only once" ) ;
68
73
}
@@ -81,59 +86,78 @@ mod entries {
81
86
}
82
87
83
88
fn visit_tree ( & mut self , _entry : & EntryRef < ' _ > ) -> Action {
84
- self . num_trees += 1 ;
89
+ self . stats . num_trees += 1 ;
85
90
Action :: Continue
86
91
}
87
92
88
93
fn visit_nontree ( & mut self , entry : & EntryRef < ' _ > ) -> Action {
89
94
use git:: objs:: tree:: EntryMode :: * ;
90
95
let size = self
91
96
. repo
92
- . as_ref ( )
93
97
. and_then ( |repo| repo. find_object ( entry. oid ) . map ( |o| o. data . len ( ) ) . ok ( ) ) ;
94
- format_entry ( & mut * self . out , entry, self . path . as_bstr ( ) , size) . ok ( ) ;
98
+ if let Some ( out) = & mut self . out {
99
+ format_entry ( out, entry, self . path . as_bstr ( ) , size) . ok ( ) ;
100
+ }
95
101
if let Some ( size) = size {
96
- self . num_bytes += size as u64 ;
102
+ self . stats . num_bytes += size as u64 ;
97
103
}
98
104
99
105
match entry. mode {
100
- Commit => self . num_submodules += 1 ,
101
- Blob => self . num_blobs += 1 ,
102
- BlobExecutable => self . num_blobs_exec += 1 ,
103
- Link => self . num_links += 1 ,
106
+ Commit => self . stats . num_submodules += 1 ,
107
+ Blob => self . stats . num_blobs += 1 ,
108
+ BlobExecutable => self . stats . num_blobs_exec += 1 ,
109
+ Link => self . stats . num_links += 1 ,
104
110
Tree => unreachable ! ( "BUG" ) ,
105
111
}
106
112
Action :: Continue
107
113
}
108
114
}
109
115
}
110
116
117
+ pub fn info (
118
+ repository : PathBuf ,
119
+ treeish : Option < & str > ,
120
+ extended : bool ,
121
+ format : OutputFormat ,
122
+ out : & mut dyn io:: Write ,
123
+ err : & mut dyn io:: Write ,
124
+ ) -> anyhow:: Result < ( ) > {
125
+ if format == OutputFormat :: Human {
126
+ writeln ! ( err, "Only JSON is implemented - using that instead" ) ?;
127
+ }
128
+
129
+ let repo = git:: open ( repository) ?. apply_environment ( ) ;
130
+ let tree = treeish_to_tree ( treeish, & repo) ?;
131
+
132
+ let mut delegate = entries:: Traverse :: new ( extended. then ( || & repo) , None ) ;
133
+ tree. traverse ( ) . breadthfirst ( & mut delegate) ?;
134
+
135
+ #[ cfg( feature = "serde1" ) ]
136
+ {
137
+ delegate. stats . bytes = extended. then ( || delegate. stats . num_bytes ) ;
138
+ serde_json:: to_writer_pretty ( out, & delegate. stats ) ?;
139
+ }
140
+
141
+ Ok ( ( ) )
142
+ }
143
+
111
144
pub fn entries (
112
145
repository : PathBuf ,
113
146
treeish : Option < & str > ,
114
147
recursive : bool ,
115
148
extended : bool ,
116
149
format : OutputFormat ,
117
150
out : & mut dyn io:: Write ,
118
- _err : & mut dyn io:: Write ,
119
151
) -> anyhow:: Result < ( ) > {
120
152
if format == OutputFormat :: Json {
121
153
bail ! ( "Only human output format is supported at the moment" ) ;
122
154
}
123
155
124
- let tree_repo = git:: open ( repository) ?;
125
- let repo = tree_repo. clone ( ) . apply_environment ( ) ;
126
-
127
- let tree = match treeish {
128
- Some ( hex) => git:: hash:: ObjectId :: from_hex ( hex. as_bytes ( ) )
129
- . map ( |id| id. attach ( & repo) ) ?
130
- . object ( ) ?
131
- . try_into_tree ( ) ?,
132
- None => repo. head ( ) ?. peel_to_commit_in_place ( ) ?. tree ( ) ?,
133
- } ;
156
+ let repo = git:: open ( repository) ?. apply_environment ( ) ;
157
+ let tree = treeish_to_tree ( treeish, & repo) ?;
134
158
135
159
if recursive {
136
- let mut delegate = entries:: Traverse :: new ( extended. then ( || tree_repo ) , out) ;
160
+ let mut delegate = entries:: Traverse :: new ( extended. then ( || & repo ) , out. into ( ) ) ;
137
161
tree. traverse ( ) . breadthfirst ( & mut delegate) ?;
138
162
} else {
139
163
for entry in tree. iter ( ) {
@@ -152,6 +176,16 @@ pub fn entries(
152
176
Ok ( ( ) )
153
177
}
154
178
179
+ fn treeish_to_tree < ' repo > ( treeish : Option < & str > , repo : & ' repo git:: Repository ) -> anyhow:: Result < Tree < ' repo > > {
180
+ Ok ( match treeish {
181
+ Some ( hex) => git:: hash:: ObjectId :: from_hex ( hex. as_bytes ( ) )
182
+ . map ( |id| id. attach ( & repo) ) ?
183
+ . object ( ) ?
184
+ . try_into_tree ( ) ?,
185
+ None => repo. head ( ) ?. peel_to_commit_in_place ( ) ?. tree ( ) ?,
186
+ } )
187
+ }
188
+
155
189
fn format_entry (
156
190
mut out : impl io:: Write ,
157
191
entry : & git:: objs:: tree:: EntryRef < ' _ > ,
0 commit comments