@@ -1725,9 +1725,9 @@ impl GitChain {
1725
1725
parent_branch : & str ,
1726
1726
branch_name : & str ,
1727
1727
) -> Result < Vec < MergeCommitInfo > , Error > {
1728
- // Get the latest commit on the branch
1728
+ // 1. Fetch all commit hashes for the given `branch_name`.
1729
1729
let mut command = Command :: new ( "git" ) ;
1730
- command. args ( [ "log" , "--oneline " , "-1" , branch_name] ) ;
1730
+ command. args ( [ "log" , "--format=%H " , branch_name] ) ; // Get only commit hashes
1731
1731
let output = match command. output ( ) {
1732
1732
Ok ( output) => output,
1733
1733
Err ( _) => return Ok ( vec ! [ ] ) , // Return empty vec on error
@@ -1737,92 +1737,101 @@ impl GitChain {
1737
1737
return Ok ( vec ! [ ] ) ;
1738
1738
}
1739
1739
1740
- let latest_commit = String :: from_utf8_lossy ( & output. stdout ) . trim ( ) . to_string ( ) ;
1741
- if latest_commit. is_empty ( ) {
1742
- return Ok ( vec ! [ ] ) ;
1743
- }
1740
+ let commit_hashes_str = String :: from_utf8_lossy ( & output. stdout ) ;
1741
+ let commit_hashes: Vec < & str > = commit_hashes_str. trim ( ) . lines ( ) . collect ( ) ;
1744
1742
1745
- // Check if it's a merge commit by looking for parent commits
1746
- let commit_hash = latest_commit. split_whitespace ( ) . next ( ) . unwrap_or ( "" ) ;
1747
- if commit_hash. is_empty ( ) {
1743
+ if commit_hashes. is_empty ( ) {
1748
1744
return Ok ( vec ! [ ] ) ;
1749
1745
}
1750
1746
1751
- // Get commit information
1752
- let mut command = Command :: new ( "git" ) ;
1753
- command. args ( [ "show" , "--stat" , commit_hash] ) ;
1754
- let output = match command. output ( ) {
1755
- Ok ( output) => output,
1756
- Err ( _) => return Ok ( vec ! [ ] ) ,
1757
- } ;
1747
+ let mut merge_commits_info = Vec :: new ( ) ;
1758
1748
1759
- if !output. status . success ( ) {
1760
- return Ok ( vec ! [ ] ) ;
1761
- }
1749
+ // 2. Iterate through each commit hash.
1750
+ for commit_hash in commit_hashes {
1751
+ if commit_hash. is_empty ( ) {
1752
+ continue ;
1753
+ }
1754
+
1755
+ // 3. For each commit, use `git show --stat <commit_hash>` to get its information.
1756
+ let mut show_command = Command :: new ( "git" ) ;
1757
+ show_command. args ( [ "show" , "--stat" , commit_hash] ) ;
1758
+ let show_output = match show_command. output ( ) {
1759
+ Ok ( output) => output,
1760
+ Err ( _) => continue , // Skip this commit on error
1761
+ } ;
1762
+
1763
+ if !show_output. status . success ( ) {
1764
+ continue ; // Skip this commit on error
1765
+ }
1762
1766
1763
- let commit_info = String :: from_utf8_lossy ( & output. stdout ) . to_string ( ) ;
1767
+ let commit_info_str = String :: from_utf8_lossy ( & show_output. stdout ) . to_string ( ) ;
1768
+ let commit_lines: Vec < & str > = commit_info_str. lines ( ) . collect ( ) ;
1764
1769
1765
- // Check if it's a merge commit, which typically contains "Merge" in the commit message
1766
- if commit_info. contains ( & format ! ( "Merge branch '{}'" , parent_branch) )
1767
- || commit_info. contains ( "Merge branch" )
1768
- {
1769
- // Extract commit message (first line after commit hash)
1770
- let commit_lines: Vec < & str > = commit_info. lines ( ) . collect ( ) ;
1771
- let message = commit_lines
1770
+ // 4. If the commit is a merge commit (contains "Merge branch" in its message AND refers to `parent_branch`),
1771
+ // extract its message and stats.
1772
+ let merge_line_prefix = format ! ( "Merge branch '{}'" , parent_branch) ;
1773
+ let is_merge_from_parent = commit_lines
1772
1774
. iter ( )
1773
- . position ( |line| line. trim ( ) . starts_with ( "Merge branch" ) )
1774
- . map ( |idx| commit_lines[ idx] . trim ( ) . to_string ( ) ) ;
1775
+ . any ( |line| line. trim ( ) . starts_with ( & merge_line_prefix) ) ;
1775
1776
1776
- // Extract stats
1777
- let stats_line = commit_lines
1777
+ // Also check for generic "Merge branch" if parent_branch is empty (e.g. for initial merge to main)
1778
+ // or if the specific format isn't exact.
1779
+ let is_generic_merge = commit_lines
1778
1780
. iter ( )
1779
- . find ( |line| line. contains ( "files changed" ) || line . contains ( "file changed ") ) ;
1781
+ . any ( |line| line. trim ( ) . starts_with ( "Merge branch ") ) ;
1780
1782
1781
- let stats = stats_line. map ( |line| {
1782
- let mut files_changed = 0 ;
1783
- let mut insertions = 0 ;
1784
- let mut deletions = 0 ;
1783
+ // A merge commit must have a line starting with "Merge: "
1784
+ let is_actual_merge_commit = commit_lines. iter ( ) . any ( |line| line. starts_with ( "Merge: " ) ) ;
1785
1785
1786
- if let Some ( files_idx) = line. find ( "file changed" ) {
1787
- if let Some ( files_num) = line[ ..files_idx] . split_whitespace ( ) . last ( ) {
1788
- files_changed = files_num. parse ( ) . unwrap_or ( 0 ) ;
1789
- }
1790
- } else if let Some ( files_idx) = line. find ( "files changed" ) {
1791
- if let Some ( files_num) = line[ ..files_idx] . split_whitespace ( ) . last ( ) {
1792
- files_changed = files_num. parse ( ) . unwrap_or ( 0 ) ;
1793
- }
1794
- }
1786
+ if is_actual_merge_commit && ( is_merge_from_parent || ( parent_branch. is_empty ( ) && is_generic_merge) ) {
1787
+ // Extract commit message (the line starting with "Merge branch ...")
1788
+ let message = commit_lines
1789
+ . iter ( )
1790
+ . find ( |line| line. trim ( ) . starts_with ( "Merge branch" ) )
1791
+ . map ( |line| line. trim ( ) . to_string ( ) ) ;
1795
1792
1796
- if let Some ( ins_idx) = line. find ( "insertion" ) {
1797
- if let Some ( ins_end) = line[ ..ins_idx] . rfind ( ' ' ) {
1798
- if let Some ( ins_start) = line[ ..ins_end] . rfind ( ' ' ) {
1799
- let ins_str = & line[ ins_start + 1 ..ins_end] ;
1800
- insertions = ins_str. parse ( ) . unwrap_or ( 0 ) ;
1793
+ // Extract stats (preserve existing logic, with slight refinement for parsing)
1794
+ let stats_line = commit_lines
1795
+ . iter ( )
1796
+ . find ( |line| line. contains ( "files changed" ) || line. contains ( "file changed" ) ) ;
1797
+
1798
+ let stats = stats_line. map ( |line| {
1799
+ let mut files_changed = 0 ;
1800
+ let mut insertions = 0 ;
1801
+ let mut deletions = 0 ;
1802
+
1803
+ // Example line: "1 file changed, 5 insertions(+), 2 deletions(-)"
1804
+ // More robust parsing:
1805
+ let parts: Vec < & str > = line. split ( ',' ) . map ( |s| s. trim ( ) ) . collect ( ) ;
1806
+ for part in parts {
1807
+ if part. contains ( "file changed" ) || part. contains ( "files changed" ) {
1808
+ if let Some ( num_str) = part. split_whitespace ( ) . next ( ) {
1809
+ files_changed = num_str. parse ( ) . unwrap_or ( 0 ) ;
1810
+ }
1811
+ } else if part. contains ( "insertion" ) {
1812
+ if let Some ( num_str) = part. split_whitespace ( ) . next ( ) {
1813
+ insertions = num_str. parse ( ) . unwrap_or ( 0 ) ;
1814
+ }
1815
+ } else if part. contains ( "deletion" ) {
1816
+ if let Some ( num_str) = part. split_whitespace ( ) . next ( ) {
1817
+ deletions = num_str. parse ( ) . unwrap_or ( 0 ) ;
1818
+ }
1801
1819
}
1802
1820
}
1803
- }
1804
-
1805
- if let Some ( del_idx) = line. find ( "deletion" ) {
1806
- if let Some ( del_end) = line[ ..del_idx] . rfind ( ' ' ) {
1807
- if let Some ( del_start) = line[ ..del_end] . rfind ( ' ' ) {
1808
- let del_str = & line[ del_start + 1 ..del_end] ;
1809
- deletions = del_str. parse ( ) . unwrap_or ( 0 ) ;
1810
- }
1821
+ MergeStats {
1822
+ files_changed,
1823
+ insertions,
1824
+ deletions,
1811
1825
}
1812
- }
1826
+ } ) ;
1813
1827
1814
- MergeStats {
1815
- files_changed,
1816
- insertions,
1817
- deletions,
1818
- }
1819
- } ) ;
1820
-
1821
- return Ok ( vec ! [ MergeCommitInfo { message, stats } ] ) ;
1828
+ merge_commits_info. push ( MergeCommitInfo { message, stats } ) ;
1829
+ }
1822
1830
}
1823
1831
1824
- // It's not a merge commit
1825
- Ok ( vec ! [ ] )
1832
+ // 5. Collect all `MergeCommitInfo` structs for the identified merge commits.
1833
+ // 6. Return `Ok(Vec<MergeCommitInfo>)`
1834
+ Ok ( merge_commits_info)
1826
1835
}
1827
1836
1828
1837
fn report_merge_results (
0 commit comments