1- //! This module is primarily used to parse diff blobs.
1+ //! This module was primarily used to parse diff blobs.
22//!
3- //! It can also be used (locally) to get a list of files changes from either the last
4- //! commit or the next commit's staging area.
5- //!
6- //! This also includes a private module that is used as a fallback (brute force)
7- //! mechanism when parsing diffs fail using libgit2. NOTE: parsing a diff from a buffer
8- //! (str or bytes) only happens in CI or when libgit2 cannot be used to initialize a
9- //! repository.
10-
11- use std:: { ops:: RangeInclusive , path:: PathBuf } ;
12-
13- // non-std crates
14- use anyhow:: Result ;
15- use git2:: { Diff , Patch } ;
16-
17- // project specific modules/crates
18- use crate :: { cli:: LinesChangedOnly , common_fs:: FileObj } ;
19- use git_bot_feedback:: { FileFilter , error:: DiffError } ;
20-
21- /// Parses a patch for a single file in a diff.
22- ///
23- /// Returns the list of line numbers that have additions and the ranges spanning each
24- /// chunk present in the `patch`.
25- fn parse_patch ( patch : & Patch ) -> ( Vec < u32 > , Vec < RangeInclusive < u32 > > ) {
26- let mut additions = Vec :: new ( ) ;
27- let mut diff_hunks = Vec :: new ( ) ;
28- for hunk_idx in 0 ..patch. num_hunks ( ) {
29- let ( hunk, line_count) = patch. hunk ( hunk_idx) . unwrap ( ) ;
30- diff_hunks. push ( RangeInclusive :: new (
31- hunk. new_start ( ) ,
32- hunk. new_start ( ) + hunk. new_lines ( ) ,
33- ) ) ;
34- for line in 0 ..line_count {
35- let diff_line = patch. line_in_hunk ( hunk_idx, line) . unwrap ( ) ;
36- if diff_line. origin_value ( ) == git2:: DiffLineType :: Addition {
37- additions. push ( diff_line. new_lineno ( ) . unwrap ( ) ) ;
38- }
39- }
40- }
41- ( additions, diff_hunks)
42- }
43-
44- /// Parses a given [`git2::Diff`] and returns a list of [`FileObj`]s.
45- ///
46- /// The `lines_changed_only` parameter is used to expedite the process and only
47- /// focus on files that have relevant changes. The `file_filter` parameter applies
48- /// a filter to only include source files (or ignored files) based on the
49- /// extensions and ignore patterns specified.
50- pub fn parse_diff (
51- diff : & git2:: Diff ,
52- file_filter : & FileFilter ,
53- lines_changed_only : & LinesChangedOnly ,
54- ) -> Vec < FileObj > {
55- let mut files: Vec < FileObj > = Vec :: new ( ) ;
56- for file_idx in 0 ..diff. deltas ( ) . count ( ) {
57- let diff_delta = diff. get_delta ( file_idx) . unwrap ( ) ;
58- let file_path = diff_delta. new_file ( ) . path ( ) . unwrap ( ) . to_path_buf ( ) ;
59- if matches ! (
60- diff_delta. status( ) ,
61- git2:: Delta :: Added | git2:: Delta :: Modified | git2:: Delta :: Renamed ,
62- ) && file_filter. is_qualified ( & file_path)
63- {
64- let ( added_lines, diff_chunks) =
65- parse_patch ( & Patch :: from_diff ( diff, file_idx) . unwrap ( ) . unwrap ( ) ) ;
66- if lines_changed_only. is_change_valid ( !added_lines. is_empty ( ) , !diff_chunks. is_empty ( ) )
67- {
68- files. push ( FileObj :: from ( file_path, added_lines, diff_chunks) ) ;
69- }
70- }
71- }
72- files
73- }
74-
75- /// Same as [`parse_diff`] but takes a buffer of bytes instead of a [`git2::Diff`].
76- ///
77- /// In the case that libgit2 fails to parse the buffer of bytes, a private algorithm is
78- /// used. In such a case, brute force parsing the diff as a string can be costly. So, a
79- /// log warning and error are output when this occurs. Please report this instance for
80- /// troubleshooting/diagnosis as this likely means the diff is malformed or there is a
81- /// bug in libgit2 source.
82- pub fn parse_diff_from_buf (
83- buff : & [ u8 ] ,
84- file_filter : & FileFilter ,
85- lines_changed_only : & LinesChangedOnly ,
86- ) -> Result < Vec < FileObj > , DiffError > {
87- if let Ok ( diff_obj) = & Diff :: from_buffer ( buff) {
88- Ok ( parse_diff ( diff_obj, file_filter, lines_changed_only) )
89- } else {
90- log:: warn!( "libgit2 failed to parse the diff" ) ;
91- Ok ( git_bot_feedback:: parse_diff (
92- & String :: from_utf8_lossy ( buff) ,
93- file_filter,
94- & lines_changed_only. clone ( ) . into ( ) ,
95- ) ?
96- . iter ( )
97- . map ( |( name, diff_lines) | {
98- let diff_chunks = diff_lines
99- . diff_hunks
100- . iter ( )
101- . map ( |hunk| hunk. start ..=hunk. end )
102- . collect ( ) ;
103- FileObj :: from (
104- PathBuf :: from ( & name) ,
105- diff_lines. added_lines . clone ( ) ,
106- diff_chunks,
107- )
108- } )
109- . collect ( ) )
110- }
111- }
3+ //! Since migrating to git-bot-feedback crate, this module is now purely for regression testing.
4+ //! Any logic that parses diffs from 2 text blobs has been moved to git-bot-feedback.
5+ //! Some diff creation/parsing logic remains in clang_tools/mod.rs module using libgit2 API instead.
1126
1137#[ cfg( test) ]
1148mod test {
@@ -127,7 +21,6 @@ mod test {
12721
12822 // used to setup a testing stage
12923 fn clone_repo ( sha : Option < & str > , path : & str , patch_path : Option < & str > ) {
130- // let repo = Repository::clone(TEST_REPO_URL, path).unwrap();
13124 let ok = Command :: new ( "git" )
13225 . args ( [ "clone" , TEST_REPO_URL , path] )
13326 . status ( )
@@ -303,15 +196,6 @@ mod test {
303196 true ,
304197 )
305198 . await ;
306- Command :: new ( "git" )
307- . args ( [
308- "--no-pager" ,
309- "show" ,
310- "a2875ac00e6f1dc3eb4ac19712c7a241b5a76e83" ,
311- "--format=%b" ,
312- ] )
313- . status ( )
314- . unwrap ( ) ;
315199 eprintln ! ( "files: {files:?}" ) ;
316200 assert ! ( files. is_empty( ) ) ;
317201 set_current_dir ( cur_dir) . unwrap ( ) ; // prep to delete temp_folder
0 commit comments