diff --git a/cmd/bazel-git/BUILD.bazel b/cmd/bazel-git/BUILD.bazel index 3cd4d8ba92a867140d6e924cf881b361680cf1aa..4ff6ce933bc671e4f9bc3ed10bcbc8906bea58ee 100644 --- a/cmd/bazel-git/BUILD.bazel +++ b/cmd/bazel-git/BUILD.bazel @@ -12,6 +12,7 @@ go_library( "init.go", "ls_remote.go", "main.go", + "show.go", ], importpath = "gitlab.arm.com/bazel/git/v1/cmd/bazel-git", visibility = ["//visibility:private"], @@ -26,6 +27,7 @@ go_library( "@com_github_go_git_go_git_v5//plumbing/protocol/packp/sideband:go_default_library", "@com_github_go_git_go_git_v5//storage/filesystem:go_default_library", "@com_github_go_git_go_git_v5//storage/memory:go_default_library", + "@com_github_go_git_go_git_v5//utils/ioutil:go_default_library", "@com_github_jessevdk_go_flags//:go_default_library", "@com_github_kevinburke_ssh_config//:go_default_library", "@com_github_mattn_go_isatty//:go_default_library", diff --git a/cmd/bazel-git/show.go b/cmd/bazel-git/show.go new file mode 100644 index 0000000000000000000000000000000000000000..f09bd23d0dd1163f8722b98aefeb3e06b2b6a018 --- /dev/null +++ b/cmd/bazel-git/show.go @@ -0,0 +1,105 @@ +package main + +import ( + "os" + "fmt" + "log/slog" + "strings" + "io" + + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/utils/ioutil" + "github.com/jessevdk/go-flags" +) + +type ShowCommand struct { + Args struct { + Revision plumbing.Revision `positional-arg-name:"" description:"The object to show."` + } `positional-args:"yes" required:"yes"` +} + +var showCommand ShowCommand + +func (x *ShowCommand) Execute(rest []string) error { + if len(rest) != 0 { + return &flags.Error{flags.ErrDuplicatedFlag, "invalid number of positional arguments"} + } + + log := slog.With("cmd", "show", "object", x.Args.Revision) + + log.Debug("open", "git-dir", options.GitDir) + repo, err := git.PlainOpen(options.GitDir) + if err != nil { + log.Error("open", "err", err) + return err + } + + log.Debug("resolve") + hash, err := repo.ResolveRevision(x.Args.Revision) + if err != nil { + log.Error("resolve", "err", err) + return err + } + log.Debug("resolved", "hash", hash) + + if !strings.Contains(x.Args.Revision.String(), ":") { + return fmt.Errorf("Only blobs with filepaths are supported (`:`) : %s", x.Args.Revision.String()) + } + + log.Debug("commit") + commit, err := repo.CommitObject(*hash) + if err != nil { + log.Error("commit", "err", err) + return err + } + + log.Debug("tree") + tree, err := commit.Tree() + if err != nil { + log.Error("tree", "err", err) + return err + } + + filepath := strings.SplitN(x.Args.Revision.String(), ":", 2)[1] + + log.Debug("file", "filepath", filepath) + file, err := tree.File(filepath) + if err != nil { + log.Error("tree", "err", err) + return err + } + + log.Debug("reader") + reader, err := file.Reader() + if err != nil { + log.Error("reader", "err", err) + return err + } + defer ioutil.CheckClose(reader, &err) + + log.Debug("copy") + _, err = io.Copy(os.Stdout, reader) + if err != nil { + log.Error("copy", "err", err) + return err + } + + return err +} + +func init() { + parser.AddCommand("show", + "Show various types of objects.", + `Shows one or more objects (blobs, tree, tags and commits). + + For commits it shows the log message and textual diff. It also presents the merge + commit in a special format as produced by 'git diff-tree --cc'. + + For tags, it shows the tag message and the referenced objects. + + For trees, it shows the names (equivalent to 'git ls-tree' with '--name-only'). + + For plain blobs, it shows the plain contents.`, + &showCommand) +}