package v2 import ( "bufio" "io" "path" "github.com/go-git/go-billy/v5" "github.com/go-git/go-git/v5/plumbing" ) // OpenChainFile reads a commit chain file and returns a slice of the hashes within it // // Commit-Graph chains are described at https://git-scm.com/docs/commit-graph // and are new line separated list of graph file hashes, oldest to newest. // // This function simply reads the file and returns the hashes as a slice. func OpenChainFile(r io.Reader) ([]string, error) { if r == nil { return nil, io.ErrUnexpectedEOF } bufRd := bufio.NewReader(r) chain := make([]string, 0, 8) for { line, err := bufRd.ReadSlice('\n') if err != nil { if err == io.EOF { break } return nil, err } hashStr := string(line[:len(line)-1]) if !plumbing.IsHash(hashStr) { return nil, ErrMalformedCommitGraphFile } chain = append(chain, hashStr) } return chain, nil } // OpenChainOrFileIndex expects a billy.Filesystem representing a .git directory. // It will first attempt to read a commit-graph index file, before trying to read a // commit-graph chain file and its index files. If neither are present, an error is returned. // Otherwise an Index will be returned. // // See: https://git-scm.com/docs/commit-graph func OpenChainOrFileIndex(fs billy.Filesystem) (Index, error) { file, err := fs.Open(path.Join("objects", "info", "commit-graph")) if err != nil { // try to open a chain file return OpenChainIndex(fs) } index, err := OpenFileIndex(file) if err != nil { // Ignore any file closing errors and return the error from OpenFileIndex instead _ = file.Close() return nil, err } return index, nil } // OpenChainIndex expects a billy.Filesystem representing a .git directory. // It will read a commit-graph chain file and return a coalesced index. // If the chain file or a graph in that chain is not present, an error is returned. // // See: https://git-scm.com/docs/commit-graph func OpenChainIndex(fs billy.Filesystem) (Index, error) { chainFile, err := fs.Open(path.Join("objects", "info", "commit-graphs", "commit-graph-chain")) if err != nil { return nil, err } chain, err := OpenChainFile(chainFile) _ = chainFile.Close() if err != nil { return nil, err } var index Index for _, hash := range chain { file, err := fs.Open(path.Join("objects", "info", "commit-graphs", "graph-"+hash+".graph")) if err != nil { // Ignore all other file closing errors and return the error from opening the last file in the graph _ = index.Close() return nil, err } index, err = OpenFileIndexWithParent(file, index) if err != nil { // Ignore file closing errors and return the error from OpenFileIndex instead _ = index.Close() return nil, err } } return index, nil }