Skip to content

Fix issue #8: Multi path input for output compilation #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions cmd/multi_path_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package cmd

import (
"bytes"
"io"
"os"
"path/filepath"
"strings"
"testing"
)

func TestMultiPathInput(t *testing.T) {
// Create temporary directories for test repositories
tempDir1, err := os.MkdirTemp("", "repo1")
if err != nil {
t.Fatalf("Failed to create temp directory: %v", err)
}
defer os.RemoveAll(tempDir1)

tempDir2, err := os.MkdirTemp("", "repo2")
if err != nil {
t.Fatalf("Failed to create temp directory: %v", err)
}
defer os.RemoveAll(tempDir2)

// Create test files in the first repository
file1Path := filepath.Join(tempDir1, "file1.txt")
err = os.WriteFile(file1Path, []byte("Content of file1"), 0644)
if err != nil {
t.Fatalf("Failed to write test file: %v", err)
}

// Create test files in the second repository
file2Path := filepath.Join(tempDir2, "file2.txt")
err = os.WriteFile(file2Path, []byte("Content of file2"), 0644)
if err != nil {
t.Fatalf("Failed to write test file: %v", err)
}

// Capture stdout
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w

// Set up command line arguments
oldArgs := os.Args
os.Args = []string{"git2gpt", tempDir1, tempDir2}

// Execute the command
Execute()

// Restore stdout and args
w.Close()
os.Stdout = oldStdout
os.Args = oldArgs

// Read captured output
var buf bytes.Buffer
io.Copy(&buf, r)
output := buf.String()

// Verify that both files are included in the output
if !strings.Contains(output, "file1.txt") || !strings.Contains(output, "Content of file1") {
t.Errorf("Output does not contain content from the first repository")
}
if !strings.Contains(output, "file2.txt") || !strings.Contains(output, "Content of file2") {
t.Errorf("Output does not contain content from the second repository")
}
}
249 changes: 133 additions & 116 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package cmd

import (
"fmt"
"os"
"fmt"
"os"

"github.com/chand1012/git2gpt/prompt"
"github.com/spf13/cobra"
"github.com/chand1012/git2gpt/prompt"
"github.com/spf13/cobra"
)

var repoPath string
Expand All @@ -20,122 +20,139 @@ var debug bool
var scrubComments bool

var rootCmd = &cobra.Command{
Use: "git2gpt [flags] /path/to/git/repository",
Short: "git2gpt is a utility to convert a Git repository to a text file for input into GPT-4",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
repoPath = args[0]
ignoreList := prompt.GenerateIgnoreList(repoPath, ignoreFilePath, !ignoreGitignore)
repo, err := prompt.ProcessGitRepo(repoPath, ignoreList)
if err != nil {
fmt.Printf("Error: %s\n", err)
os.Exit(1)
}
if outputJSON {
output, err := prompt.MarshalRepo(repo, scrubComments)
if err != nil {
fmt.Printf("Error: %s\n", err)
os.Exit(1)
}
if outputFile != "" {
// if output file exists, throw error
if _, err := os.Stat(outputFile); err == nil {
fmt.Printf("Error: output file %s already exists\n", outputFile)
os.Exit(1)
}
err = os.WriteFile(outputFile, []byte(output), 0644)
if err != nil {
fmt.Printf("Error: could not write to output file %s\n", outputFile)
os.Exit(1)
}
} else {
if !debug {
fmt.Println(string(output))
}
}
return
}
if outputXML {
output, err := prompt.OutputGitRepoXML(repo, scrubComments)
if err != nil {
fmt.Printf("Error: %s\n", err)
os.Exit(1)
}

// Validate the XML output
if err := prompt.ValidateXML(output); err != nil {
fmt.Printf("Error: %s\n", err)
os.Exit(1)
}

if outputFile != "" {
// if output file exists, throw error
if _, err := os.Stat(outputFile); err == nil {
fmt.Printf("Error: output file %s already exists\n", outputFile)
os.Exit(1)
}
err = os.WriteFile(outputFile, []byte(output), 0644)
if err != nil {
fmt.Printf("Error: could not write to output file %s\n", outputFile)
os.Exit(1)
}
} else {
if !debug {
fmt.Println(output)
}
}
return
}
output, err := prompt.OutputGitRepo(repo, preambleFile, scrubComments)
if err != nil {
fmt.Printf("Error: %s\n", err)
os.Exit(1)
}
if outputFile != "" {
// if output file exists, throw error
if _, err := os.Stat(outputFile); err == nil {
fmt.Printf("Error: output file %s already exists\n", outputFile)
os.Exit(1)
}
err = os.WriteFile(outputFile, []byte(output), 0644)
if err != nil {
fmt.Printf("Error: could not write to output file %s\n", outputFile)
os.Exit(1)
}
} else {
if !debug {
fmt.Println(output)
}
}
if estimateTokens {
fmt.Printf("Estimated number of tokens: %d\n", prompt.EstimateTokens(output))
}
},
Use: "git2gpt [flags] /path/to/git/repository [/path/to/another/repository ...]",
Short: "git2gpt is a utility to convert one or more Git repositories to a text file for input into GPT-4",
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
// Create a combined repository to hold all files
combinedRepo := &prompt.GitRepo{
Files: []prompt.GitFile{},
}

// Process each repository path
for _, path := range args {
repoPath = path
ignoreList := prompt.GenerateIgnoreList(repoPath, ignoreFilePath, !ignoreGitignore)
repo, err := prompt.ProcessGitRepo(repoPath, ignoreList)
if err != nil {
fmt.Printf("Error processing %s: %s\n", repoPath, err)
os.Exit(1)
}

// Add files from this repo to the combined repo
combinedRepo.Files = append(combinedRepo.Files, repo.Files...)
}

// Update the file count
combinedRepo.FileCount = len(combinedRepo.Files)
if outputJSON {
output, err := prompt.MarshalRepo(combinedRepo, scrubComments)
if err != nil {
fmt.Printf("Error: %s\n", err)
os.Exit(1)
}
if outputFile != "" {
// if output file exists, throw error
if _, err := os.Stat(outputFile); err == nil {
fmt.Printf("Error: output file %s already exists\n", outputFile)
os.Exit(1)
}
err = os.WriteFile(outputFile, []byte(output), 0644)
if err != nil {
fmt.Printf("Error: could not write to output file %s\n", outputFile)
os.Exit(1)
}
} else {
if !debug {
fmt.Println(string(output))
}
}
return
}
if outputXML {
output, err := prompt.OutputGitRepoXML(combinedRepo, scrubComments)
if err != nil {
fmt.Printf("Error: %s\n", err)
os.Exit(1)
}

// Validate the XML output
if err := prompt.ValidateXML(output); err != nil {
fmt.Printf("Error: %s\n", err)
os.Exit(1)
}

if outputFile != "" {
// if output file exists, throw error
if _, err := os.Stat(outputFile); err == nil {
fmt.Printf("Error: output file %s already exists\n", outputFile)
os.Exit(1)
}
err = os.WriteFile(outputFile, []byte(output), 0644)
if err != nil {
fmt.Printf("Error: could not write to output file %s\n", outputFile)
os.Exit(1)
}
} else {
if !debug {
fmt.Println(output)
}
}
return
}
output, err := prompt.OutputGitRepo(combinedRepo, preambleFile, scrubComments)
if err != nil {
fmt.Printf("Error: %s\n", err)
os.Exit(1)
}
if outputFile != "" {
// if output file exists, throw error
if _, err := os.Stat(outputFile); err == nil {
fmt.Printf("Error: output file %s already exists\n", outputFile)
os.Exit(1)
}
err = os.WriteFile(outputFile, []byte(output), 0644)
if err != nil {
fmt.Printf("Error: could not write to output file %s\n", outputFile)
os.Exit(1)
}
} else {
if !debug {
fmt.Println(output)
}
}
if estimateTokens {
fmt.Printf("Estimated number of tokens: %d\n", prompt.EstimateTokens(output))
}
},
}

func init() {
rootCmd.Flags().StringVarP(&preambleFile, "preamble", "p", "", "path to preamble text file")
// output to file flag. Should be a string
rootCmd.Flags().StringVarP(&outputFile, "output", "o", "", "path to output file")
// estimate tokens. Should be a bool
rootCmd.Flags().BoolVarP(&estimateTokens, "estimate", "e", false, "estimate the number of tokens in the output")
// ignore file path. Should be a string
rootCmd.Flags().StringVarP(&ignoreFilePath, "ignore", "i", "", "path to .gptignore file")
// ignore gitignore. Should be a bool
rootCmd.Flags().BoolVarP(&ignoreGitignore, "ignore-gitignore", "g", false, "ignore .gitignore file")
// output JSON. Should be a bool
rootCmd.Flags().BoolVarP(&outputJSON, "json", "j", false, "output JSON")
// output XML. Should be a bool
rootCmd.Flags().BoolVarP(&outputXML, "xml", "x", false, "output XML")
// debug. Should be a bool
rootCmd.Flags().BoolVarP(&debug, "debug", "d", false, "debug mode. Do not output to standard output")
// scrub comments. Should be a bool
rootCmd.Flags().BoolVarP(&scrubComments, "scrub-comments", "s", false, "scrub comments from the output. Decreases token count")
rootCmd.Flags().StringVarP(&preambleFile, "preamble", "p", "", "path to preamble text file")
// output to file flag. Should be a string
rootCmd.Flags().StringVarP(&outputFile, "output", "o", "", "path to output file")
// estimate tokens. Should be a bool
rootCmd.Flags().BoolVarP(&estimateTokens, "estimate", "e", false, "estimate the number of tokens in the output")
// ignore file path. Should be a string
rootCmd.Flags().StringVarP(&ignoreFilePath, "ignore", "i", "", "path to .gptignore file")
// ignore gitignore. Should be a bool
rootCmd.Flags().BoolVarP(&ignoreGitignore, "ignore-gitignore", "g", false, "ignore .gitignore file")
// output JSON. Should be a bool
rootCmd.Flags().BoolVarP(&outputJSON, "json", "j", false, "output JSON")
// output XML. Should be a bool
rootCmd.Flags().BoolVarP(&outputXML, "xml", "x", false, "output XML")
// debug. Should be a bool
rootCmd.Flags().BoolVarP(&debug, "debug", "d", false, "debug mode. Do not output to standard output")
// scrub comments. Should be a bool
rootCmd.Flags().BoolVarP(&scrubComments, "scrub-comments", "s", false, "scrub comments from the output. Decreases token count")

// Update the example usage to show multiple paths
rootCmd.Example = " git2gpt /path/to/repo1 /path/to/repo2\n git2gpt -o output.txt /path/to/repo1 /path/to/repo2"
}

func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
1 change: 1 addition & 0 deletions test_repo1/file1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Test file 1
1 change: 1 addition & 0 deletions test_repo2/file2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Test file 2