95d1c1f737
When we form a lock path, we take the working directory and resolve all symlinks in it, and then append the name of the file specified on the command line. However, the working directory we get need not be canonicalized for the system: it may contain lowercase drive paths (on Windows) or components that differ in case or normalization from the canonical form written on the file system. We then made the path relative to the root of the repository by using the strings module to pull of the prefix of the Git repository, which will always be canonicalized. If the working directory was noncanonical, the paths would end up just concatenated together, leading to failures down the line. On Windows, this could lead to multiple drive components, and hence a syntax error when trying to use the path. Use the filepath.Rel function that's built into Go to help us make the path relative in a way that is appropriate for the operating system. Convert the path to slashes so that we store paths using slashes and expose only slashes to the remote API.
112 lines
2.6 KiB
Go
112 lines
2.6 KiB
Go
package commands
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/git-lfs/git-lfs/errors"
|
|
"github.com/git-lfs/git-lfs/git"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var (
|
|
lockRemote string
|
|
lockRemoteHelp = "specify which remote to use when interacting with locks"
|
|
)
|
|
|
|
func lockCommand(cmd *cobra.Command, args []string) {
|
|
if len(args) == 0 {
|
|
Print("Usage: git lfs lock <path>")
|
|
return
|
|
}
|
|
|
|
path, err := lockPath(args[0])
|
|
if err != nil {
|
|
Exit(err.Error())
|
|
}
|
|
|
|
if len(lockRemote) > 0 {
|
|
cfg.SetRemote(lockRemote)
|
|
}
|
|
|
|
refUpdate := git.NewRefUpdate(cfg.Git, cfg.PushRemote(), cfg.CurrentRef(), nil)
|
|
lockClient := newLockClient()
|
|
lockClient.RemoteRef = refUpdate.Right()
|
|
defer lockClient.Close()
|
|
|
|
lock, err := lockClient.LockFile(path)
|
|
if err != nil {
|
|
Exit("Lock failed: %v", errors.Cause(err))
|
|
}
|
|
|
|
if locksCmdFlags.JSON {
|
|
if err := json.NewEncoder(os.Stdout).Encode(lock); err != nil {
|
|
Error(err.Error())
|
|
}
|
|
return
|
|
}
|
|
|
|
Print("Locked %s", path)
|
|
}
|
|
|
|
// lockPaths relativizes the given filepath such that it is relative to the root
|
|
// path of the repository it is contained within, taking into account the
|
|
// working directory of the caller.
|
|
//
|
|
// lockPaths also respects different filesystem directory separators, so that a
|
|
// Windows path of "\foo\bar" will be normalized to "foo/bar".
|
|
//
|
|
// If the root directory, working directory, or file cannot be
|
|
// determined/opened, an error will be returned. If the file in question is
|
|
// actually a directory, an error will be returned. Otherwise, the cleaned path
|
|
// will be returned.
|
|
//
|
|
// For example:
|
|
// - Working directory: /code/foo/bar/
|
|
// - Repository root: /code/foo/
|
|
// - File to lock: ./baz
|
|
// - Resolved path bar/baz
|
|
func lockPath(file string) (string, error) {
|
|
repo, err := git.RootDir()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
wd, err := os.Getwd()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
wd, err = filepath.EvalSymlinks(wd)
|
|
if err != nil {
|
|
return "", errors.Wrapf(err,
|
|
"could not follow symlinks for %s", wd)
|
|
}
|
|
|
|
abs := filepath.Join(wd, file)
|
|
path, err := filepath.Rel(repo, abs)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
path = filepath.ToSlash(path)
|
|
|
|
if stat, err := os.Stat(abs); err != nil {
|
|
return "", err
|
|
} else {
|
|
if stat.IsDir() {
|
|
return path, fmt.Errorf("lfs: cannot lock directory: %s", file)
|
|
}
|
|
|
|
return filepath.ToSlash(path), nil
|
|
}
|
|
}
|
|
|
|
func init() {
|
|
RegisterCommand("lock", lockCommand, func(cmd *cobra.Command) {
|
|
cmd.Flags().StringVarP(&lockRemote, "remote", "r", "", lockRemoteHelp)
|
|
cmd.Flags().BoolVarP(&locksCmdFlags.JSON, "json", "", false, "print output in json")
|
|
})
|
|
}
|