@@ -15,6 +15,10 @@ type parse_error = {
1515 (* TODO: add the start position of the error *)
1616}
1717
18+ type diff_kind =
19+ | Unified_diff
20+ | Context_diff
21+
1822exception Parse_error of parse_error
1923
2024let unified_diff ~mine_no_nl ~their_no_nl hunk =
@@ -129,21 +133,25 @@ let to_start_len data =
129133 (* TODO: investigate start line. This shouldn't be start - 1 but the output of diff is also inconsistent *)
130134 (st, len)
131135
132- let count_to_sl_sl data =
133- if String. is_prefix ~prefix: " @@ -" data then
134- (* input: "@@ -19,23 +19,12 @@ bla" *)
135- (* output: ((19,23), (19, 12)) *)
136- match List. filter (function "" -> false | _ -> true ) (String. cuts '@' data) with
137- | numbers ::_ ->
138- let nums = String. trim numbers in
139- (match String. cut ' ' nums with
140- | None -> invalid_arg " couldn't find space in count"
141- | Some (mine , theirs ) -> Some (to_start_len mine, to_start_len theirs))
142- | _ -> invalid_arg " broken line!"
143- else
144- None
136+ let count_to_sl_sl diff_kind data =
137+ match diff_kind with
138+ | Unified_diff ->
139+ if String. is_prefix ~prefix: " @@ -" data then
140+ (* input: "@@ -19,23 +19,12 @@ bla" *)
141+ (* output: ((19,23), (19, 12)) *)
142+ match List. filter (function "" -> false | _ -> true ) (String. cuts '@' data) with
143+ | numbers ::_ ->
144+ let nums = String. trim numbers in
145+ (match String. cut ' ' nums with
146+ | None -> invalid_arg " couldn't find space in count"
147+ | Some (mine , theirs ) -> Some (to_start_len mine, to_start_len theirs))
148+ | _ -> invalid_arg " broken line!"
149+ else
150+ None
151+ | Context_diff ->
152+ assert false
145153
146- let sort_into_bags ~counter :(mine_len , their_len ) dir mine their m_nl t_nl str =
154+ let sort_into_bags diff_kind ~counter :(mine_len , their_len ) dir mine their m_nl t_nl str =
147155 let both data =
148156 if m_nl || t_nl then
149157 failwith " \" no newline at the end of file\" is not at the end of the file" ;
@@ -157,26 +165,36 @@ let sort_into_bags ~counter:(mine_len, their_len) dir mine their m_nl t_nl str =
157165 None
158166 else if str_len = 0 then
159167 both " " (* NOTE: this should technically be a parse error but GNU patch accepts that and some patches in opam-repository do use this behaviour *)
160- else match String. get str 0 , String. slice ~start: 1 str with
161- | ' ' , data ->
168+ else
169+ let data =
170+ let start = match diff_kind with
171+ | Unified_diff -> 1
172+ | Context_diff when str_len = 1 -> failwith " bad context diff"
173+ | Context_diff when str.[1 ] = ' ' || str.[1 ] = '\t' -> 2
174+ | Context_diff -> 1
175+ in
176+ String. slice ~start str
177+ in
178+ match String. get str 0 , diff_kind with
179+ | ' ' , _ ->
162180 both data
163- | '\t' , data ->
181+ | '\t' , _ ->
164182 both (" \t " ^ data) (* NOTE: not valid but accepted by GNU patch *)
165- | '+' , data ->
183+ | '+' , _ ->
166184 if t_nl then
167185 failwith " \" no newline at the end of file\" is not at the end of the file" ;
168186 if their_len = 0 then
169187 failwith " invalid patch (+ size exhausted)" ;
170188 let counter = (mine_len, their_len - 1 ) in
171189 Some (counter, `Their , mine, (data :: their), m_nl, t_nl)
172- | '-' , data ->
190+ | '-' , _ | '!' , Context_diff ->
173191 if m_nl then
174192 failwith " \" no newline at the end of file\" is not at the end of the file" ;
175193 if mine_len = 0 then
176194 failwith " invalid patch (- size exhausted)" ;
177195 let counter = (mine_len - 1 , their_len) in
178196 Some (counter, `Mine , (data :: mine), their, m_nl, t_nl)
179- | '\\' , _data ->
197+ | '\\' , _ ->
180198 (* NOTE: Any line starting with '\' is taken as if it was
181199 '\ No newline at end of file' by GNU patch so we do the same *)
182200 (* diff: 'No newline at end of file' turns out to be context-sensitive *)
@@ -189,10 +207,10 @@ let sort_into_bags ~counter:(mine_len, their_len) dir mine their m_nl t_nl str =
189207 in
190208 let counter = (mine_len, their_len) in
191209 Some (counter, dir, mine, their, my_nl, their_nl)
192- | _ -> failwith " invalid patch (unknown character)"
210+ | _ , _ -> failwith " invalid patch (unknown character)"
193211
194- let to_hunk count data mine_no_nl their_no_nl =
195- match count_to_sl_sl count with
212+ let to_hunk diff_kind count data mine_no_nl their_no_nl =
213+ match count_to_sl_sl diff_kind count with
196214 | None -> None , mine_no_nl, their_no_nl, count :: data
197215 | Some ((mine_start , mine_len ), (their_start , their_len )) ->
198216 let counter = (mine_len, their_len) in
@@ -202,18 +220,22 @@ let to_hunk count data mine_no_nl their_no_nl =
202220 | [" " ] when counter = (2 , 2 ) -> (List. rev (" " :: " " :: mine), List. rev (" " :: " " :: their), mine_no_nl, their_no_nl, [] ) (* GNU patch behaviour *)
203221 | [" " ] when counter = (3 , 3 ) -> (List. rev (" " :: " " :: " " :: mine), List. rev (" " :: " " :: " " :: their), mine_no_nl, their_no_nl, [] ) (* GNU patch behaviour *)
204222 | [] | [" " ] -> failwith " bad file"
205- | x ::xs -> match sort_into_bags ~counter dir mine their mine_no_nl their_no_nl x with
223+ | x ::xs -> match sort_into_bags diff_kind ~counter dir mine their mine_no_nl their_no_nl x with
206224 | Some (counter , dir , mine , their , mine_no_nl' , their_no_nl' ) -> step ~counter dir mine their mine_no_nl' their_no_nl' xs
207225 | None -> (List. rev mine, List. rev their, mine_no_nl, their_no_nl, x :: xs)
208226 in
209227 let mine, their, mine_no_nl, their_no_nl, rest = step ~counter `Both [] [] mine_no_nl their_no_nl data in
210228 (Some { mine_start ; mine_len ; mine ; their_start ; their_len ; their }, mine_no_nl, their_no_nl, rest)
211229
212- let rec to_hunks (mine_no_nl , their_no_nl , acc ) = function
213- | [] -> (List. rev acc, mine_no_nl, their_no_nl, [] )
214- | count ::data -> match to_hunk count data mine_no_nl their_no_nl with
215- | None , mine_no_nl , their_no_nl , rest -> List. rev acc, mine_no_nl, their_no_nl, rest
216- | Some hunk , mine_no_nl , their_no_nl , rest -> to_hunks (mine_no_nl, their_no_nl, hunk :: acc) rest
230+ let rec to_hunks diff_kind (mine_no_nl , their_no_nl , acc ) l = match diff_kind, l with
231+ | _ , [] -> (List. rev acc, mine_no_nl, their_no_nl, [] )
232+ | Unified_diff , count ::data | Context_diff , "********" ::count ::data ->
233+ begin match to_hunk diff_kind count data mine_no_nl their_no_nl with
234+ | None , mine_no_nl , their_no_nl , rest -> List. rev acc, mine_no_nl, their_no_nl, rest
235+ | Some hunk , mine_no_nl , their_no_nl , rest -> to_hunks diff_kind (mine_no_nl, their_no_nl, hunk :: acc) rest
236+ end
237+ | Context_diff , _ ::xs ->
238+ to_hunks diff_kind (mine_no_nl, their_no_nl, acc) xs
217239
218240type operation =
219241 | Edit of string * string
@@ -348,13 +370,13 @@ let parse_one ~p data =
348370 in
349371 (* first locate --- and +++ lines *)
350372 let rec find_start ~mode ~git_action = function
351- | [] -> git_action, []
373+ | [] -> git_action, Unified_diff , []
352374 | x ::xs when String. is_prefix ~prefix: " diff --git " x ->
353375 let git_filename = Fname. parse_git_header (String. slice ~start: 11 x) in
354376 begin match mode, git_action with
355377 | (None | Some (Git _ )), None -> find_start ~mode: (Some (Git git_filename)) ~git_action: None xs
356378 | None , Some _ -> assert false (* impossible state *)
357- | Some (Git _ ), Some git_action -> (Some git_action, x :: xs)
379+ | Some (Git _ ), Some git_action -> (Some git_action, Unified_diff , x :: xs)
358380 end
359381 | x ::y ::xs when is_git mode && String. is_prefix ~prefix: " rename from " x && String. is_prefix ~prefix: " rename to " y ->
360382 let git_action = Some (Rename_only (String. slice ~start: 12 x, String. slice ~start: 10 y)) in
@@ -367,20 +389,21 @@ let parse_one ~p data =
367389 in
368390 find_start ~mode ~git_action xs
369391 | x ::y ::xs when String. is_prefix ~prefix: " --- " x && String. is_prefix ~prefix: " +++ " y ->
370- Some (operation_of_strings ~p x y), xs
371- | x ::y ::_xs when String. is_prefix ~prefix: " *** " x && String. is_prefix ~prefix: " --- " y ->
372- failwith " Context diffs are not supported "
392+ Some (operation_of_strings ~p x y), Unified_diff , xs
393+ | x ::y ::"********" :: xs when String. is_prefix ~prefix: " *** " x && String. is_prefix ~prefix: " --- " y ->
394+ Some (operation_of_strings ~p x y), Context_diff , xs
373395 | _ ::xs -> find_start ~mode ~git_action xs
374396 in
375397 match find_start ~mode: None ~git_action: None data with
376- | Some (Rename_only _ as operation ), rest ->
398+ | Some (Rename_only _ as operation ), _ , rest ->
399+ (* assert diff_kind = Unified_diff *)
377400 let hunks = [] and mine_no_nl = false and their_no_nl = false in
378401 Some ({ operation ; hunks ; mine_no_nl ; their_no_nl }, rest)
379- | Some operation , rest ->
380- let hunks, mine_no_nl, their_no_nl, rest = to_hunks (false , false , [] ) rest in
402+ | Some operation , diff_kind , rest ->
403+ let hunks, mine_no_nl, their_no_nl, rest = to_hunks diff_kind (false , false , [] ) rest in
381404 Some ({ operation ; hunks ; mine_no_nl ; their_no_nl }, rest)
382- | None , [] -> None
383- | None , _ -> assert false
405+ | None , _ , [] -> None
406+ | None , _ , _ -> assert false
384407
385408let to_lines = String. cuts '\n'
386409
0 commit comments