ref: 93b94c30aefe0eebb5acb5e36df5b950dec70a94
parent: 3011c5e84e9c05bfabe0a5f24b8b267b4bd23912
author: Runxi Yu <me@runxiyu.org>
date: Sat Feb 21 10:06:22 EST 2026
cmd/show-object: Add command
--- /dev/null
+++ b/cmd/show-object/main.go
@@ -1,0 +1,108 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "log"
+ "os"
+ "strings"
+
+ "codeberg.org/lindenii/furgit/objectid"
+ "codeberg.org/lindenii/furgit/objectstored"
+ "codeberg.org/lindenii/furgit/objecttype"
+ "codeberg.org/lindenii/furgit/repository"
+)
+
+func main() {+ repoPath := flag.String("r", "", "path to git dir (.git or bare repo root)")+ name := flag.String("h", "", "reference name or object id")+ flag.Parse()
+
+ if *repoPath == "" || *name == "" {+ log.Fatal("must provide -r <repo> and -h <ref-or-object-id>")+ }
+
+ repo, err := repository.Open(*repoPath)
+ if err != nil {+ log.Fatalf("open repository: %v", err)+ }
+
+ id, err := resolveInput(repo, *name)
+ if err != nil {+ _ = repo.Close()
+ log.Fatalf("resolve %q: %v", *name, err)+ }
+
+ stored, err := repo.ReadStored(id)
+ if err != nil {+ _ = repo.Close()
+ log.Fatalf("read object %s: %v", id, err)+ }
+
+ printStored(stored)
+ if err := repo.Close(); err != nil {+ log.Fatalf("close repository: %v", err)+ }
+}
+
+func resolveInput(repo *repository.Repository, input string) (objectid.ObjectID, error) {+ if id, err := objectid.ParseHex(repo.Algorithm(), strings.TrimSpace(input)); err == nil {+ return id, nil
+ }
+ resolved, err := repo.Refs().ResolveFully(input)
+ if err != nil {+ return objectid.ObjectID{}, err+ }
+ return resolved.ID, nil
+}
+
+func printStored(stored objectstored.StoredObject) {+ var b strings.Builder
+
+ id := stored.ID()
+ ty := stored.Object().ObjectType()
+ tyName, ok := objecttype.Name(ty)
+ if !ok {+ tyName = fmt.Sprintf("type %d", ty)+ }
+ fmt.Fprintf(&b, "id: %s\n", id)
+ fmt.Fprintf(&b, "type: %s\n", tyName)
+
+ switch stored := stored.(type) {+ case *objectstored.StoredBlob:
+ blob := stored.Blob()
+ fmt.Fprintf(&b, "size: %d\n", len(blob.Data))
+ fmt.Fprintf(&b, "data: %q\n", string(blob.Data))
+ case *objectstored.StoredTree:
+ tree := stored.Tree()
+ fmt.Fprintf(&b, "entries: %d\n", len(tree.Entries))
+ for _, entry := range tree.Entries {+ fmt.Fprintf(&b, "%06o %s\t%s\n", entry.Mode, entry.ID, entry.Name)
+ }
+ case *objectstored.StoredCommit:
+ commit := stored.Commit()
+ fmt.Fprintf(&b, "tree: %s\n", commit.Tree)
+ for _, parent := range commit.Parents {+ fmt.Fprintf(&b, "parent: %s\n", parent)
+ }
+ fmt.Fprintf(&b, "author: %s <%s>\n", commit.Author.Name, commit.Author.Email)
+ fmt.Fprintf(&b, "committer: %s <%s>\n", commit.Committer.Name, commit.Committer.Email)
+ fmt.Fprintf(&b, "message:\n%s\n", string(commit.Message))
+ case *objectstored.StoredTag:
+ tag := stored.Tag()
+ targetTy, ok := objecttype.Name(tag.TargetType)
+ if !ok {+ targetTy = fmt.Sprintf("type %d", tag.TargetType)+ }
+ fmt.Fprintf(&b, "target: %s (%s)\n", tag.Target, targetTy)
+ fmt.Fprintf(&b, "name: %s\n", tag.Name)
+ if tag.Tagger != nil {+ fmt.Fprintf(&b, "tagger: %s <%s>\n", tag.Tagger.Name, tag.Tagger.Email)
+ }
+ fmt.Fprintf(&b, "message:\n%s\n", string(tag.Message))
+ default:
+ fmt.Fprintf(&b, "%#v\n", stored.Object())
+ }
+
+ _, _ = os.Stdout.WriteString(b.String())
+}
--
⑨