@@ -2,6 +2,7 @@ const std = @import("std");
22const git = @import ("../git.zig" );
33const c = git .c ;
44const color = @import ("../color.zig" );
5+ const fmt = @import ("../fmt.zig" );
56
67const Writer = std .Io .Writer ;
78
@@ -15,6 +16,7 @@ pub fn run(repo: *c.git_repository, human: bool, staged: bool, w: *Writer) !void
1516 var diff_result : ? * c.git_diff = null ;
1617
1718 if (staged ) {
19+ // Diff HEAD to index. HEAD may not exist yet (initial commit).
1820 var head : ? * c.git_object = null ;
1921 _ = c .git_revparse_single (& head , repo , "HEAD" );
2022 defer if (head != null ) c .git_object_free (head );
@@ -41,194 +43,19 @@ pub fn run(repo: *c.git_repository, human: bool, staged: bool, w: *Writer) !void
4143 if (human ) {
4244 try printHuman (diff_result , w );
4345 } else {
44- var ctx = PrintCtx { .writer = w , . last_file = null };
45- _ = c .git_diff_print (diff_result , c .GIT_DIFF_FORMAT_PATCH , compactCallback , @ptrCast (& ctx ));
46+ var ctx = fmt.CompactCtx { .writer = w };
47+ _ = c .git_diff_print (diff_result , c .GIT_DIFF_FORMAT_PATCH , fmt . compactCallback , @ptrCast (& ctx ));
4648 }
4749}
4850
49- // --- Compact (agent) output ---
50-
51- const PrintCtx = struct {
52- writer : * Writer ,
53- last_file : ? [* :0 ]const u8 ,
54- };
55-
56- fn compactCallback (
57- delta : [* c ]const c.git_diff_delta ,
58- _ : [* c ]const c.git_diff_hunk ,
59- line : [* c ]const c.git_diff_line ,
60- payload : ? * anyopaque ,
61- ) callconv (.c ) c_int {
62- const ctx : * PrintCtx = @ptrCast (@alignCast (payload ));
63- const content = line .* .content ;
64- const len = line .* .content_len ;
65-
66- if (len == 0 or content == null ) return 0 ;
67-
68- const slice = content [0.. len ];
69- const w = ctx .writer ;
70-
71- switch (line .* .origin ) {
72- '+' , '-' , ' ' = > {
73- w .writeByte (@intCast (line .* .origin )) catch return -1 ;
74- w .writeAll (slice ) catch return -1 ;
75- },
76- 'H' = > {
77- // Strip trailing context text after closing @@
78- if (std .mem .indexOf (u8 , slice , " @@" )) | pos | {
79- w .writeAll (slice [0 .. pos + 3 ]) catch return -1 ;
80- w .writeByte ('\n ' ) catch return -1 ;
81- } else {
82- w .writeAll (slice ) catch return -1 ;
83- }
84- },
85- 'F' = > {
86- if (delta != null ) {
87- const path = delta .* .new_file .path ;
88- if (path != null ) {
89- if (slice .len >= 4 and std .mem .eql (u8 , slice [0.. 4], "diff" )) {
90- w .writeAll ("--- " ) catch return -1 ;
91- w .writeAll (std .mem .span (path )) catch return -1 ;
92- w .writeByte ('\n ' ) catch return -1 ;
93- }
94- }
95- }
96- },
97- else = > {},
98- }
99-
100- return 0 ;
101- }
102-
103- // --- Human output ---
104-
10551fn printHuman (diff_result : ? * c.git_diff , w : * Writer ) ! void {
10652 const use_color = color .isTty ();
107-
108- // Print per-file stat + patch
10953 const num_deltas = c .git_diff_num_deltas (diff_result );
11054 if (num_deltas == 0 ) return ;
11155
112- // Collect stats first
113- var stats : ? * c.git_diff_stats = null ;
114- try git .check (c .git_diff_get_stats (& stats , diff_result ));
115- defer c .git_diff_stats_free (stats );
116-
117- const total_files = c .git_diff_stats_files_changed (stats );
118- const total_add = c .git_diff_stats_insertions (stats );
119- const total_del = c .git_diff_stats_deletions (stats );
120-
121- // Summary line
122- if (use_color ) {
123- try w .print ("{s}{d} file{s}{s}, " , .{ color .bold , total_files , if (total_files != 1 ) "s" else "" , color .reset });
124- try w .print ("{s}+{d}{s} " , .{ color .bright_green , total_add , color .reset });
125- try w .print ("{s}-{d}{s}\n\n " , .{ color .bright_red , total_del , color .reset });
126- } else {
127- try w .print ("{d} file{s}, +{d} -{d}\n\n " , .{ total_files , if (total_files != 1 ) "s" else "" , total_add , total_del });
128- }
129-
130- // Print each file's patch
131- var ctx = HumanCtx { .writer = w , .use_color = use_color , .current_file = null };
132- _ = c .git_diff_print (diff_result , c .GIT_DIFF_FORMAT_PATCH , humanCallback , @ptrCast (& ctx ));
133- }
134-
135- const HumanCtx = struct {
136- writer : * Writer ,
137- use_color : bool ,
138- current_file : ? [* :0 ]const u8 ,
139- };
140-
141- fn humanCallback (
142- delta : [* c ]const c.git_diff_delta ,
143- _ : [* c ]const c.git_diff_hunk ,
144- line : [* c ]const c.git_diff_line ,
145- payload : ? * anyopaque ,
146- ) callconv (.c ) c_int {
147- const ctx : * HumanCtx = @ptrCast (@alignCast (payload ));
148- const content = line .* .content ;
149- const len = line .* .content_len ;
150-
151- if (len == 0 or content == null ) return 0 ;
152-
153- const slice = content [0.. len ];
154- const w = ctx .writer ;
155-
156- switch (line .* .origin ) {
157- 'F' = > {
158- // File header - print a clean colored separator on first line only
159- if (delta != null ) {
160- const path = delta .* .new_file .path ;
161- if (path != null and slice .len >= 4 and std .mem .eql (u8 , slice [0.. 4], "diff" )) {
162- // Check if this is a new file vs the last one we printed
163- const new_path = path ;
164- if (ctx .current_file == null or ctx .current_file != new_path ) {
165- if (ctx .current_file != null ) {
166- // Separator between files
167- w .writeByte ('\n ' ) catch return -1 ;
168- }
169- ctx .current_file = new_path ;
170-
171- const path_str = std .mem .span (path );
172-
173- if (ctx .use_color ) {
174- // Bold filename
175- w .writeAll (color .bold ) catch return -1 ;
176- w .writeAll (path_str ) catch return -1 ;
177- w .writeAll (color .reset ) catch return -1 ;
178- w .writeByte ('\n ' ) catch return -1 ;
179- } else {
180- w .writeAll (path_str ) catch return -1 ;
181- w .writeByte ('\n ' ) catch return -1 ;
182- }
183- }
184- }
185- }
186- },
187- 'H' = > {
188- // Hunk header
189- if (ctx .use_color ) {
190- w .writeAll (color .cyan ) catch return -1 ;
191- w .writeAll (slice ) catch return -1 ;
192- w .writeAll (color .reset ) catch return -1 ;
193- } else {
194- w .writeAll (slice ) catch return -1 ;
195- }
196- },
197- '+' = > {
198- if (ctx .use_color ) {
199- w .writeAll (color .bright_green ) catch return -1 ;
200- w .writeByte ('+' ) catch return -1 ;
201- w .writeAll (slice ) catch return -1 ;
202- w .writeAll (color .reset ) catch return -1 ;
203- } else {
204- w .writeByte ('+' ) catch return -1 ;
205- w .writeAll (slice ) catch return -1 ;
206- }
207- },
208- '-' = > {
209- if (ctx .use_color ) {
210- w .writeAll (color .bright_red ) catch return -1 ;
211- w .writeByte ('-' ) catch return -1 ;
212- w .writeAll (slice ) catch return -1 ;
213- w .writeAll (color .reset ) catch return -1 ;
214- } else {
215- w .writeByte ('-' ) catch return -1 ;
216- w .writeAll (slice ) catch return -1 ;
217- }
218- },
219- ' ' = > {
220- if (ctx .use_color ) {
221- w .writeAll (color .dim ) catch return -1 ;
222- w .writeByte (' ' ) catch return -1 ;
223- w .writeAll (slice ) catch return -1 ;
224- w .writeAll (color .reset ) catch return -1 ;
225- } else {
226- w .writeByte (' ' ) catch return -1 ;
227- w .writeAll (slice ) catch return -1 ;
228- }
229- },
230- else = > {},
231- }
56+ try fmt .writeStat (diff_result , use_color , w );
57+ try w .writeByte ('\n ' );
23258
233- return 0 ;
59+ var ctx = fmt.HumanCtx { .writer = w , .use_color = use_color , .current_file = null };
60+ _ = c .git_diff_print (diff_result , c .GIT_DIFF_FORMAT_PATCH , fmt .humanCallback , @ptrCast (& ctx ));
23461}
0 commit comments