Help us shape the future of Go! Take the 2026 Go Developer Survey

git

package
v1.47.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 27, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Overview

Package git provides cobra commands for managing multiple git repositories in a development folder.

Index

Constants

This section is empty.

Variables

View Source
var BranchAllCmd = &cobra.Command{
	Use:   "branch-all",
	Short: "Show current branch of all git repositories in development folder",
	Long:  `This command shows the current branch for all git repositories found in your development folder.`,
	Run: func(cmd *cobra.Command, args []string) {
		log.Start("Checking current branch of all git repositories")

		setup, err := setupGitCommand(cmd)
		if err != nil {
			log.Error("%s", err)
			return
		}

		repos, err := findGitRepositories(setup.DevPath)
		if err != nil {
			log.Error("Failed to find git repositories: %s", err)
			return
		}

		if len(repos) == 0 {
			log.Warn("No git repositories found in %s", setup.DevPath)
			return
		}

		log.Info("Checking branches of %d repositories:", len(repos))

		mainCount := 0
		otherBranchCount := 0

		for _, repoPath := range repos {
			repoName := filepath.Base(repoPath)

			branch, err := repo.GetCurrentBranch(cmd.Context(), repoPath)
			if err != nil {
				log.Error("  %s: Failed to get current branch - %s", repoName, err)
				continue
			}

			mainBranch, err := repo.GetMainBranch(cmd.Context(), repoPath)
			if err != nil {
				log.Warn("  %s: Could not determine main branch - %s", repoName, err)
				mainBranch = "main"
			}

			if branch == mainBranch {
				log.Success("  %s: %s", repoName, branch)
				mainCount++
			} else {
				log.Warn("  %s: %s", repoName, branch)
				otherBranchCount++
			}
		}

		log.Info("Branch summary: %d on default branch, %d on other branches", mainCount, otherBranchCount)
	},
}

BranchAllCmd defines the cobra command for showing current branch of all git repositories. It displays the current branch for all repositories in the development folder.

View Source
var CleanAllCmd = &cobra.Command{
	Use:   "clean-all",
	Short: "Clean untracked files in all git repositories in development folder",
	Long:  `This command removes untracked files and directories for all git repositories found in your development folder.`,
	Run: func(cmd *cobra.Command, _args []string) {
		printHeader("🧹 Clean Development Repositories")

		setup, err := setupGitCommand(cmd)
		if err != nil {
			log.Error("%s", err)
			return
		}

		force, _ := cmd.Flags().GetBool("force")
		directories, _ := cmd.Flags().GetBool("directories")

		var scanSpinner *ui.Spinner
		if !ui.DisableProgress {
			scanSpinner = ui.NewSpinner(fmt.Sprintf(
				"Scanning git repositories in %s for untracked files...",
				setup.DevPath,
			))
		}

		repos, err := findGitRepositories(setup.DevPath)
		if err != nil {
			if scanSpinner != nil {
				scanSpinner.Stop()
			}
			log.Error("Failed to find git repositories: %s", err)
			return
		}

		type RepoCleanSummary struct {
			Path           string
			Name           string
			UntrackedCount int
			SizeBytes      int64
		}

		var reposToClean []RepoCleanSummary
		var totalUntrackedBytes int64

		for _, repoPath := range repos {
			untrackedFiles, sizeBytes := getUntrackedFilesAndSize(repoPath)
			if len(untrackedFiles) > 0 {
				repoName := filepath.Base(repoPath)
				reposToClean = append(reposToClean, RepoCleanSummary{
					Path:           repoPath,
					Name:           repoName,
					UntrackedCount: len(untrackedFiles),
					SizeBytes:      sizeBytes,
				})
				totalUntrackedBytes += sizeBytes
			}
		}

		if scanSpinner != nil {
			scanSpinner.Stop()
		}

		if len(reposToClean) == 0 {
			theme.SuccessMessage("No untracked files found across any development repositories. Everything is clean!")
			return
		}

		totalSizeStr := humanize.Bytes(uint64(totalUntrackedBytes))

		// Render Callout Box
		var boxLines []string
		boxLines = append(boxLines, fmt.Sprintf("Found %s repository(ies) with untracked files reclaiming %s space:",
			theme.PrimaryText.Bold(true).Render(fmt.Sprintf("%d", len(reposToClean))),
			theme.SuccessText.Bold(true).Render(totalSizeStr),
		))

		for _, r := range reposToClean {
			sizeStr := ""
			if r.SizeBytes > 0 {
				sizeStr = fmt.Sprintf(" (%s)", humanize.Bytes(uint64(r.SizeBytes)))
			}
			boxLines = append(boxLines, fmt.Sprintf("  • %s: %s untracked file(s)%s",
				theme.BoldText.Render(r.Name),
				theme.MutedText.Render(fmt.Sprintf("%d", r.UntrackedCount)),
				theme.MutedText.Render(sizeStr),
			))
		}

		if !ui.DisableProgress {
			fmt.Println(theme.InfoBox.Render(strings.Join(boxLines, "\n")))
		}

		if setup.DryRun {
			theme.InfoMessage(
				fmt.Sprintf(
					"Dry run complete. Previewed cleaning %d repository(ies) reclaiming %s disk space.",
					len(reposToClean),
					totalSizeStr,
				),
			)
			return
		}

		if !force {
			confirmMsg := fmt.Sprintf(
				"Are you sure you want to clean untracked files across these %d repository(ies) to reclaim %s?",
				len(reposToClean),
				totalSizeStr,
			)
			confirmed, err := ui.Confirm(confirmMsg, false)
			if err != nil || !confirmed {
				log.Message("Clean operation cancelled.")
				return
			}
		}

		totalCount := len(reposToClean)
		var progressSpinner *ui.Spinner
		if !ui.DisableProgress {
			progressSpinner = ui.NewProgressSpinner(fmt.Sprintf("Cleaning %d git repository(ies)...", totalCount))
		}

		var successCount int
		var failureCount int
		var freedBytes int64

		for i, r := range reposToClean {
			ratio := float64(i+1) / float64(totalCount)
			statusMsg := fmt.Sprintf("[%d/%d] Cleaning %s", i+1, totalCount, r.Name)

			if progressSpinner != nil {
				progressSpinner.SetProgressBar(ratio, statusMsg)
			}

			if err := cleanRepository(r.Path, directories); err != nil {
				failureCount++
				if progressSpinner != nil {
					progressSpinner.Logf("  %s Failed to clean %s: %v\n", theme.ErrorText.Render("✗"), r.Name, err)
				} else {
					log.Error("Failed to clean %s: %v", r.Name, err)
				}
			} else {
				successCount++
				freedBytes += r.SizeBytes
				sizeStr := ""
				if r.SizeBytes > 0 {
					sizeStr = fmt.Sprintf(" (freed %s)", humanize.Bytes(uint64(r.SizeBytes)))
				}
				if progressSpinner != nil {
					progressSpinner.Logf("  %s Cleaned %s%s\n", theme.SuccessText.Render("✓"), r.Name, sizeStr)
				} else {
					log.Success("Cleaned %s%s", r.Name, sizeStr)
				}
			}
		}

		if progressSpinner != nil {
			progressSpinner.SetProgressBar(1.0, "Cleaning complete")
			progressSpinner.Stop()
		}

		freedStr := humanize.Bytes(uint64(freedBytes))
		if failureCount > 0 {
			theme.WarningMessage(
				fmt.Sprintf(
					"Cleaned %d repository(ies) freeing %s, but %d failed.",
					successCount,
					freedStr,
					failureCount,
				),
			)
		} else {
			theme.SuccessMessage(
				fmt.Sprintf(
					"Successfully cleaned %d git repository(ies) freeing %s of disk space!",
					successCount,
					freedStr,
				),
			)
		}
	},
}

CleanAllCmd defines the cobra command for cleaning untracked files in all git repositories.

View Source
var FetchAllCmd = &cobra.Command{
	Use:   "fetch-all",
	Short: "Fetch all git repositories in development folder",
	Long:  `This command fetches updates from remote for all git repositories found in your development folder.`,
	Run: func(cmd *cobra.Command, args []string) {
		printHeader("🔍 Fetching Git Repositories")

		setup, err := setupGitCommand(cmd)
		if err != nil {
			log.Error("%s", err)
			return
		}

		repos, err := findGitRepositories(setup.DevPath)
		if err != nil {
			log.Error("Failed to find git repositories: %s", err)
			return
		}

		if len(repos) == 0 {
			log.Warn("No git repositories found in %s", setup.DevPath)
			return
		}

		log.Info("Found %d git repositories", len(repos))

		var successCount atomic.Int32
		var failureCount atomic.Int32

		multi, err := ui.NewMultiSpinner()
		if err != nil {
			log.Error("Failed to initialize UI: %s", err)
			return
		}
		defer multi.Stop()

		var eg errgroup.Group
		eg.SetLimit(10)

		for _, repoPath := range repos {
			rPath := repoPath
			eg.Go(func() error {
				repoName := filepath.Base(rPath)

				if setup.DryRun {
					spinner := multi.AddSpinner(fmt.Sprintf("[DRY RUN] Would fetch repository at: %s", rPath))
					spinner.Success()
					successCount.Add(1)
					return nil
				}

				spinner := multi.AddSpinner(fmt.Sprintf("Fetching %s...", repoName))

				if err := fetchRepository(rPath); err != nil {
					spinner.Fail(fmt.Sprintf("Failed to fetch %s: %s", repoName, err))
					failureCount.Add(1)
					return nil
				}

				spinner.Success(fmt.Sprintf("Fetched %s", repoName))
				successCount.Add(1)
				return nil
			})
		}

		_ = eg.Wait()
		multi.Stop()

		summaryMsg := fmt.Sprintf(
			"Fetch completed: %d successful, %d failed across %d repositories.",
			successCount.Load(),
			failureCount.Load(),
			len(repos),
		)
		if failureCount.Load() > 0 {
			theme.WarningMessage(summaryMsg)
		} else {
			theme.SuccessMessage(summaryMsg)
		}
	},
}

FetchAllCmd defines the cobra command for fetching all git repositories. It fetches updates from remote for all repositories in the development folder.

View Source
var GitCmd = &cobra.Command{
	Use:   "git",
	Short: "Manage multiple git repositories",
	Long:  `This command is used to facilitate the management of multiple git repositories in your development folder.`,
	Run: func(cmd *cobra.Command, args []string) {
		showInfo, _ := cmd.Flags().GetBool("info")
		isVerbose := cmdutil.IsVerbose(cmd)

		if showInfo {
			log.Info("Current git repository management configuration:")
			gitCfg := config.GetGitConfig()
			devPath := gitCfg.DevPath

			if devPath == "" {
				log.Warn("  Development Path: Not Set")
				log.Info("  Use 'eng config git-dev-path' to set your development folder path")
			} else {
				log.Info("  Development Path: %s", devPath)
			}
			return
		}

		if len(args) == 0 {
			log.Verbose(isVerbose, "No subcommand provided, showing help.")
			err := cmd.Help()
			cobra.CheckErr(err)
		} else {
			log.Verbose(isVerbose, "Subcommand '%s' provided.", args[0])
		}
	},
}

GitCmd serves as the base command for all git repository management operations. It doesn't perform any action itself but groups subcommands like sync-all, status-all, etc.

View Source
var ListCmd = &cobra.Command{
	Use:   "list",
	Short: "List all git repositories in development folder",
	Long:  `This command lists all git repositories found in your development folder.`,
	Run: func(cmd *cobra.Command, _args []string) {
		printHeader("📁 Development Git Repositories")

		setup, err := setupGitCommand(cmd)
		if err != nil {
			log.Error("%s", err)
			return
		}

		showPaths, _ := cmd.Flags().GetBool("paths")

		repos, err := findGitRepositories(setup.DevPath)
		if err != nil {
			log.Error("Failed to find git repositories: %s", err)
			return
		}

		if len(repos) == 0 {
			log.Warn("No git repositories found in %s", setup.DevPath)
			return
		}

		var cardLines []string
		cardLines = append(cardLines, fmt.Sprintf("Found %s repositories in %s:",
			theme.PrimaryText.Bold(true).Render(fmt.Sprintf("%d", len(repos))),
			theme.BoldText.Render(setup.DevPath),
		))
		cardLines = append(cardLines, "")

		for i, repoPath := range repos {
			repoName := filepath.Base(repoPath)
			branch := getRepoBranch(repoPath)
			if showPaths {
				cardLines = append(cardLines, fmt.Sprintf("  %-3d %-25s %-15s %s",
					i+1,
					theme.PrimaryText.Render(repoName),
					theme.MutedText.Render(branch),
					theme.BaseText.Render(repoPath),
				))
			} else {
				cardLines = append(cardLines, fmt.Sprintf("  %-3d %-25s %s",
					i+1,
					theme.PrimaryText.Render(repoName),
					theme.MutedText.Render(branch),
				))
			}
		}

		if !ui.DisableProgress {
			boxStyle := lipgloss.NewStyle().
				Border(lipgloss.RoundedBorder()).
				BorderForeground(theme.Primary).
				Padding(0, 1).
				MarginBottom(1)
			fmt.Fprintln(log.Out, boxStyle.Render(strings.Join(cardLines, "\n")))
		} else {
			for _, line := range cardLines {
				log.Info("%s", line)
			}
		}

		theme.SuccessMessage(fmt.Sprintf("Listed %d git repositories", len(repos)))
	},
}

ListCmd defines the cobra command for listing all git repositories. It lists all git repositories found in the development folder.

View Source
var PullAllCmd = &cobra.Command{
	Use:   "pull-all",
	Short: "Pull all git repositories in development folder",
	Long:  `This command pulls with rebase for all git repositories found in your development folder. Use this after fetch-all for faster operations.`,
	Run: func(cmd *cobra.Command, args []string) {
		printHeader("📥 Pulling Git Repositories")

		setup, err := setupGitCommand(cmd)
		if err != nil {
			log.Error("%s", err)
			return
		}

		repos, err := findGitRepositories(setup.DevPath)
		if err != nil {
			log.Error("Failed to find git repositories: %s", err)
			return
		}

		if len(repos) == 0 {
			log.Warn("No git repositories found in %s", setup.DevPath)
			return
		}

		log.Info("Found %d git repositories", len(repos))

		var successCount atomic.Int32
		var failureCount atomic.Int32

		multi, err := ui.NewMultiSpinner()
		if err != nil {
			log.Error("Failed to initialize UI: %s", err)
			return
		}
		defer multi.Stop()

		var eg errgroup.Group
		eg.SetLimit(10)

		for _, repoPath := range repos {
			rPath := repoPath
			eg.Go(func() error {
				repoName := filepath.Base(rPath)

				if setup.DryRun {
					spinner := multi.AddSpinner(fmt.Sprintf("[DRY RUN] Would pull repository at: %s", rPath))
					spinner.Success()
					successCount.Add(1)
					return nil
				}

				spinner := multi.AddSpinner(fmt.Sprintf("Pulling %s...", repoName))

				isDirty, err := repo.IsDirty(cmd.Context(), rPath)
				if err != nil {
					spinner.Fail(fmt.Sprintf("Failed to check status for %s: %s", repoName, err))
					failureCount.Add(1)
					return nil
				}

				if isDirty {
					spinner.Warning(fmt.Sprintf("Repository %s has uncommitted changes, skipping...", repoName))
					failureCount.Add(1)
					return nil
				}

				if err := pullRepository(rPath); err != nil {
					spinner.Fail(fmt.Sprintf("Failed to pull %s: %s", repoName, err))
					failureCount.Add(1)
					return nil
				}

				spinner.Success(fmt.Sprintf("Successfully pulled %s", repoName))
				successCount.Add(1)
				return nil
			})
		}

		_ = eg.Wait()
		multi.Stop()

		summaryMsg := fmt.Sprintf(
			"Pull completed: %d successful, %d failed across %d repositories.",
			successCount.Load(),
			failureCount.Load(),
			len(repos),
		)
		if failureCount.Load() > 0 {
			theme.WarningMessage(summaryMsg)
		} else {
			theme.SuccessMessage(summaryMsg)
		}
	},
}

PullAllCmd defines the cobra command for pulling all git repositories. It pulls with rebase for all repositories in the development folder (assumes fetch was already done).

View Source
var PushAllCmd = &cobra.Command{
	Use:   "push-all",
	Short: "Push all git repositories in development folder",
	Long:  `This command pushes commits for all git repositories found in your development folder that have unpushed commits.`,
	Run: func(cmd *cobra.Command, args []string) {
		printHeader("📤 Pushing Git Repositories")

		setup, err := setupGitCommand(cmd)
		if err != nil {
			log.Error("%s", err)
			return
		}

		force, _ := cmd.Flags().GetBool("force")

		if force {
			log.Warn("Force push mode enabled - this will force push to remote")
		}

		repos, err := findGitRepositories(setup.DevPath)
		if err != nil {
			log.Error("Failed to find git repositories: %s", err)
			return
		}

		if len(repos) == 0 {
			log.Warn("No git repositories found in %s", setup.DevPath)
			return
		}

		log.Info("Found %d git repositories", len(repos))

		successCount := 0
		failureCount := 0
		skippedCount := 0

		for _, repoPath := range repos {
			repoName := filepath.Base(repoPath)
			log.Verbose(setup.IsVerbose, "Checking repository: %s", repoName)

			if setup.DryRun {
				hasUnpushed, err := hasUnpushedCommits(repoPath)
				if err != nil {
					log.Error("  [DRY RUN] Failed to check for unpushed commits: %s", err)
					failureCount++
					continue
				}
				if hasUnpushed {
					log.Info("  [DRY RUN] Would push repository at: %s", repoPath)
					successCount++
				} else {
					log.Verbose(setup.IsVerbose, "  [DRY RUN] No unpushed commits, skipping: %s", repoPath)
					skippedCount++
				}
				continue
			}

			hasUnpushed, err := hasUnpushedCommits(repoPath)
			if err != nil {
				log.Error("  Failed to check for unpushed commits: %s", err)
				failureCount++
				continue
			}

			if !hasUnpushed {
				log.Verbose(setup.IsVerbose, "  No unpushed commits, skipping: %s", repoName)
				skippedCount++
				continue
			}

			isDirty, err := repo.IsDirty(cmd.Context(), repoPath)
			if err != nil {
				log.Error("  Failed to check repository status: %s", err)
				failureCount++
				continue
			}

			if isDirty {
				log.Warn("  Repository %s has uncommitted changes, but proceeding with push...", repoName)
			}

			if err := pushRepository(repoPath, force); err != nil {
				log.Error("  Failed to push %s: %s", repoName, err)
				failureCount++
				continue
			}

			log.Success("  Successfully pushed %s", repoName)
			successCount++
		}

		summaryMsg := fmt.Sprintf(
			"Push completed: %d successful, %d failed, %d skipped across %d repositories.",
			successCount,
			failureCount,
			skippedCount,
			len(repos),
		)
		if failureCount > 0 {
			theme.WarningMessage(summaryMsg)
		} else {
			theme.SuccessMessage(summaryMsg)
		}
	},
}

PushAllCmd defines the cobra command for pushing all git repositories. It pushes commits for all repositories in the development folder that have unpushed commits.

View Source
var StashAllCmd = &cobra.Command{
	Use:   "stash-all",
	Short: "Stash changes in all git repositories in development folder",
	Long:  `This command stashes uncommitted changes for all git repositories found in your development folder that have uncommitted changes.`,
	Run: func(cmd *cobra.Command, _args []string) {
		log.Start("Stashing changes in all git repositories")

		setup, err := setupGitCommand(cmd)
		if err != nil {
			log.Error("%s", err)
			return
		}

		message, _ := cmd.Flags().GetString("message")

		repos, err := findGitRepositories(setup.DevPath)
		if err != nil {
			log.Error("Failed to find git repositories: %s", err)
			return
		}

		if len(repos) == 0 {
			log.Warn("No git repositories found in %s", setup.DevPath)
			return
		}

		log.Info("Found %d git repositories", len(repos))

		successCount := 0
		failureCount := 0
		skippedCount := 0

		for _, repoPath := range repos {
			repoName := filepath.Base(repoPath)
			log.Info("Checking repository: %s", repoName)

			if setup.DryRun {
				hasChanges, err := hasUncommittedChanges(repoPath)
				if err != nil {
					log.Error("  [DRY RUN] Failed to check for changes: %s", err)
					failureCount++
					continue
				}
				if hasChanges {
					log.Info("  [DRY RUN] Would stash changes in: %s", repoPath)
					successCount++
				} else {
					log.Info("  [DRY RUN] No changes to stash, skipping: %s", repoPath)
					skippedCount++
				}
				continue
			}

			hasChanges, err := hasUncommittedChanges(repoPath)
			if err != nil {
				log.Error("  Failed to check for uncommitted changes: %s", err)
				failureCount++
				continue
			}

			if !hasChanges {
				log.Info("  No uncommitted changes, skipping...")
				skippedCount++
				continue
			}

			if err := stashRepository(repoPath, message); err != nil {
				log.Error("  Failed to stash %s: %s", repoName, err)
				failureCount++
				continue
			}

			log.Success("  Successfully stashed changes in %s", repoName)
			successCount++
		}

		log.Info("Stash completed: %d successful, %d failed, %d skipped", successCount, failureCount, skippedCount)

		if failureCount > 0 {
			log.Warn("Some repositories failed to stash. Check the output above for details.")
		} else {
			log.Success("All git repositories with changes stashed successfully")
		}
	},
}

StashAllCmd defines the cobra command for stashing changes in all git repositories. It stashes uncommitted changes for all repositories in the development folder that have changes.

View Source
var StatusAllCmd = &cobra.Command{
	Use:   "status-all",
	Short: "Check status of all git repositories in development folder",
	Long:  `This command checks the status of all git repositories found in your development folder.`,
	Run: func(cmd *cobra.Command, _args []string) {
		printHeader("📊 Development Repositories Status")

		setup, err := setupGitCommand(cmd)
		if err != nil {
			log.Error("%s", err)
			return
		}

		var scanSpinner *ui.Spinner
		if !ui.DisableProgress {
			scanSpinner = ui.NewSpinner(fmt.Sprintf("Scanning git repositories in %s...", setup.DevPath))
		}

		repos, err := findGitRepositories(setup.DevPath)
		if err != nil {
			if scanSpinner != nil {
				scanSpinner.Stop()
			}
			log.Error("Failed to find git repositories: %s", err)
			return
		}

		if scanSpinner != nil {
			scanSpinner.Stop()
		}

		if len(repos) == 0 {
			log.Warn("No git repositories found in %s", setup.DevPath)
			return
		}

		type RepoStatusSummary struct {
			Name    string
			Branch  string
			IsDirty bool
		}

		var summaries []RepoStatusSummary
		cleanCount := 0
		dirtyCount := 0

		for _, repoPath := range repos {
			repoName := filepath.Base(repoPath)
			branch := getRepoBranch(repoPath)

			isDirty, err := repo.IsDirty(cmd.Context(), repoPath)
			if err != nil {
				log.Error("  %s: Failed to check status - %s", repoName, err)
				continue
			}

			if isDirty {
				dirtyCount++
			} else {
				cleanCount++
			}

			summaries = append(summaries, RepoStatusSummary{
				Name:    repoName,
				Branch:  branch,
				IsDirty: isDirty,
			})
		}

		// Render Lipgloss Table Box
		var boxLines []string
		boxLines = append(boxLines, fmt.Sprintf("Checked %s repository(ies) in %s:",
			theme.PrimaryText.Bold(true).Render(fmt.Sprintf("%d", len(repos))),
			theme.BoldText.Render(setup.DevPath),
		))
		boxLines = append(boxLines, "")
		boxLines = append(boxLines, fmt.Sprintf("  %-30s %-20s %s",
			theme.BoldText.Render("Repository"),
			theme.BoldText.Render("Branch"),
			theme.BoldText.Render("Status"),
		))
		boxLines = append(boxLines, "  "+strings.Repeat("─", 65))

		for _, s := range summaries {
			statusTag := theme.SuccessText.Render("✓ Clean")
			if s.IsDirty {
				statusTag = theme.ErrorText.Render("⚠️ Uncommitted Changes")
			}

			boxLines = append(boxLines, fmt.Sprintf("  %-30s %-20s %s",
				theme.PrimaryText.Render(s.Name),
				theme.MutedText.Render(s.Branch),
				statusTag,
			))
		}

		if !ui.DisableProgress {
			fmt.Println(theme.InfoBox.Render(strings.Join(boxLines, "\n")))
		}

		summaryMsg := fmt.Sprintf(
			"Status summary: %d clean, %d with uncommitted changes across %d repositories.",
			cleanCount,
			dirtyCount,
			len(repos),
		)
		if dirtyCount > 0 {
			theme.WarningMessage(summaryMsg)
		} else {
			theme.SuccessMessage(summaryMsg)
		}
	},
}

StatusAllCmd defines the cobra command for checking status of all git repositories.

View Source
var SyncAllCmd = &cobra.Command{
	Use:   "sync-all",
	Short: "Sync all git repositories in development folder",
	Long:  `This command fetches and pulls with rebase for all git repositories found in your development folder.`,
	Run: func(cmd *cobra.Command, args []string) {
		printHeader("🔄 Syncing Git Repositories")

		setup, err := setupGitCommand(cmd)
		if err != nil {
			log.Error("%s", err)
			return
		}

		repos, err := findGitRepositories(setup.DevPath)
		if err != nil {
			log.Error("Failed to find git repositories: %s", err)
			return
		}

		if len(repos) == 0 {
			log.Warn("No git repositories found in %s", setup.DevPath)
			return
		}

		log.Info("Found %d git repositories", len(repos))

		var successCount atomic.Int32
		var failureCount atomic.Int32

		multi, err := ui.NewMultiSpinner()
		if err != nil {
			log.Error("Failed to initialize UI: %s", err)
			return
		}
		defer multi.Stop()

		var eg errgroup.Group
		eg.SetLimit(10)

		for _, repoPath := range repos {
			rPath := repoPath
			eg.Go(func() error {
				repoName := filepath.Base(rPath)

				if setup.DryRun {
					spinner := multi.AddSpinner(fmt.Sprintf("[DRY RUN] Would sync repository at: %s", rPath))
					spinner.Success()
					successCount.Add(1)
					return nil
				}

				spinner := multi.AddSpinner(fmt.Sprintf("Processing %s...", repoName))

				isDirty, err := repo.IsDirty(cmd.Context(), rPath)
				if err != nil {
					spinner.Fail(fmt.Sprintf("Failed to check status for %s: %s", repoName, err))
					failureCount.Add(1)
					return nil
				}

				if isDirty {
					spinner.Warning(fmt.Sprintf("Repository %s has uncommitted changes, skipping...", repoName))
					failureCount.Add(1)
					return nil
				}

				spinner.UpdateText(fmt.Sprintf("Pulling %s...", repoName))
				if err := repo.PullLatestCode(cmd.Context(), rPath); err != nil {
					spinner.Fail(fmt.Sprintf("Failed to pull latest code for %s: %s", repoName, err))
					failureCount.Add(1)
					return nil
				}

				spinner.Success(fmt.Sprintf("Successfully synced %s", repoName))
				successCount.Add(1)
				return nil
			})
		}

		_ = eg.Wait()
		multi.Stop()

		summaryMsg := fmt.Sprintf(
			"Sync completed: %d successful, %d failed across %d repositories.",
			successCount.Load(),
			failureCount.Load(),
			len(repos),
		)
		if failureCount.Load() > 0 {
			theme.WarningMessage(summaryMsg)
		} else {
			theme.SuccessMessage(summaryMsg)
		}
	},
}

SyncAllCmd defines the cobra command for syncing all git repositories. It fetches and pulls with rebase for all repositories in the development folder.

Functions

This section is empty.

Types

type GitCommandSetup added in v1.34.0

type GitCommandSetup struct {
	IsVerbose bool
	DryRun    bool
	DevPath   string
}

GitCommandSetup holds common variables and flags resolved during git command initialization.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL