ref: 9053c85456bd9b4457b588610eeef1b8dfff2b89
dir: /receivepack/hooks/reject_force_push.go/
package hooks
import (
"context"
"errors"
"fmt"
"codeberg.org/lindenii/furgit/ancestor"
"codeberg.org/lindenii/furgit/objectid"
objectmix "codeberg.org/lindenii/furgit/objectstore/mix"
receivepack "codeberg.org/lindenii/furgit/receivepack"
"codeberg.org/lindenii/furgit/refstore"
)
// RejectForcePush rejects updates whose new value is not a fast-forward of the
// currently resolved reference.
func RejectForcePush() receivepack.Hook {
return func(
ctx context.Context,
req receivepack.HookRequest,
) ([]receivepack.UpdateDecision, error) {
_ = ctx
objects := objectmix.New(req.QuarantinedObjects, req.ExistingObjects)
decisions := make([]receivepack.UpdateDecision, len(req.Updates))
for i := range decisions {
decisions[i].Accept = true
}
for i, update := range req.Updates {
if update.OldID == objectid.Zero(update.OldID.Algorithm()) || update.NewID == objectid.Zero(update.NewID.Algorithm()) {
continue
}
current, err := req.Refs.ResolveFully(update.Name)
switch {
case err == nil:
case errors.Is(err, refstore.ErrReferenceNotFound):
continue
default:
return nil, fmt.Errorf("resolve %s: %w", update.Name, err)
}
if current.ID == update.NewID {
continue
}
ok, err := ancestor.Is(objects, nil, current.ID, update.NewID)
if err != nil {
return nil, fmt.Errorf("check fast-forward %s: %w", update.Name, err)
}
if !ok {
decisions[i] = receivepack.UpdateDecision{
Accept: false,
Message: "non-fast-forward",
}
}
}
return decisions, nil
}
}