Hard restore with X asks before dropping staged pins

X dropped staged pins without a question, including the selected
submodule's own pin, which the loss check didn't count. A staged pin is
a decision, and X sits one Shift away from x, so dropped staged pins now
count as a loss and make X ask (D31, amends D30).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 23:26:32 +02:00
co-authored by Claude Opus 5.5
parent 72500353cd
commit b205576759
9 changed files with 84 additions and 24 deletions
+22 -3
View File
@@ -35,10 +35,29 @@ func (p *Provider) RestoreLoss(ctx context.Context, path string, to submodule.Re
if err != nil {
return submodule.Loss{}, err
}
if dir == "" { // not initialized: nothing on disk to lose
return submodule.Loss{}, nil
var loss submodule.Loss
if dir != "" { // not initialized: nothing on disk to lose
loss = p.lossOf(ctx, dir, target, to.KeepsStaged())
}
return p.lossOf(ctx, dir, target, to.KeepsStaged()), nil
if to == submodule.RestoreHard && path != "" && p.pinStaged(ctx, path, target) {
// a hard restore unstages the submodule's own pin too
loss.StagedPins++
loss.Modules = max(loss.Modules, 1)
}
return loss, nil
}
// pinStaged reports whether the pin of path in its parent's index differs
// from committed, the pin a hard restore goes to.
func (p *Provider) pinStaged(ctx context.Context, path, committed string) bool {
p.mu.RLock()
in, ok := p.mods[path]
p.mu.RUnlock()
if !ok {
return false
}
staged, err := pinOf(ctx, in, submodule.RestoreStaged)
return err == nil && staged != committed
}
// Restore makes path ("" is the superproject) and all submodules below it