func printCommit(node LogNode, path types.Path, w io.Writer, db datas.Database, o opts) (err error) {
	maxMetaFieldNameLength := func(commit types.Struct) int {
		maxLen := 0
		if m, ok := commit.MaybeGet(datas.MetaField); ok {
			meta := m.(types.Struct)
			types.TypeOf(meta).Desc.(types.StructDesc).IterFields(func(name string, t *types.Type, optional bool) {
				maxLen = max(maxLen, len(name))
			})
		}
		return maxLen
	}
	hashStr := node.commit.Hash().String()
	if o.useColor {
		hashStr = ansi.Color("commit "+hashStr, "red+h")
	}
	maxFieldNameLen := maxMetaFieldNameLength(node.commit)
	parentLabel := "Parent"
	parentValue := "None"
	parents := commitRefsFromSet(node.commit.Get(datas.ParentsField).(types.Set))
	if len(parents) > 1 {
		pstrings := make([]string, len(parents))
		for i, p := range parents {
			pstrings[i] = p.TargetHash().String()
		}
		parentLabel = "Merge"
		parentValue = strings.Join(pstrings, " ")
	} else if len(parents) == 1 {
		parentValue = parents[0].TargetHash().String()
	}
	if o.oneline {
		parentStr := fmt.Sprintf("%s %s", parentLabel+":", parentValue)
		fmt.Fprintf(w, "%s (%s)\n", hashStr, parentStr)
		return
	}
	maxFieldNameLen = max(maxFieldNameLen, len(parentLabel))
	parentStr := fmt.Sprintf("%-*s %s", maxFieldNameLen+1, parentLabel+":", parentValue)
	fmt.Fprintf(w, "%s%s\n", genGraph(node, 0, o), hashStr)
	fmt.Fprintf(w, "%s%s\n", genGraph(node, 1, o), parentStr)
	lineno := 1
	if o.maxLines != 0 {
		lineno, err = writeMetaLines(node, o.maxLines, lineno, maxFieldNameLen, w, o)
		if err != nil && err != writers.MaxLinesErr {
			fmt.Fprintf(w, "error: %s\n", err)
			return
		}
		if o.showValue {
			_, err = writeCommitLines(node, path, o.maxLines, lineno, w, db, o)
		} else {
			_, err = writeDiffLines(node, path, db, o.maxLines, lineno, w, o)
		}
	}
	return
}
Prints the information for one commit in the log, including ascii graph on left side of commits if
-graph arg is true.
package main
import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"math"
	"os"
	"strings"
	"time"
	"github.com/attic-labs/kingpin"
	"github.com/attic-labs/noms/cmd/util"
	"github.com/attic-labs/noms/go/config"
	"github.com/attic-labs/noms/go/d"
	"github.com/attic-labs/noms/go/datas"
	"github.com/attic-labs/noms/go/diff"
	"github.com/attic-labs/noms/go/spec"
	"github.com/attic-labs/noms/go/types"
	"github.com/attic-labs/noms/go/util/datetime"
	"github.com/attic-labs/noms/go/util/functions"
	"github.com/attic-labs/noms/go/util/outputpager"
	"github.com/attic-labs/noms/go/util/writers"
	"github.com/mgutz/ansi"
)
const parallelism = 16
type opts struct {
	useColor   bool
	maxLines   int
	maxCommits int
	oneline    bool
	showGraph  bool
	showValue  bool
	path       string
	tz         *time.Location
}
func nomsLog(noms *kingpin.Application) (*kingpin.CmdClause, util.KingpinHandler) {
	var o opts
	var color int
	var tzName string
	cmd := noms.Command("log", "Lists the history of changes to a path -- see Spelling Values at https://github.com/attic-labs/noms/blob/master/doc/spelling.md.")
	cmd.Flag("color", "set to 1 to force color on, 0 to force off").Default("-1").IntVar(&color)
	cmd.Flag("max-lines", "max number of lines to show per commit (-1 for all lines)").Default("9").IntVar(&o.maxLines)
	cmd.Flag("max-commits", "max number of commits to display (0 for all commits)").Short('n').Default("0").IntVar(&o.maxCommits)
	cmd.Flag("oneline", "show a summary of each commit on a single line").BoolVar(&o.oneline)
	cmd.Flag("graph", "show ascii-based commit hierarchy on left side of output").BoolVar(&o.showGraph)
	cmd.Flag("show-value", "show commit value rather than diff information").BoolVar(&o.showValue)
	cmd.Flag("tz", "display formatted date comments in specified timezone, must be: local or utc").Default("local").StringVar(&tzName)
	cmd.Arg("value", "dataset or value to display history for").Required().StringVar(&o.path)
	outputpager.RegisterOutputpagerFlags(cmd)
	return cmd, func(input string) int {
		o.useColor = shouldUseColor(color)
		cfg := config.NewResolver()
		o.tz, _ = locationFromTimezoneArg(tzName, nil)
		datetime.RegisterHRSCommenter(o.tz)
		resolved := cfg.ResolvePathSpec(o.path)
		sp, err := spec.ForPath(resolved)
		d.CheckErrorNoUsage(err)
		defer sp.Close()
		pinned, ok := sp.Pin()
		if !ok {
			fmt.Fprintf(os.Stderr, "Cannot resolve spec: %s\n", o.path)
			return 1
		}
		defer pinned.Close()
		database := pinned.GetDatabase()
		absPath := pinned.Path
		path := absPath.Path
		if len(path) == 0 {
			path = types.MustParsePath(".value")
		}
		origCommit, ok := database.ReadValue(absPath.Hash).(types.Struct)
		if !ok || !datas.IsCommit(origCommit) {
			d.CheckError(fmt.Errorf("%s does not reference a Commit object", path))
		}
		iter := NewCommitIterator(database, origCommit)
		displayed := 0
		if o.maxCommits <= 0 {
			o.maxCommits = math.MaxInt32
		}
		bytesChan := make(chan chan []byte, parallelism)
		var done = false
		go func() {
			for ln, ok := iter.Next(); !done && ok && displayed < o.maxCommits; ln, ok = iter.Next() {
				ch := make(chan []byte)
				bytesChan <- ch
				go func(ch chan []byte, node LogNode) {
					buff := &bytes.Buffer{}
					printCommit(node, path, buff, database, o)
					ch <- buff.Bytes()
				}(ch, ln)
				displayed++
			}
			close(bytesChan)
		}()
		pgr := outputpager.Start()
		defer pgr.Stop()
		for ch := range bytesChan {
			commitBuff := <-ch
			_, err := io.Copy(pgr.Writer, bytes.NewReader(commitBuff))
			if err != nil {
				done = true
				for range bytesChan {
				}
			}
		}
		return 0
	}
}