Skip to content
go-libfossil Artifact [47eed41d7d]
Login

Artifact [47eed41d7d]

Artifact 47eed41d7df12871ad625c63bbc31c8e12da698979f27270e4c5f1c200b3cd1d:


//go:build !js

package checkout

import (
	"fmt"

	"github.com/danmestas/go-libfossil/internal/undo"
)

// Undo reverses the last operation that saved undo state (Update, for
// one), as fossil's undo does: the working files it saved come back, and
// the file list, the pending merges and the checked-out version are as they
// were before it. It returns the working files it changed, in the order it
// changed them, for the caller to report as fossil's undo prints them.
//
// Panics if c is nil (TigerStyle precondition).
func (c *Checkout) Undo() ([]undo.Change, error) {
	if c == nil {
		panic("checkout.Undo: nil *Checkout")
	}
	changes, err := c.swapUndo(false)
	if err != nil {
		return nil, fmt.Errorf("checkout.Undo: %w", err)
	}
	return changes, nil
}

// Redo reverses the last Undo, returning the working files it changed as
// Undo does.
//
// Panics if c is nil (TigerStyle precondition).
func (c *Checkout) Redo() ([]undo.Change, error) {
	if c == nil {
		panic("checkout.Redo: nil *Checkout")
	}
	changes, err := c.swapUndo(true)
	if err != nil {
		return nil, fmt.Errorf("checkout.Redo: %w", err)
	}
	return changes, nil
}

// swapUndo is undo.Swap on the checkout, its files seen as fossil's RepoFILE
// sees them.
func (c *Checkout) swapUndo(redo bool) ([]undo.Change, error) {
	if err := c.checkUndoCone(redo); err != nil {
		return nil, err
	}
	disk, err := c.newDiskView()
	if err != nil {
		return nil, err
	}
	return undo.Swap(c.db, disk, c.dir, redo, c.versionHash)
}

// checkUndoCone leaves older, wider snapshots intact until they can be
// restored without materializing or managing an excluded path.
func (c *Checkout) checkUndoCone(redo bool) error {
	if c.cone == nil {
		return nil
	}
	want, op := "1", "Undo"
	if redo {
		want, op = "2", "Redo"
	}
	available, err := getVVar(c.db, "undo_available")
	if err != nil || available != want {
		return err
	}
	rows, err := c.db.Query("SELECT pathname FROM undo_vfile UNION SELECT pathname FROM undo ORDER BY pathname")
	if err != nil {
		return err
	}
	defer rows.Close()
	var paths []string
	for rows.Next() {
		var name string
		if err := rows.Scan(&name); err != nil {
			return err
		}
		paths = append(paths, name)
	}
	if err := rows.Err(); err != nil {
		return err
	}
	return c.refuseUncovered(op, paths...)
}

// versionHash returns the hash of check-in rid.
func (c *Checkout) versionHash(rid int64) (string, error) {
	var uuid string
	if err := c.repo.DB().QueryRow("SELECT uuid FROM blob WHERE rid = ?", rid).Scan(&uuid); err != nil {
		return "", fmt.Errorf("hash of version %d: %w", rid, err)
	}
	return uuid, nil
}