arche / commit

commit f201620ab19940b5257b06c4b3b0f61a3b433835e269776c3882274e5b9273f7
change wskpgceh
author dewn <dewn5228@proton.me>
committer dewn <dewn5228@proton.me>
date 2026-03-12 05:24:42
phase public
parents af02ba0a
signature Unsigned
add arche log --graph with topological sort and lane renderer
internal/cli/cmd_log.go [M]
--- a/internal/cli/cmd_log.go
+++ b/internal/cli/cmd_log.go
@@ -1,161 +1,357 @@
 package cli
 
 import (
 	"fmt"
 	"
+strings"
+	"
 time"
 
 	"arche/internal/object"
 	"arche/internal/revset"
 
 	"github.com/spf13/cobra"
 )
 
 var (
 	logLimit      int
 	logOps        bool
 	logShowSecret bool
 	logWhere      string
+	logGraph      bool
-)
+)
+
+
+type logCommit struct {
+	id      [32]byte
+	commit  *object.Commit
+	phase   object.Phase
+	parents [][32]byte
+}
 
 
 var logCmd = &cobra.Command{
 	Use:   "log",
 	Short: "Show the commit DAG",
 	Long: `Walk the commit graph backwards from HEAD (and all bookmarks) and display
 each commit in reverse chronological order.
 
 With --ops, show the operation log instead (equivalent to 'arche op log').`,
 	RunE: func(cmd *cobra.Command, args []string) error {
 		r := openRepo()
 		defer r.Close()
 
 		if logOps {
 			ops, err := r.Store.ListOperations(logLimit)
 			if err != nil {
 				return err
 			}
 			if len(ops) == 0 {
 				fmt.Println("No operations recorded.")
 				return nil
 			}
 			for _, op := range ops {
 				ts := time.Unix(op.Timestamp, 0).Format("2006-01-02 15:04:05")
 				if op.Metadata != "" {
 					fmt.Printf("#%-4d  %-14s  %s  %s\n", op.Seq, op.Kind, ts, op.Metadata)
 				} else {
 					fmt.Printf("#%-4d  %-14s  %s\n", op.Seq, op.Kind, ts)
 				}
 			}
 			return nil
 		}
 
 		var whereFilter revset.Func
 		if logWhere != "" {
 			var err error
 			whereFilter, err = revset.Parse(logWhere)
 			if err != nil {
 				return err
 			}
 		}
 
 		
+bms, _ := r.S
-t
+t
+ore.L
-i
+i
-p
+stBookmark
-s
+s
+()
+		bmIndex
- := make(map[[32]byte]
+ := make(map[[32]byte]
+[]string)
+		for _, 
-b
+b
+m := range bms {
+			bmIndex[bm.C
-o
+o
+mmitID] = append(bmIndex[bm.C
-o
+o
-l
+mmitID], bm.Name
-)
-		
+)
+		
-_,
+}
+		var
  
-h
+curH
 eadID
+ [32]byte
+		if _, id
 , err := r.HeadCommit()
-
-		if
+;
  err == nil {
 			
-tips[h
+curH
 eadID
-]
  = 
-true
+id
 
 		}
 
 		
-bm
+tip
 s
-, _
  := 
-r.S
+make(map[[32]by
 t
+e]b
 o
+ol)
+		if cu
 r
+H
 e
-.L
+adID != ([32]byte{}) {
+			t
 i
+p
 s
+[curHeadID] = 
 t
-Bookma
 r
-ks()
+ue
+		}
 
 		for _, bm := range bms {
 			tips[bm.CommitID] = true
 		}
 
 		
-s
+typ
 e
-e
+ commitI
 n
+fo = logCommit
+		allByID
  := ma
-k
+p[[32]byt
 e
-(
+]*commitInfo{}
+		{
+			seen := 
 map[[32]byte]bool
-)
+{}
 
+	
 		queue := make([][32]byte, 0, len(tips))
+	
 		for id := range tips {
 			
+	
 queue = append(queue, id)
 		
-}
-
-		bmIndex := make(map[[32]byte][]string)
-		for _, bm := range bms {
-			bmIndex[bm.CommitID] = append(bmIndex[bm.CommitID], bm.Name)
-	
 	}
 		
-var curHeadID [32]byte
-		if _, id, err := r.HeadCommit(); err == nil {
-			curHeadID = id
-		}
-
-		count := 0
-	
 	for len(queue) > 0 
-&& (logLimit <= 0 || count < logLimit) 
 {
+	
 			id := queue[0]
 			
+	
 queue = queue[1:]
 			
+	
 if seen[id] {
+	
 				continue
 			
+	
 }
+	
 			seen[id] = true
-
+	
 			c, err := r.ReadCommit(id)
+	
 			if err != nil {
 				
+	
 continue
 			
-}
-
 	
-		if !logShowSecret {
+}
 
 				phase, _ := r.Store.GetPhase(id)
 				if
+ !logShowSecret &&
  phase == object.PhaseSecret {
 					for _, p := range c.Parents {
 						if !seen[p] {
 							queue = append(queue, p)
 						}
 					}
 					continue
 				}
 			
-}
-
-		
 	if whereFilter != nil 
-{
-				phase, _ := r.Store.GetPhase(id)
-				if
+&&
  !whereFilter(id, c, phase) {
 					for _, p := range c.Parents {
 						if !seen[p] {
 							queue = append(queue, p)
 						}
 					}
 					continue
 				}
 			
+	allByID[id] = &commitInfo{id: id, commit: c, phase: phase, parents: c.Parents
 }
-
+	
 			
+for _, 
 p
+ := 
 r
-i
+ange c.Pare
 nt
+s {
+					if !seen[p] {
+						queue = append(queue, p)
+					}
+				}
+			}
+		}
+
+		child
 Co
-m
+unt := 
 m
+ap[[32]byte]
 i
+n
 t
-(
+{}
+		for _, c
 i
-d
+ := range allByID {
+			for _
 , 
+p := range 
 c
+i.parents {
+				if _
 , 
-bm
+ok := allBy
 I
+D[p]; ok {
+					childCou
 n
+t[p]++
+				}
+			}
+		}
+		topoQueue := make([][32]byte, 0, len(allByID))
+		for i
 d
+ := rang
 e
-x
+ allByID {
+			if childCount
 [id]
+ == 0 {
+				topoQueue = append(topoQueue
 , 
+id)
+			}
+		}
+		var ordered []
 c
+ommitInfo
+		for len(topoQ
 u
+eue) > 0 {
+			best := 0
+			fo
 r
-H
+ i := 1; i < len(topoQu
 e
+ue); i++ {
+				
 a
-d
+ := allBy
 ID
+[topoQueue[i]].commit.Author.Timestamp
+				b
  
+:
 =
+ allByID[topoQueue[best]].commit.Author.Timestamp
+				if a.After(b) {
+					best 
 = i
+
+				}
+			}
+			i
 d
+ := topoQueue[best]
+			topoQueue[best] = topoQueue[len(topoQueue
 )
+-1]
 
 			
-c
+t
 o
+poQue
 u
+e = topoQueue[:le
 n
+(
 t
-++
+opoQueue)-1]
 
 
 			
+ci := allByID[id]
+			ordered = append(ordered, *ci)
+			
 for _, p := range c
+i
 .
-P
+p
 arents {
 				if 
+_, ok := allByID[p]; 
 !
-s
+ok {
+					continu
 e
-e
+
+				}
+				childCou
 n
+t
 [p]
+--
+				if childCount[p] == 0
  {
 					
-q
+topoQ
 ueue = append(
-q