package commands
import (
"io"
"os"
"sort"
"strings"
"github.com/git-lfs/git-lfs/v3/errors"
"github.com/git-lfs/git-lfs/v3/git"
"github.com/git-lfs/git-lfs/v3/locking"
"github.com/git-lfs/git-lfs/v3/tools"
"github.com/git-lfs/git-lfs/v3/tr"
"github.com/spf13/cobra"
)
var (
locksCmdFlags = new(locksFlags)
)
func locksCommand(cmd *cobra.Command, args []string) {
lockData, err := computeLockData()
if err != nil {
ExitWithError(err)
}
filters, err := locksCmdFlags.Filters(lockData)
if err != nil {
Exit(tr.Tr.Get("Error building filters: %v", err))
}
if len(lockRemote) > 0 {
cfg.SetRemote(lockRemote)
}
refUpdate := git.NewRefUpdate(cfg.Git, cfg.PushRemote(), cfg.CurrentRef(), nil)
lockClient := newLockClient()
lockClient.RemoteRef = refUpdate.RemoteRef()
defer lockClient.Close()
if locksCmdFlags.Cached {
if locksCmdFlags.Limit > 0 {
Exit(tr.Tr.Get("--cached option can't be combined with --limit"))
}
if len(filters) > 0 {
Exit(tr.Tr.Get("--cached option can't be combined with filters"))
}
if locksCmdFlags.Local {
Exit(tr.Tr.Get("--cached option can't be combined with --local"))
}
}
if locksCmdFlags.Verify {
if len(filters) > 0 {
Exit(tr.Tr.Get("--verify option can't be combined with filters"))
}
if locksCmdFlags.Local {
Exit(tr.Tr.Get("--verify option can't be combined with --local"))
}
}
var locks []locking.Lock
var locksOwned map[locking.Lock]bool
var jsonWriteFunc func(io.Writer) error
if locksCmdFlags.Verify {
var ourLocks, theirLocks []locking.Lock
ourLocks, theirLocks, err = lockClient.SearchLocksVerifiable(locksCmdFlags.Limit, locksCmdFlags.Cached)
jsonWriteFunc = func(writer io.Writer) error {
return lockClient.EncodeLocksVerifiable(ourLocks, theirLocks, writer)
}
locks = append(ourLocks, theirLocks...)
locksOwned = make(map[locking.Lock]bool)
for _, lock := range ourLocks {
locksOwned[lock] = true
}
} else {
locks, err = lockClient.SearchLocks(filters, locksCmdFlags.Limit, locksCmdFlags.Local, locksCmdFlags.Cached)
jsonWriteFunc = func(writer io.Writer) error {
return lockClient.EncodeLocks(locks, writer)
}
}
if locksCmdFlags.JSON {
if err := jsonWriteFunc(os.Stdout); err != nil {
Error(err.Error())
}
return
}
var maxPathLen int
var maxNameLen int
lockPaths := make([]string, 0, len(locks))
locksByPath := make(map[string]locking.Lock)
for _, lock := range locks {
lockPaths = append(lockPaths, lock.Path)
locksByPath[lock.Path] = lock
maxPathLen = tools.MaxInt(maxPathLen, len(lock.Path))
if lock.Owner != nil {
maxNameLen = tools.MaxInt(maxNameLen, len(lock.Owner.Name))
}
}
sort.Strings(lockPaths)
for _, lockPath := range lockPaths {
var ownerName string
lock := locksByPath[lockPath]
if lock.Owner != nil {
ownerName = lock.Owner.Name
}
pathPadding := tools.MaxInt(maxPathLen-len(lock.Path), 0)
namePadding := tools.MaxInt(maxNameLen-len(ownerName), 0)
kind := ""
if locksOwned != nil {
if locksOwned[lock] {
kind = "O "
} else {
kind = " "
}
}
Print("%s%s%s\t%s%s\tID:%s", kind, lock.Path, strings.Repeat(" ", pathPadding),
ownerName, strings.Repeat(" ", namePadding),
lock.Id,
)
}
if err != nil {
Exit(tr.Tr.Get("Error while retrieving locks: %v", errors.Cause(err)))
}
}
type locksFlags struct {
Path string
Id string
Limit int
Local bool
JSON bool
Cached bool
Verify bool
}
func (l *locksFlags) Filters(data *lockData) (map[string]string, error) {
filters := make(map[string]string)
if l.Path != "" {
path, err := lockPath(data, l.Path)
if err != nil {
return nil, err
}
filters["path"] = path
}
if l.Id != "" {
filters["id"] = l.Id
}
return filters, nil
}
func init() {
RegisterCommand("locks", locksCommand, func(cmd *cobra.Command) {
cmd.Flags().StringVarP(&lockRemote, "remote", "r", "", "specify which remote to use when interacting with locks")
cmd.Flags().StringVarP(&locksCmdFlags.Path, "path", "p", "", "filter locks results matching a particular path")
cmd.Flags().StringVarP(&locksCmdFlags.Id, "id", "i", "", "filter locks results matching a particular ID")
cmd.Flags().IntVarP(&locksCmdFlags.Limit, "limit", "l", 0, "optional limit for number of results to return")
cmd.Flags().BoolVarP(&locksCmdFlags.Local, "local", "", false, "only list cached local record of own locks")
cmd.Flags().BoolVarP(&locksCmdFlags.Cached, "cached", "", false, "list cached lock information from the last remote query, instead of actually querying the server")
cmd.Flags().BoolVarP(&locksCmdFlags.Verify, "verify", "", false, "verify lock owner on server and mark own locks by 'O'")
cmd.Flags().BoolVarP(&locksCmdFlags.JSON, "json", "", false, "print output in json")
})
}