Add tijo-cat command

andybalholm
Apr 6, 2023, 11:47 PM
FWGMIVUZRDI2QWHHR5LLENXWVOLG7G6DGH52ULOV7TPOJYKLGP6AC

Dependencies

  • [2] 7D7CMNN3 Handle Edit hunks
  • [3] DF5RFSLH Read directory entries
  • [4] EYPCPIP7 Apply changes and build a graph
  • [*] AYLFNM5C Add the ability to output the contents of a file.

Change contents

  • edit in pristine.go at line 4
    [4.61]
    [4.61]
    "errors"
  • replacement in pristine.go at line 55
    [2.261][2.261:293]()
    return blocks[i].Start >= pos
    [2.261]
    [2.293]
    return blocks[i].Start == pos || blocks[i].End > pos
  • replacement in pristine.go at line 63
    [2.387][2.387:422]()
    // TODO: split blocks
    return nil
    [2.387]
    [2.422]
    if blocks[i].Start > pos {
    return nil
    }
    return g.splitBlock(blocks[i], pos)
  • edit in pristine.go at line 246
    [4.3352]
    [4.3352]
    }
    // RootDirectory returns the "inode" block corresponding to the root directory
    // of the repository.
    func (g *Graph) RootDirectory() (*Block, error) {
    b := g.Root
    for i := 0; i < 2; i++ {
    var next *Block
    for _, e := range b.Edges {
    if e.Flag&EdgeFlagsDeleted != 0 {
    continue
    }
    if e.Flag&EdgeFlagsFolder == 0 {
    return nil, errors.New("non-folder edge found while looking for root directory")
    }
    next = e.To
    break
    }
    if next == nil {
    return nil, errors.New("no root directory found")
    }
    b = next
    }
    return b, nil
  • edit in output.go at line 7
    [6.72]
    [6.72]
    "path"
    "strings"
  • edit in output.go at line 215
    [3.1535]
    [3.1535]
    }
    func FollowPath(g *Graph, p string) (DirEntry, error) {
    p = path.Clean(p)
    names := strings.Split(p, "/")
    dir, err := g.RootDirectory()
    if err != nil {
    return DirEntry{}, err
    }
    for level, name := range names {
    entries, err := ReadDir(dir)
    if err != nil {
    return DirEntry{}, err
    }
    var entry DirEntry
    for _, e := range entries {
    if e.Name == name {
    entry = e
    break
    }
    }
    if entry == (DirEntry{}) {
    return DirEntry{}, fmt.Errorf("not found: %s", path.Join(names[:level+1]...))
    }
    if level == len(names)-1 {
    return entry, nil
    }
    if !entry.IsDirectory {
    return DirEntry{}, fmt.Errorf("not a directory: %s", path.Join(names[:level+1]...))
    }
    dir = entry.Inode
    }
    panic("unreachable")
  • file addition: tijo-cat (d--r------)
    [4.12919]
  • file addition: main.go (----------)
    [0.1484]
    // The tijo-cat command prints the content of the specified file or files to
    // standard output.
    package main
    import (
    "fmt"
    "os"
    "os/exec"
    "strings"
    "pijul-go"
    )
    func printErrorAndExit(description string, err error) {
    msg := err.Error()
    if err, ok := err.(*exec.ExitError); ok && len(err.Stderr) > 0 {
    msg = string(err.Stderr)
    }
    fmt.Fprintln(os.Stderr, description, msg)
    os.Exit(2)
    }
    func main() {
    hashLog, err := exec.Command("pijul", "log", "--hash-only").Output()
    if err != nil {
    printErrorAndExit("error listing changes:", err)
    }
    hashes := strings.Fields(string(hashLog))
    parsedHashes := make([]pijul.Hash, 0, len(hashes))
    for i := len(hashes) - 1; i >= 0; i-- {
    h, err := pijul.HashFromBase32(hashes[i])
    if err != nil {
    printErrorAndExit("", fmt.Errorf("error parsing hash %q: %v", hashes[i], err))
    }
    parsedHashes = append(parsedHashes, h)
    }
    pristine, err := loadChanges(parsedHashes)
    if err != nil {
    printErrorAndExit("error loading changes:", err)
    }
    for _, filename := range os.Args[1:] {
    dirEntry, err := pijul.FollowPath(pristine, filename)
    if err != nil {
    printErrorAndExit("error finding file:", err)
    }
    pijul.OutputFile(os.Stdout, dirEntry.Inode)
    }
    }
    func loadChanges(hashes []pijul.Hash) (*pijul.Graph, error) {
    pristine := pijul.NewGraph()
    for _, h := range hashes {
    hs := h.String()
    data, err := os.ReadFile(fmt.Sprintf(".pijul/changes/%s/%s.change", hs[:2], hs[2:]))
    if err != nil {
    return nil, fmt.Errorf("error reading change %s: %w", hs, err)
    }
    c, err := pijul.DeserializeChange(data)
    if err != nil {
    return nil, fmt.Errorf("error deserializing change %s: %w", hs, err)
    }
    err = pristine.ApplyChange(h, c)
    if err != nil {
    return nil, fmt.Errorf("error applying change %s: %w", hs, err)
    }
    }
    return pristine, nil
    }