arche / internal/cli/cmd_log.go

commit 154431fd
  1package cli
  2
  3import (
  4	"fmt"
  5	"time"
  6
  7	"arche/internal/object"
  8	"arche/internal/revset"
  9
 10	"github.com/spf13/cobra"
 11)
 12
 13var (
 14	logLimit      int
 15	logOps        bool
 16	logShowSecret bool
 17	logWhere      string
 18)
 19
 20var logCmd = &cobra.Command{
 21	Use:   "log",
 22	Short: "Show the commit DAG",
 23	Long: `Walk the commit graph backwards from HEAD (and all bookmarks) and display
 24each commit in reverse chronological order.
 25
 26With --ops, show the operation log instead (equivalent to 'arche op log').`,
 27	RunE: func(cmd *cobra.Command, args []string) error {
 28		r := openRepo()
 29		defer r.Close()
 30
 31		if logOps {
 32			ops, err := r.Store.ListOperations(logLimit)
 33			if err != nil {
 34				return err
 35			}
 36			if len(ops) == 0 {
 37				fmt.Println("No operations recorded.")
 38				return nil
 39			}
 40			for _, op := range ops {
 41				ts := time.Unix(op.Timestamp, 0).Format("2006-01-02 15:04:05")
 42				if op.Metadata != "" {
 43					fmt.Printf("#%-4d  %-14s  %s  %s\n", op.Seq, op.Kind, ts, op.Metadata)
 44				} else {
 45					fmt.Printf("#%-4d  %-14s  %s\n", op.Seq, op.Kind, ts)
 46				}
 47			}
 48			return nil
 49		}
 50
 51		var whereFilter revset.Func
 52		if logWhere != "" {
 53			var err error
 54			whereFilter, err = revset.Parse(logWhere)
 55			if err != nil {
 56				return err
 57			}
 58		}
 59
 60		tips := make(map[[32]byte]bool)
 61		_, headID, err := r.HeadCommit()
 62		if err == nil {
 63			tips[headID] = true
 64		}
 65
 66		bms, _ := r.Store.ListBookmarks()
 67		for _, bm := range bms {
 68			tips[bm.CommitID] = true
 69		}
 70
 71		seen := make(map[[32]byte]bool)
 72		queue := make([][32]byte, 0, len(tips))
 73		for id := range tips {
 74			queue = append(queue, id)
 75		}
 76
 77		bmIndex := make(map[[32]byte][]string)
 78		for _, bm := range bms {
 79			bmIndex[bm.CommitID] = append(bmIndex[bm.CommitID], bm.Name)
 80		}
 81		var curHeadID [32]byte
 82		if _, id, err := r.HeadCommit(); err == nil {
 83			curHeadID = id
 84		}
 85
 86		count := 0
 87		for len(queue) > 0 && (logLimit <= 0 || count < logLimit) {
 88			id := queue[0]
 89			queue = queue[1:]
 90			if seen[id] {
 91				continue
 92			}
 93			seen[id] = true
 94
 95			c, err := r.ReadCommit(id)
 96			if err != nil {
 97				continue
 98			}
 99
100			if !logShowSecret {
101				phase, _ := r.Store.GetPhase(id)
102				if phase == object.PhaseSecret {
103					for _, p := range c.Parents {
104						if !seen[p] {
105							queue = append(queue, p)
106						}
107					}
108					continue
109				}
110			}
111
112			if whereFilter != nil {
113				phase, _ := r.Store.GetPhase(id)
114				if !whereFilter(id, c, phase) {
115					for _, p := range c.Parents {
116						if !seen[p] {
117							queue = append(queue, p)
118						}
119					}
120					continue
121				}
122			}
123
124			printCommit(id, c, bmIndex[id], curHeadID == id)
125			count++
126
127			for _, p := range c.Parents {
128				if !seen[p] {
129					queue = append(queue, p)
130				}
131			}
132		}
133		return nil
134	},
135}
136
137func init() {
138	logCmd.Flags().IntVarP(&logLimit, "limit", "n", 0, "maximum number of commits to show (0 = all)")
139	logCmd.Flags().BoolVar(&logOps, "ops", false, "show the operation log instead of the commit graph")
140	logCmd.Flags().BoolVarP(&logShowSecret, "secret", "s", false, "include secret commits in output")
141	logCmd.Flags().StringVar(&logWhere, "where", "", `filter commits with a revset expression, e.g. --where 'author(alice) and not public()'`)
142}
143
144func printCommit(id [32]byte, c *object.Commit, bookmarks []string, isHead bool) {
145	prefix := "  "
146	if isHead {
147		prefix = "@ "
148	}
149	fmt.Printf("%scommit  %x\n", prefix, id)
150	fmt.Printf("  change  ch:%s\n", c.ChangeID)
151	if len(bookmarks) > 0 {
152		fmt.Printf("  marks   %v\n", bookmarks)
153	}
154	fmt.Printf("  author  %s <%s>\n", c.Author.Name, c.Author.Email)
155	fmt.Printf("  date    %s\n", c.Author.Timestamp.Format(time.RFC1123))
156	fmt.Printf("  phase   %s\n", c.Phase)
157	if c.Message != "" {
158		fmt.Printf("\n    %s\n", c.Message)
159	}
160	fmt.Println()
161}