|
1 | | -//! This module is primarily used to parse diff blobs. |
| 1 | +//! This module was primarily used to parse diff blobs. |
2 | 2 | //! |
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. |
112 | 6 |
|
113 | 7 | #[cfg(test)] |
114 | 8 | mod test { |
|
0 commit comments