/
sondr
/
bxmake
Обзор
Документация
Войти
/
sondr
/
bxmake
Код
Запросы
0
Пакеты
0
Релизы
7
Аналитика
Безопасность
master
cmd/diff/diff.go
176 строк
5 KB
Denis Son
Initial commit
17 ноя 2025, 14:32
17 ноя 2025, 14:32
1a12c0b
Код
Авторство
О чём код?
package diff import ( "fmt" "os/exec" "strings" "github.com/spf13/cobra" "gitverse.ru/sondr/bxmake/internal/errors" "gitverse.ru/sondr/bxmake/internal/git" ) // diffCmd represents the diff command var diffCmd = &cobra.Command{ Use: "show-files [--from=COMMIT] [--to=COMMIT] [--summary]", Aliases: []string{"sf"}, Short: "Show list of files changed between commits/tags (alias: sf)", Long: `Shows list of files changed between two commits/tags. If no flags provided, shows changes since last Git tag to HEAD. FROM and TO can be commit hashes, branch names, or tag names. Use --summary to show statistics instead of file list.`, RunE: runDiff, } // GetDiffCmd returns the diff command func GetDiffCmd() *cobra.Command { return diffCmd } func init() { // Configure flags for diff command diffCmd.Flags().String("from", "", "Starting commit/tag/branch (default: latest tag)") diffCmd.Flags().String("to", "", "Ending commit/tag/branch (default: HEAD)") diffCmd.Flags().Bool("summary", false, "Show summary statistics instead of file list") } func runDiff(cmd *cobra.Command, args []string) error { // Check that positional arguments are not passed if len(args) > 0 { return errors.NewValidationError("diff command does not accept positional arguments, use --from and --to flags instead", nil) } // Check that we are in a git repository if !git.IsRepository() { return errors.NewConfigError("current directory is not a Git repository", nil) } repo, err := git.NewRepository() if err != nil { return errors.NewConfigError("failed to open Git repository", err) } // Get flag values fromFlag, _ := cmd.Flags().GetString("from") toFlag, _ := cmd.Flags().GetString("to") summaryFlag, _ := cmd.Flags().GetBool("summary") var from, to string // Determine commits for comparison based on flags if fromFlag != "" { from = fromFlag } else { // Default: latest tag latestTag, err := repo.GetLatestTag() if err != nil { return errors.NewConfigError("failed to get last tag", err) } from = latestTag } if toFlag != "" { to = toFlag } else { // Default: HEAD to = "HEAD" } // Get all changes between commits (show-files should show everything, not filtered) changes, err := repo.GetChangesBetweenCommits(from, to) if err != nil { return errors.NewConfigError("failed to get changes", err) } if len(changes) == 0 { fmt.Println("No changes found") return nil } // If --summary flag is specified, display statistics if summaryFlag { stats := git.GetDiffStats(changes) fmt.Printf("Changes between %s and %s:\n", from, to) fmt.Println() if stats.Added > 0 { fmt.Printf(" Added: %d files\n", stats.Added) } if stats.Modified > 0 { fmt.Printf(" Modified: %d files\n", stats.Modified) } if stats.Deleted > 0 { fmt.Printf(" Deleted: %d files\n", stats.Deleted) } if stats.Renamed > 0 { fmt.Printf(" Renamed: %d files\n", stats.Renamed) } if stats.Copied > 0 { fmt.Printf(" Copied: %d files\n", stats.Copied) } if stats.TypeChanged > 0 { fmt.Printf(" Type changed: %d files\n", stats.TypeChanged) } if stats.Unmerged > 0 { fmt.Printf(" Unmerged: %d files\n", stats.Unmerged) } if stats.Unknown > 0 { fmt.Printf(" Unknown: %d files\n", stats.Unknown) } fmt.Println() fmt.Printf("Total files changed: %d\n", stats.TotalFiles) return nil } // Sort changes by file name for i := 0; i < len(changes)-1; i++ { for j := i + 1; j < len(changes); j++ { if changes[i].Path > changes[j].Path { changes[i], changes[j] = changes[j], changes[i] } } } // Get the raw git diff --name-status output to preserve full format for renamed files gitCmd := exec.Command("git", "diff", "--name-status", from, to) gitCmd.Dir = "." // Ensure we run in current directory rawOutput, err := gitCmd.Output() if err != nil { return errors.NewConfigError(fmt.Sprintf("failed to get git diff output: %v", err), err) } rawLines := strings.Split(strings.TrimSpace(string(rawOutput)), "\n") // Display result in git diff --name-status format with color coding like 'show' command for _, rawLine := range rawLines { if rawLine == "" { continue } // Parse the line to determine color parts := strings.SplitN(rawLine, "\t", 2) if len(parts) < 1 { continue } status := parts[0] var colorCode string switch status[0] { case 'A': // Added colorCode = "\033[32m" // Green case 'M': // Modified colorCode = "\033[33m" // Yellow case 'D': // Deleted colorCode = "\033[31m" // Red default: // Other types (R, C, T, U, etc.) colorCode = "\033[34m" // Blue } fmt.Printf("%s%s\033[0m\n", colorCode, rawLine) } return nil }