From d776f2608c062cd60416aeb08106ca804df1d01d Mon Sep 17 00:00:00 2001 From: openhands Date: Thu, 20 Mar 2025 20:22:34 +0000 Subject: [PATCH] Fix issue #8: Multi path input for output compilation --- cmd/multi_path_test.go | 69 ++++++++++++ cmd/root.go | 249 ++++++++++++++++++++++------------------- test_repo1/file1.txt | 1 + test_repo2/file2.txt | 1 + 4 files changed, 204 insertions(+), 116 deletions(-) create mode 100644 cmd/multi_path_test.go create mode 100644 test_repo1/file1.txt create mode 100644 test_repo2/file2.txt diff --git a/cmd/multi_path_test.go b/cmd/multi_path_test.go new file mode 100644 index 0000000..7e17240 --- /dev/null +++ b/cmd/multi_path_test.go @@ -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") + } +} diff --git a/cmd/root.go b/cmd/root.go index 5f1aeb6..c7da231 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -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 @@ -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) + } } diff --git a/test_repo1/file1.txt b/test_repo1/file1.txt new file mode 100644 index 0000000..870c88c --- /dev/null +++ b/test_repo1/file1.txt @@ -0,0 +1 @@ +Test file 1 diff --git a/test_repo2/file2.txt b/test_repo2/file2.txt new file mode 100644 index 0000000..705e376 --- /dev/null +++ b/test_repo2/file2.txt @@ -0,0 +1 @@ +Test file 2