This repository has been archived on 2026-10-25. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
simonfrankenandClaude Opus 5.5 72500353cd Hard restore with X drops staged pins at every depth
X restores the selection to the committed pins without the staged or
committed question, and resets every restored repository's index to
HEAD, so staged pins below are dropped too. Dropped staged pins alone
don't make it ask; they are listed if something else is lost.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 22:58:08 +02:00

126 lines
5.1 KiB
Go

package submodule
import (
"context"
"time"
)
// Provider supplies repository data. Tree is expected to be fast (structure
// only); Status may be slow and is called once per submodule, concurrently.
type Provider interface {
Tree(ctx context.Context) (*Repo, error)
Status(ctx context.Context, path string) (*Status, error)
}
// Fetcher is implemented by providers that can download remote refs. Path
// "" is the superproject. Fetch must be safe to call concurrently and must
// never prompt the user.
type Fetcher interface {
// Fetchable reports whether path is a repository that can be fetched
// (e.g. not an uninitialized submodule).
Fetchable(path string) bool
Fetch(ctx context.Context, path string) error
}
// Watcher is implemented by providers that notice changes made outside the
// application, e.g. a fetch or commit in another shell. The channel is
// closed when ctx is done.
type Watcher interface {
Watch(ctx context.Context) <-chan Change
}
// Locator is implemented by providers whose submodules live on the local
// disk. Dir returns the absolute directory of path ("" is the superproject),
// or "" if it is unknown.
type Locator interface {
Dir(path string) string
}
// Restorer is implemented by providers that can make a repository (and all
// submodules below it) exactly what git records. Path "" is the
// superproject. Restore may clone or fetch objects that are missing, but must
// never prompt the user.
type Restorer interface {
// RestoreLoss reports what Restore would discard, read from the disk at
// this moment.
RestoreLoss(ctx context.Context, path string, to RestoreTarget) (Loss, error)
Restore(ctx context.Context, path string, to RestoreTarget) error
}
// Branch is a local or remote-tracking branch of a repository.
type Branch struct {
Ref string // full ref, e.g. "refs/remotes/origin/feat"
Name string // short name, e.g. "origin/feat"
Local string // name of the local branch it stands for, e.g. "feat"
Remote bool // a remote-tracking branch
Current bool // HEAD is on this branch
SHA string // the tip when the list was read
When time.Time // committer date of the tip
}
// Switcher is implemented by providers that can switch a repository to a
// branch: the tip becomes the staged pin of a submodule, and the repository
// (with everything below it) is restored to it. Path "" is the superproject.
// Like Restore, Switch may clone or fetch what is missing below, but must
// never prompt the user. Branches uses no network.
type Switcher interface {
Branches(ctx context.Context, path string) ([]Branch, error)
// SwitchLoss reports what Switch would discard, read from the disk now.
SwitchLoss(ctx context.Context, path string, b Branch) (Loss, error)
// Switch returns the branch HEAD ends up on, "" if it is detached. It
// fails if b.Ref no longer points at b.SHA.
Switch(ctx context.Context, path string, b Branch) (string, error)
}
// RestoreTarget is the pin a submodule is restored to, and whether staged
// pins below it are kept. The superproject's target is always HEAD; only
// RestoreHard matters for it.
type RestoreTarget int
const (
RestoreStaged RestoreTarget = iota // the pin in the parent's index: a staged pin is kept and used
RestoreCommitted // the pin in the parent's HEAD: a staged pin is unstaged
RestoreHard // the pin in the parent's HEAD, and staged pins below are dropped too
)
// Committed reports whether a submodule goes to the pin in its parent's HEAD.
func (t RestoreTarget) Committed() bool { return t != RestoreStaged }
// KeepsStaged reports whether staged pins below the restored repository are
// kept.
func (t RestoreTarget) KeepsStaged() bool { return t != RestoreHard }
// Loss is what a restore discards. The zero Loss means nothing is lost.
type Loss struct {
Files int // changed, conflicted and untracked files and directories
Modules int // repositories with a loss, the repository itself included
Operations int // merges, rebases, cherry-picks, reverts in progress
Commits int // commits that no branch, tag or remote ref reaches
Repositories int // untracked repositories with their own .git directory
StagedPins int // staged pins of repositories that move to another commit
Unknown int // repositories whose state could not be read
}
// Empty reports whether nothing would be lost.
func (l Loss) Empty() bool { return l == Loss{} }
// Add sums two losses.
func (l Loss) Add(o Loss) Loss {
return Loss{
Files: l.Files + o.Files,
Modules: l.Modules + o.Modules,
Operations: l.Operations + o.Operations,
Commits: l.Commits + o.Commits,
Repositories: l.Repositories + o.Repositories,
StagedPins: l.StagedPins + o.StagedPins,
Unknown: l.Unknown + o.Unknown,
}
}
// Change reports that the git metadata of a repository changed.
type Change struct {
Path string // "" is the superproject
Local bool // HEAD, index or local branches changed, not only remote refs
Structure bool // .gitmodules changed: submodules may have been added or removed
}