@@ -22,6 +22,107 @@ func IsRepository(path string) bool {
2222 return strings .TrimSpace (string (output )) == "true"
2323}
2424
25+ // parseGitStatusLine represents a parsed git status line
26+ type parseGitStatusLine struct {
27+ status string
28+ filenames []string
29+ }
30+
31+ // parseGitNameStatus parses a single line from git diff --name-status output
32+ // Handles various git status codes including rename (R) and copy (C) operations
33+ func parseGitNameStatus (line string ) parseGitStatusLine {
34+ if line == "" {
35+ return parseGitStatusLine {}
36+ }
37+
38+ // Git uses tabs to separate fields in --name-status output
39+ parts := strings .Split (line , "\t " )
40+ if len (parts ) < 2 {
41+ return parseGitStatusLine {}
42+ }
43+
44+ status := parts [0 ]
45+
46+ // Handle rename/copy status codes (e.g., "R100", "C75")
47+ if len (status ) > 1 && (status [0 ] == 'R' || status [0 ] == 'C' ) {
48+ // For rename/copy, we expect: "R100\toldname\tnewname" or "C75\toldname\tnewname"
49+ if len (parts ) >= 3 {
50+ // For renames/copies, both old and new filenames need to be checked
51+ oldFile := parts [1 ]
52+ newFile := parts [2 ]
53+ return parseGitStatusLine {
54+ status : status ,
55+ filenames : []string {oldFile , newFile },
56+ }
57+ }
58+ }
59+
60+ // Handle regular status codes (M, A, D, etc.)
61+ filename := parts [1 ]
62+ return parseGitStatusLine {
63+ status : status ,
64+ filenames : []string {filename },
65+ }
66+ }
67+
68+ // processGitStatusOutput processes git diff --name-status output and returns filtered results
69+ func processGitStatusOutput (nameStatusOutput string , returnFilenames bool ) ([]string , []string ) {
70+ if nameStatusOutput == "" {
71+ return nil , nil
72+ }
73+
74+ lines := strings .Split (strings .TrimSpace (nameStatusOutput ), "\n " )
75+ var filteredLines []string
76+ var nonBinaryFiles []string
77+
78+ for _ , line := range lines {
79+ if line == "" {
80+ continue
81+ }
82+
83+ parsed := parseGitNameStatus (line )
84+ if len (parsed .filenames ) == 0 {
85+ continue
86+ }
87+
88+ // Check if any of the filenames are binary
89+ hasBinaryFile := false
90+ for _ , filename := range parsed .filenames {
91+ if utils .IsBinaryFile (filename ) {
92+ hasBinaryFile = true
93+ break
94+ }
95+ }
96+
97+ // If no binary files found, include this line/files
98+ if ! hasBinaryFile {
99+ filteredLines = append (filteredLines , line )
100+ if returnFilenames {
101+ nonBinaryFiles = append (nonBinaryFiles , parsed .filenames ... )
102+ }
103+ }
104+ }
105+
106+ return filteredLines , nonBinaryFiles
107+ }
108+
109+ // filterBinaryFiles filters out binary files from git diff --name-status output
110+ func filterBinaryFiles (nameStatusOutput string ) string {
111+ filteredLines , _ := processGitStatusOutput (nameStatusOutput , false )
112+
113+ if len (filteredLines ) == 0 {
114+ return ""
115+ }
116+
117+ return strings .Join (filteredLines , "\n " )
118+ }
119+
120+ // extractNonBinaryFiles extracts non-binary filenames from git diff --name-status output
121+ func extractNonBinaryFiles (nameStatusOutput string ) []string {
122+ _ , nonBinaryFiles := processGitStatusOutput (nameStatusOutput , true )
123+ return nonBinaryFiles
124+ }
125+
25126// GetChanges retrieves all Git changes including staged, unstaged, and untracked files
26127func GetChanges (config * types.RepoConfig ) (string , error ) {
27128 var changes strings.Builder
@@ -34,20 +135,29 @@ func GetChanges(config *types.RepoConfig) (string, error) {
34135 }
35136
36137 if len (output ) > 0 {
37- changes .WriteString ("Unstaged changes:\n " )
38- changes .WriteString (string (output ))
39- changes .WriteString ("\n \n " )
40-
41- // Get the content of these changes
42- diffCmd := exec .Command ("git" , "-C" , config .Path , "diff" )
43- diffOutput , err := diffCmd .Output ()
44- if err != nil {
45- return "" , fmt .Errorf ("git diff content failed: %v" , err )
46- }
138+ // Filter out binary files from the name-status output
139+ filteredOutput := filterBinaryFiles (string (output ))
140+
141+ if filteredOutput != "" {
142+ changes .WriteString ("Unstaged changes:\n " )
143+ changes .WriteString (filteredOutput )
144+ changes .WriteString ("\n \n " )
145+
146+ // Get the content of these changes (only for non-binary files)
147+ nonBinaryFiles := extractNonBinaryFiles (string (output ))
148+ if len (nonBinaryFiles ) > 0 {
149+ diffCmd := exec .Command ("git" , "-C" , config .Path , "diff" , "--" )
150+ diffCmd .Args = append (diffCmd .Args , nonBinaryFiles ... )
151+ diffOutput , err := diffCmd .Output ()
152+ if err != nil {
153+ return "" , fmt .Errorf ("git diff content failed: %v" , err )
154+ }
47155
48- changes .WriteString ("Unstaged diff content:\n " )
49- changes .WriteString (string (diffOutput ))
50- changes .WriteString ("\n \n " )
156+ changes .WriteString ("Unstaged diff content:\n " )
157+ changes .WriteString (string (diffOutput ))
158+ changes .WriteString ("\n \n " )
159+ }
160+ }
51161 }
52162
53163 // 2. Check for staged changes
@@ -58,20 +168,29 @@ func GetChanges(config *types.RepoConfig) (string, error) {
58168 }
59169
60170 if len (stagedOutput ) > 0 {
61- changes .WriteString ("Staged changes:\n " )
62- changes .WriteString (string (stagedOutput ))
63- changes .WriteString ("\n \n " )
64-
65- // Get the content of these changes
66- stagedDiffCmd := exec .Command ("git" , "-C" , config .Path , "diff" , "--cached" )
67- stagedDiffOutput , err := stagedDiffCmd .Output ()
68- if err != nil {
69- return "" , fmt .Errorf ("git diff --cached content failed: %v" , err )
70- }
171+ // Filter out binary files from the staged changes
172+ filteredStagedOutput := filterBinaryFiles (string (stagedOutput ))
173+
174+ if filteredStagedOutput != "" {
175+ changes .WriteString ("Staged changes:\n " )
176+ changes .WriteString (filteredStagedOutput )
177+ changes .WriteString ("\n \n " )
71178
72- changes .WriteString ("Staged diff content:\n " )
73- changes .WriteString (string (stagedDiffOutput ))
74- changes .WriteString ("\n \n " )
179+ // Get the content of these changes (only for non-binary files)
180+ nonBinaryStagedFiles := extractNonBinaryFiles (string (stagedOutput ))
181+ if len (nonBinaryStagedFiles ) > 0 {
182+ stagedDiffCmd := exec .Command ("git" , "-C" , config .Path , "diff" , "--cached" , "--" )
183+ stagedDiffCmd .Args = append (stagedDiffCmd .Args , nonBinaryStagedFiles ... )
184+ stagedDiffOutput , err := stagedDiffCmd .Output ()
185+ if err != nil {
186+ return "" , fmt .Errorf ("git diff --cached content failed: %v" , err )
187+ }
188+
189+ changes .WriteString ("Staged diff content:\n " )
190+ changes .WriteString (string (stagedDiffOutput ))
191+ changes .WriteString ("\n \n " )
192+ }
193+ }
75194 }
76195
77196 // 3. Check for untracked files
@@ -82,34 +201,44 @@ func GetChanges(config *types.RepoConfig) (string, error) {
82201 }
83202
84203 if len (untrackedOutput ) > 0 {
85- changes .WriteString ("Untracked files:\n " )
86- changes .WriteString (string (untrackedOutput ))
87- changes .WriteString ("\n \n " )
88-
89- // Try to get content of untracked files (limited to text files and smaller size)
204+ // Filter out binary files from untracked files
90205 untrackedFiles := strings .Split (strings .TrimSpace (string (untrackedOutput )), "\n " )
206+ var nonBinaryUntrackedFiles []string
207+
91208 for _ , file := range untrackedFiles {
92209 if file == "" {
93210 continue
94211 }
212+ if ! utils .IsBinaryFile (file ) {
213+ nonBinaryUntrackedFiles = append (nonBinaryUntrackedFiles , file )
214+ }
215+ }
216+
217+ if len (nonBinaryUntrackedFiles ) > 0 {
218+ changes .WriteString ("Untracked files:\n " )
219+ changes .WriteString (strings .Join (nonBinaryUntrackedFiles , "\n " ))
220+ changes .WriteString ("\n \n " )
95221
96- fullPath := filepath .Join (config .Path , file )
97- if utils .IsTextFile (fullPath ) && utils .IsSmallFile (fullPath ) {
98- fileContent , err := os .ReadFile (fullPath )
99- if err != nil {
100- // Log but don't fail - untracked file may have been deleted or is inaccessible
101- continue
102- }
103- changes .WriteString (fmt .Sprintf ("Content of new file %s:\n " , file ))
104-
105- // Use special scrubbing for .env files
106- if strings .HasSuffix (strings .ToLower (file ), ".env" ) ||
107- strings .Contains (strings .ToLower (file ), ".env." ) {
108- changes .WriteString (scrubber .ScrubEnvFile (string (fileContent )))
109- } else {
110- changes .WriteString (string (fileContent ))
222+ // Try to get content of untracked files (limited to text files and smaller size)
223+ for _ , file := range nonBinaryUntrackedFiles {
224+ fullPath := filepath .Join (config .Path , file )
225+ if utils .IsTextFile (fullPath ) && utils .IsSmallFile (fullPath ) {
226+ fileContent , err := os .ReadFile (fullPath )
227+ if err != nil {
228+ // Log but don't fail - untracked file may have been deleted or is inaccessible
229+ continue
230+ }
231+ changes .WriteString (fmt .Sprintf ("Content of new file %s:\n " , file ))
232+
233+ // Use special scrubbing for .env files
234+ if strings .HasSuffix (strings .ToLower (file ), ".env" ) ||
235+ strings .Contains (strings .ToLower (file ), ".env." ) {
236+ changes .WriteString (scrubber .ScrubEnvFile (string (fileContent )))
237+ } else {
238+ changes .WriteString (string (fileContent ))
239+ }
240+ changes .WriteString ("\n \n " )
111241 }
112- changes .WriteString ("\n \n " )
113242 }
114243 }
115244 }
0 commit comments