@@ -2,7 +2,6 @@ package parser
22
33import (
44 "bufio"
5- "bytes"
65 "fmt"
76 "os/exec"
87 "path/filepath"
@@ -39,100 +38,86 @@ func NewGitParser() *GitParser {
3938func (p * GitParser ) ParseStagedChanges () ([]* Change , error ) {
4039 // Use git status --porcelain for more accurate file state detection
4140 cmd := exec .Command ("git" , "status" , "--porcelain" )
42- var out bytes.Buffer
43- cmd .Stdout = & out
44- err := cmd .Run ()
41+ stdout , err := cmd .StdoutPipe ()
4542 if err != nil {
46- return nil , fmt .Errorf ("error running git status --porcelain: %w" , err )
43+ return nil , fmt .Errorf ("error creating stdout pipe for git status: %w" , err )
44+ }
45+
46+ if err := cmd .Start (); err != nil {
47+ return nil , fmt .Errorf ("error starting git status: %w" , err )
4748 }
4849
4950 var changes []* Change
50- scanner := bufio .NewScanner (& out )
51+ scanner := bufio .NewScanner (stdout )
5152 for scanner .Scan () {
5253 line := scanner .Text ()
5354 if len (line ) < 3 {
5455 continue
5556 }
5657
5758 // Porcelain format: XY filename
58- // X = staged status, Y = unstaged status
5959 stagedStatus := line [0 :1 ]
60- // unstagedStatus := line[1:2]
6160 filename := strings .TrimSpace (line [3 :])
6261
63- // Skip if not staged (staged status is space)
62+ // Skip if not staged
6463 if stagedStatus == " " || stagedStatus == "?" {
6564 continue
6665 }
6766
68- // Map porcelain status to action
6967 action := stagedStatus
70- switch stagedStatus {
71- case "M" :
72- action = "M" // Modified
73- case "A" :
74- action = "A" // Added
75- case "D" :
76- action = "D" // Deleted
77- case "R" :
78- action = "R" // Renamed
79- case "C" :
80- action = "C" // Copied
81- }
82-
8368 change := & Change {
8469 File : filename ,
8570 Action : action ,
8671 FileExtension : getFileExtension (filename ),
8772 }
8873
89- // Handle renames and copies (format: "R oldname -> newname")
74+ // Handle renames and copies
9075 if action == "R" || action == "C" {
9176 parts := strings .Split (filename , " -> " )
9277 if len (parts ) == 2 {
9378 change .IsRename = action == "R"
9479 change .IsCopy = action == "C"
9580 change .Source = strings .TrimSpace (parts [0 ])
9681 change .Target = strings .TrimSpace (parts [1 ])
97- change .File = change .Target // Use the new name as the file
82+ change .File = change .Target
9883 change .FileExtension = getFileExtension (change .Target )
9984 }
10085 }
10186
102- // Get the diff for the file
87+ // Get the diff for the file using streaming
10388 diffCmd := exec .Command ("git" , "diff" , "--cached" , "-U0" , "--" , change .File )
104- var diffOut bytes. Buffer
105- diffCmd . Stdout = & diffOut
106- err := diffCmd .Run ()
107- if err != nil && action != "D" {
108- // For deleted files, diff may fail, which is expected
109- return nil , fmt . Errorf ( "error running git diff for %s: %w" , change . File , err )
110- }
111- change . Diff = diffOut . String ()
112-
113- // Count added and removed lines
114- diffScanner := bufio . NewScanner ( strings . NewReader ( change .Diff ))
115- for diffScanner . Scan () {
116- diffLine := diffScanner . Text ( )
117- if strings . HasPrefix ( diffLine , "+" ) && ! strings . HasPrefix ( diffLine , "+++" ) {
118- change . Added ++
119- } else if strings . HasPrefix ( diffLine , "-" ) && ! strings . HasPrefix ( diffLine , "---" ) {
120- change . Removed ++
89+ diffStdout , err := diffCmd . StdoutPipe ()
90+ if err == nil {
91+ if err := diffCmd .Start (); err == nil {
92+ diffScanner := bufio . NewScanner ( diffStdout )
93+ var diffBuilder strings. Builder
94+ for diffScanner . Scan () {
95+ diffLine := diffScanner . Text ()
96+ if strings . HasPrefix ( diffLine , "+" ) && ! strings . HasPrefix ( diffLine , "+++" ) {
97+ change . Added ++
98+ } else if strings . HasPrefix ( diffLine , "-" ) && ! strings . HasPrefix ( diffLine , "---" ) {
99+ change .Removed ++
100+ }
101+ diffBuilder . WriteString ( diffLine )
102+ diffBuilder . WriteString ( " \n " )
103+ }
104+ change . Diff = diffBuilder . String ()
105+ diffCmd . Wait ()
121106 }
122107 }
108+
123109 p .TotalAdded += change .Added
124110 p .TotalRemoved += change .Removed
125111
126- // Detect large changes
127112 if (change .Added + change .Removed ) >= 500 {
128113 change .IsMajor = true
129114 }
130115
131116 changes = append (changes , change )
132117 }
133118
134- if err := scanner . Err (); err != nil {
135- return nil , fmt .Errorf ("error scanning git status output : %w" , err )
119+ if err := cmd . Wait (); err != nil {
120+ return nil , fmt .Errorf ("error waiting for git status: %w" , err )
136121 }
137122
138123 return changes , nil
@@ -141,13 +126,26 @@ func (p *GitParser) ParseStagedChanges() ([]*Change, error) {
141126// GetCurrentBranch returns the name of the current git branch
142127func (p * GitParser ) GetCurrentBranch () (string , error ) {
143128 cmd := exec .Command ("git" , "rev-parse" , "--abbrev-ref" , "HEAD" )
144- var out bytes.Buffer
145- cmd .Stdout = & out
146- err := cmd .Run ()
129+ stdout , err := cmd .StdoutPipe ()
147130 if err != nil {
148- return "" , fmt .Errorf ("error getting current branch : %w" , err )
131+ return "" , fmt .Errorf ("error creating stdout pipe for rev-parse : %w" , err )
149132 }
150- return strings .TrimSpace (out .String ()), nil
133+
134+ if err := cmd .Start (); err != nil {
135+ return "" , fmt .Errorf ("error starting rev-parse: %w" , err )
136+ }
137+
138+ var branch string
139+ scanner := bufio .NewScanner (stdout )
140+ if scanner .Scan () {
141+ branch = strings .TrimSpace (scanner .Text ())
142+ }
143+
144+ if err := cmd .Wait (); err != nil {
145+ return "" , fmt .Errorf ("error waiting for rev-parse: %w" , err )
146+ }
147+
148+ return branch , nil
151149}
152150
153151// getFileExtension returns the file extension of a given file path
0 commit comments