@@ -10,13 +10,20 @@ pub struct Options {
10
10
pub ref_specs : Vec < BString > ,
11
11
pub shallow : gix:: remote:: fetch:: Shallow ,
12
12
pub handshake_info : bool ,
13
+ pub negotiation_info : bool ,
14
+ pub open_negotiation_graph : Option < std:: path:: PathBuf > ,
13
15
}
14
16
15
17
pub const PROGRESS_RANGE : std:: ops:: RangeInclusive < u8 > = 1 ..=3 ;
16
18
17
19
pub ( crate ) mod function {
18
20
use anyhow:: bail;
19
21
use gix:: { prelude:: ObjectIdExt , refspec:: match_group:: validate:: Fix , remote:: fetch:: Status } ;
22
+ use layout:: backends:: svg:: SVGWriter ;
23
+ use layout:: core:: base:: Orientation ;
24
+ use layout:: core:: geometry:: Point ;
25
+ use layout:: core:: style:: StyleAttr ;
26
+ use layout:: std_shapes:: shapes:: { Arrow , Element , ShapeKind } ;
20
27
21
28
use super :: Options ;
22
29
use crate :: OutputFormat ;
@@ -31,6 +38,8 @@ pub(crate) mod function {
31
38
dry_run,
32
39
remote,
33
40
handshake_info,
41
+ negotiation_info,
42
+ open_negotiation_graph,
34
43
shallow,
35
44
ref_specs,
36
45
} : Options ,
@@ -62,41 +71,49 @@ pub(crate) mod function {
62
71
63
72
let ref_specs = remote. refspecs ( gix:: remote:: Direction :: Fetch ) ;
64
73
match res. status {
65
- Status :: NoPackReceived { update_refs } => {
66
- print_updates ( & repo, 1 , update_refs, ref_specs, res. ref_map , & mut out, err)
67
- }
68
- Status :: DryRun {
69
- update_refs,
70
- negotiation_rounds,
71
- } => print_updates (
72
- & repo,
73
- negotiation_rounds,
74
- update_refs,
75
- ref_specs,
76
- res. ref_map ,
77
- & mut out,
78
- err,
79
- ) ,
80
- Status :: Change {
74
+ Status :: NoPackReceived {
81
75
update_refs,
82
- write_pack_bundle ,
83
- negotiation_rounds ,
76
+ negotiate ,
77
+ dry_run : _ ,
84
78
} => {
79
+ let negotiate_default = Default :: default ( ) ;
85
80
print_updates (
86
81
& repo,
87
- negotiation_rounds ,
82
+ negotiate . as_ref ( ) . unwrap_or ( & negotiate_default ) ,
88
83
update_refs,
89
84
ref_specs,
90
85
res. ref_map ,
91
86
& mut out,
92
87
err,
93
88
) ?;
89
+ if negotiation_info {
90
+ print_negotiate_info ( & mut out, negotiate. as_ref ( ) ) ?;
91
+ }
92
+ if let Some ( ( negotiate, path) ) =
93
+ open_negotiation_graph. and_then ( |path| negotiate. as_ref ( ) . map ( |n| ( n, path) ) )
94
+ {
95
+ render_graph ( & repo, & negotiate. graph , & path, progress) ?;
96
+ }
97
+ Ok :: < _ , anyhow:: Error > ( ( ) )
98
+ }
99
+ Status :: Change {
100
+ update_refs,
101
+ write_pack_bundle,
102
+ negotiate,
103
+ } => {
104
+ print_updates ( & repo, & negotiate, update_refs, ref_specs, res. ref_map , & mut out, err) ?;
94
105
if let Some ( data_path) = write_pack_bundle. data_path {
95
106
writeln ! ( out, "pack file: \" {}\" " , data_path. display( ) ) . ok ( ) ;
96
107
}
97
108
if let Some ( index_path) = write_pack_bundle. index_path {
98
109
writeln ! ( out, "index file: \" {}\" " , index_path. display( ) ) . ok ( ) ;
99
110
}
111
+ if negotiation_info {
112
+ print_negotiate_info ( & mut out, Some ( & negotiate) ) ?;
113
+ }
114
+ if let Some ( path) = open_negotiation_graph {
115
+ render_graph ( & repo, & negotiate. graph , & path, progress) ?;
116
+ }
100
117
Ok ( ( ) )
101
118
}
102
119
} ?;
@@ -106,9 +123,83 @@ pub(crate) mod function {
106
123
Ok ( ( ) )
107
124
}
108
125
126
+ fn render_graph (
127
+ repo : & gix:: Repository ,
128
+ graph : & gix:: negotiate:: IdMap ,
129
+ path : & std:: path:: Path ,
130
+ mut progress : impl gix:: Progress ,
131
+ ) -> anyhow:: Result < ( ) > {
132
+ progress. init ( Some ( graph. len ( ) ) , gix:: progress:: count ( "commits" ) ) ;
133
+ progress. set_name ( "building graph" ) ;
134
+
135
+ let mut map = gix:: hashtable:: HashMap :: default ( ) ;
136
+ let mut vg = layout:: topo:: layout:: VisualGraph :: new ( Orientation :: TopToBottom ) ;
137
+
138
+ for ( id, commit) in graph. iter ( ) . inspect ( |_| progress. inc ( ) ) {
139
+ let source = match map. get ( id) {
140
+ Some ( handle) => * handle,
141
+ None => {
142
+ let handle = vg. add_node ( new_node ( id. attach ( repo) , commit. data . flags ) ) ;
143
+ map. insert ( * id, handle) ;
144
+ handle
145
+ }
146
+ } ;
147
+
148
+ for parent_id in & commit. parents {
149
+ let dest = match map. get ( parent_id) {
150
+ Some ( handle) => * handle,
151
+ None => {
152
+ let flags = match graph. get ( parent_id) {
153
+ Some ( c) => c. data . flags ,
154
+ None => continue ,
155
+ } ;
156
+ let dest = vg. add_node ( new_node ( parent_id. attach ( repo) , flags) ) ;
157
+ map. insert ( * parent_id, dest) ;
158
+ dest
159
+ }
160
+ } ;
161
+ let arrow = Arrow :: simple ( "" ) ;
162
+ vg. add_edge ( arrow, source, dest) ;
163
+ }
164
+ }
165
+
166
+ let start = std:: time:: Instant :: now ( ) ;
167
+ progress. set_name ( "layout graph" ) ;
168
+ progress. info ( format ! ( "writing {path:?}…" ) ) ;
169
+ let mut svg = SVGWriter :: new ( ) ;
170
+ vg. do_it ( false , false , false , & mut svg) ;
171
+ std:: fs:: write ( path, svg. finalize ( ) . as_bytes ( ) ) ?;
172
+ open:: that ( path) ?;
173
+ progress. show_throughput ( start) ;
174
+
175
+ return Ok ( ( ) ) ;
176
+
177
+ fn new_node ( id : gix:: Id < ' _ > , flags : gix:: negotiate:: Flags ) -> Element {
178
+ let pt = Point :: new ( 250. , 50. ) ;
179
+ let name = format ! ( "{}\n \n {flags:?}" , id. shorten_or_id( ) ) ;
180
+ let shape = ShapeKind :: new_box ( name. as_str ( ) ) ;
181
+ let style = StyleAttr :: simple ( ) ;
182
+ Element :: create ( shape, style, Orientation :: LeftToRight , pt)
183
+ }
184
+ }
185
+
186
+ fn print_negotiate_info (
187
+ mut out : impl std:: io:: Write ,
188
+ negotiate : Option < & gix:: remote:: fetch:: outcome:: Negotiate > ,
189
+ ) -> std:: io:: Result < ( ) > {
190
+ writeln ! ( out, "Negotiation Phase Information" ) ?;
191
+ match negotiate {
192
+ Some ( negotiate) => {
193
+ writeln ! ( out, "\t {:?}" , negotiate. rounds) ?;
194
+ writeln ! ( out, "\t num commits traversed in graph: {}" , negotiate. graph. len( ) )
195
+ }
196
+ None => writeln ! ( out, "\t no negotiation performed" ) ,
197
+ }
198
+ }
199
+
109
200
pub ( crate ) fn print_updates (
110
201
repo : & gix:: Repository ,
111
- negotiation_rounds : usize ,
202
+ negotiate : & gix :: remote :: fetch :: outcome :: Negotiate ,
112
203
update_refs : gix:: remote:: fetch:: refs:: update:: Outcome ,
113
204
refspecs : & [ gix:: refspec:: RefSpec ] ,
114
205
mut map : gix:: remote:: fetch:: RefMap ,
@@ -212,8 +303,10 @@ pub(crate) mod function {
212
303
refspecs. len( )
213
304
) ?;
214
305
}
215
- if negotiation_rounds != 1 {
216
- writeln ! ( err, "needed {negotiation_rounds} rounds of pack-negotiation" ) ?;
306
+ match negotiate. rounds . len ( ) {
307
+ 0 => writeln ! ( err, "no negotiation was necessary" ) ?,
308
+ 1 => { }
309
+ rounds => writeln ! ( err, "needed {rounds} rounds of pack-negotiation" ) ?,
217
310
}
218
311
Ok ( ( ) )
219
312
}
0 commit comments