cmd/gitbayd/maint.go

80 lines · 2093 bytes

 1package main
 2
 3import (
 4	"fmt"
 5	"os"
 6	"os/exec"
 7
 8	"github.com/spf13/cobra"
 9
10	"gitbay.org/gitbay/internal/config"
11	"gitbay.org/gitbay/internal/control"
12	"gitbay.org/gitbay/internal/gitutil"
13	"gitbay.org/gitbay/internal/store"
14)
15
16func gcCmd() *cobra.Command {
17	var repoPath string
18	var aggressive bool
19	cmd := &cobra.Command{
20		Use:   "gc",
21		Short: "repack and prune repositories (git gc)",
22		RunE: func(cmd *cobra.Command, args []string) error {
23			cfg, err := config.Load(configPath)
24			if err != nil {
25				return err
26			}
27			st, err := openStore(cfg)
28			if err != nil {
29				return err
30			}
31			defer st.Close()
32
33			var repos []store.Repo
34			if repoPath != "" {
35				r, err := st.RepoByPath(repoPath)
36				if err != nil {
37					return fmt.Errorf("no repository %q", repoPath)
38				}
39				repos = []store.Repo{r}
40			} else if repos, err = st.ListAllRepos(); err != nil {
41				return err
42			}
43
44			var before, after int64
45			for _, r := range repos {
46				dir := control.RepoDir(cfg.Server.Root, r.OwnerName, r.Name)
47				b := gitutil.DirSize(dir)
48				gcArgs := []string{"-C", dir, "gc", "--quiet"}
49				if aggressive {
50					gcArgs = append(gcArgs, "--aggressive")
51				}
52				if out, err := exec.Command("git", gcArgs...).CombinedOutput(); err != nil {
53					fmt.Fprintf(os.Stderr, "%s: gc failed: %v\n%s", r.Path(), err, out)
54					continue
55				}
56				a := gitutil.DirSize(dir)
57				before, after = before+b, after+a
58				fmt.Printf("%s\t%s -> %s\n", r.Path(), human(b), human(a))
59			}
60			fmt.Printf("total\t%s -> %s (freed %s)\n", human(before), human(after), human(before-after))
61			return nil
62		},
63	}
64	cmd.Flags().StringVar(&repoPath, "repo", "", "one repository (owner/name) instead of all")
65	cmd.Flags().BoolVar(&aggressive, "aggressive", false, "more thorough repack (slow; rarely needed)")
66	return cmd
67}
68
69func human(b int64) string {
70	switch {
71	case b >= 1<<30:
72		return fmt.Sprintf("%.1f GiB", float64(b)/(1<<30))
73	case b >= 1<<20:
74		return fmt.Sprintf("%.1f MiB", float64(b)/(1<<20))
75	case b >= 1<<10:
76		return fmt.Sprintf("%.1f KiB", float64(b)/(1<<10))
77	default:
78		return fmt.Sprintf("%d B", b)
79	}
80}