<commit_before>package main

import (
	"fmt"
	"io"
	"path"
	"restic"
	"restic/backend"
)

// CmdForget implements the 'forget' command.
type CmdForget struct {
	Last    int `short:"l" long:"keep-last" description:"keep the last n snapshots"`
	Hourly  int `short:"H" long:"keep-hourly" description:"keep the last n hourly snapshots"`
	Daily   int `short:"d" long:"keep-daily" description:"keep the last n daily snapshots"`
	Weekly  int `short:"w" long:"keep-weekly" description:"keep the last n weekly snapshots"`
	Monthly int `short:"m" long:"keep-monthly" description:"keep the last n monthly snapshots"`
	Yearly  int `short:"y" long:"keep-yearly" description:"keep the last n yearly snapshots"`

	DryRun bool `short:"n" long:"dry-run" description:"do not delete anything, just print what would be done"`

	global *GlobalOptions
}

func init() {
	_, err := parser.AddCommand("forget",
		"removes snapshots from a repository",
		"The forget command removes snapshots according to a policy.",
		&CmdForget{global: &globalOpts})
	if err != nil {
		panic(err)
	}
}

// Usage returns usage information for 'forget'.
func (cmd CmdForget) Usage() string {
	return "[snapshot ID] ..."
}

func printSnapshots(w io.Writer, snapshots restic.Snapshots) {
	tab := NewTable()
	tab.Header = fmt.Sprintf("%-8s  %-19s  %-10s  %s", "ID", "Date", "Host", "Directory")
	tab.RowFormat = "%-8s  %-19s  %-10s  %s"

	for _, sn := range snapshots {
		if len(sn.Paths) == 0 {
			continue
		}
		id := sn.ID()
		tab.Rows = append(tab.Rows, []interface{}{id.Str(), sn.Time.Format(TimeFormat), sn.Hostname, sn.Paths[0]})

		if len(sn.Paths) > 1 {
			for _, path := range sn.Paths[1:] {
				tab.Rows = append(tab.Rows, []interface{}{"", "", "", path})
			}
		}
	}

	tab.Write(w)
}

// Execute runs the 'forget' command.
func (cmd CmdForget) Execute(args []string) error {
	repo, err := cmd.global.OpenRepository()
	if err != nil {
		return err
	}

	lock, err := lockRepoExclusive(repo)
	defer unlockRepo(lock)
	if err != nil {
		return err
	}

	err = repo.LoadIndex()
	if err != nil {
		return err
	}

	// first, process all snapshot IDs given as arguments
	for _, s := range args {
		id, err := restic.FindSnapshot(repo, s)
		if err != nil {
			return err
		}

		if !cmd.DryRun {
			err = repo.Backend().Remove(backend.Snapshot, id.String())
			if err != nil {
				return err
			}

			cmd.global.Verbosef("removed snapshot %v\n", id.Str())
		} else {
			cmd.global.Verbosef("would removed snapshot %v\n", id.Str())
		}

	}

	// then, load all remaining snapshots
	snapshots, err := restic.LoadAllSnapshots(repo)
	if err != nil {
		return err
	}

	// group by hostname and dirs
	type key struct {
		Hostname string
		Dirs     string
	}

	snapshotGroups := make(map[key]restic.Snapshots)

	for _, sn := range snapshots {
		k := key{Hostname: sn.Hostname, Dirs: path.Join(sn.Paths...)}
		list := snapshotGroups[k]
		list = append(list, sn)
		snapshotGroups[k] = list
	}

	policy := restic.ExpirePolicy{
		Last:    cmd.Last,
		Hourly:  cmd.Hourly,
		Daily:   cmd.Daily,
		Weekly:  cmd.Weekly,
		Monthly: cmd.Monthly,
		Yearly:  cmd.Yearly,
	}

	for key, snapshotGroup := range snapshotGroups {
		cmd.global.Printf("snapshots for host %v, directories %v:\n\n", key.Hostname, key.Dirs)
		keep, remove := restic.ApplyPolicy(snapshotGroup, policy)
<commit_msg>forget: Do nothing if no policy is configured<commit_after>package main

import (
	"fmt"
	"io"
	"path"
	"restic"
	"restic/backend"
)

// CmdForget implements the 'forget' command.
type CmdForget struct {
	Last    int `short:"l" long:"keep-last" description:"keep the last n snapshots"`
	Hourly  int `short:"H" long:"keep-hourly" description:"keep the last n hourly snapshots"`
	Daily   int `short:"d" long:"keep-daily" description:"keep the last n daily snapshots"`
	Weekly  int `short:"w" long:"keep-weekly" description:"keep the last n weekly snapshots"`
	Monthly int `short:"m" long:"keep-monthly" description:"keep the last n monthly snapshots"`
	Yearly  int `short:"y" long:"keep-yearly" description:"keep the last n yearly snapshots"`

	DryRun bool `short:"n" long:"dry-run" description:"do not delete anything, just print what would be done"`

	global *GlobalOptions
}

func init() {
	_, err := parser.AddCommand("forget",
		"removes snapshots from a repository",
		"The forget command removes snapshots according to a policy.",
		&CmdForget{global: &globalOpts})
	if err != nil {
		panic(err)
	}
}

// Usage returns usage information for 'forget'.
func (cmd CmdForget) Usage() string {
	return "[snapshot ID] ..."
}

func printSnapshots(w io.Writer, snapshots restic.Snapshots) {
	tab := NewTable()
	tab.Header = fmt.Sprintf("%-8s  %-19s  %-10s  %s", "ID", "Date", "Host", "Directory")
	tab.RowFormat = "%-8s  %-19s  %-10s  %s"

	for _, sn := range snapshots {
		if len(sn.Paths) == 0 {
			continue
		}
		id := sn.ID()
		tab.Rows = append(tab.Rows, []interface{}{id.Str(), sn.Time.Format(TimeFormat), sn.Hostname, sn.Paths[0]})

		if len(sn.Paths) > 1 {
			for _, path := range sn.Paths[1:] {
				tab.Rows = append(tab.Rows, []interface{}{"", "", "", path})
			}
		}
	}

	tab.Write(w)
}

// Execute runs the 'forget' command.
func (cmd CmdForget) Execute(args []string) error {
	repo, err := cmd.global.OpenRepository()
	if err != nil {
		return err
	}

	lock, err := lockRepoExclusive(repo)
	defer unlockRepo(lock)
	if err != nil {
		return err
	}

	err = repo.LoadIndex()
	if err != nil {
		return err
	}

	// first, process all snapshot IDs given as arguments
	for _, s := range args {
		id, err := restic.FindSnapshot(repo, s)
		if err != nil {
			return err
		}

		if !cmd.DryRun {
			err = repo.Backend().Remove(backend.Snapshot, id.String())
			if err != nil {
				return err
			}

			cmd.global.Verbosef("removed snapshot %v\n", id.Str())
		} else {
			cmd.global.Verbosef("would removed snapshot %v\n", id.Str())
		}
	}

	policy := restic.ExpirePolicy{
		Last:    cmd.Last,
		Hourly:  cmd.Hourly,
		Daily:   cmd.Daily,
		Weekly:  cmd.Weekly,
		Monthly: cmd.Monthly,
		Yearly:  cmd.Yearly,
	}

	if policy.Empty() {
		cmd.global.Verbosef("no expire policy configured, exiting\n")
		return nil
	}

	// then, load all remaining snapshots
	snapshots, err := restic.LoadAllSnapshots(repo)
	if err != nil {
		return err
	}

	// group by hostname and dirs
	type key struct {
		Hostname string
		Dirs     string
	}

	snapshotGroups := make(map[key]restic.Snapshots)

	for _, sn := range snapshots {
		k := key{Hostname: sn.Hostname, Dirs: path.Join(sn.Paths...)}
		list := snapshotGroups[k]
		list = append(list, sn)
		snapshotGroups[k] = list
	}

	for key, snapshotGroup := range snapshotGroups {
		cmd.global.Printf("snapshots for host %v, directories %v:\n\n", key.Hostname, key.Dirs)
		keep, remove := restic.ApplyPolicy(snapshotGroup, policy)

{'fraction_non_alphanumeric': 0.1495795840094409, 'fraction_numerical': 0.0041304027142646405, 'mean_word_length': 3.6470185058259084, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4096434', 'n_tokens_mistral': 2597, 'n_tokens_neox': 2402, 'n_words': 674}