Check that files are not uncommitted before unlock

This commit is contained in:
Steve Streeting 2017-01-30 16:49:56 +00:00
parent 7e495f5cf7
commit 7616e56760
3 changed files with 131 additions and 0 deletions

@ -4,6 +4,8 @@ import (
"encoding/json"
"os"
"github.com/git-lfs/git-lfs/git"
"github.com/git-lfs/git-lfs/locking"
"github.com/spf13/cobra"
)
@ -31,11 +33,16 @@ func unlockCommand(cmd *cobra.Command, args []string) {
Exit("Unable to determine path: %v", err.Error())
}
unlockCheckFileStatus(path)
err = lockClient.UnlockFile(path, unlockCmdFlags.Force)
if err != nil {
Exit("Unable to unlock: %v", err.Error())
}
} else if unlockCmdFlags.Id != "" {
unlockCheckFileStatusById(unlockCmdFlags.Id, lockClient)
err := lockClient.UnlockFileById(unlockCmdFlags.Id, unlockCmdFlags.Force)
if err != nil {
Exit("Unable to unlock %v: %v", unlockCmdFlags.Id, err.Error())
@ -55,6 +62,41 @@ func unlockCommand(cmd *cobra.Command, args []string) {
Print("'%s' was unlocked", args[0])
}
func unlockCheckFileStatus(path string) {
modified, err := git.IsFileModified(path)
if err != nil {
Exit(err.Error())
}
if modified {
if unlockCmdFlags.Force {
// Only a warning
Error("Warning: unlocking with uncommitted changes because --force")
} else {
Exit("Cannot unlock file with uncommitted changes")
}
}
}
func unlockCheckFileStatusById(id string, lockClient *locking.Client) {
// Get the path so we can check the status
filter := map[string]string{"id": id}
// try local cache first
locks, _ := lockClient.SearchLocks(filter, 0, true)
if len(locks) == 0 {
// Fall back on calling server
locks, _ = lockClient.SearchLocks(filter, 0, false)
}
if len(locks) > 0 {
unlockCheckFileStatus(locks[0].Path)
}
// Don't block if we can't determine the path, may be cleaning up old data
}
func init() {
if !isCommandEnabled(cfg, "locks") {
return

@ -1092,3 +1092,43 @@ func GetFilesChanged(from, to string) ([]string, error) {
return files, err
}
// IsFileModified returns whether the filepath specified is modified according
// to `git status`. A file is modified if it has uncommitted changes in the
// working copy or the index. This includes being untracked.
func IsFileModified(filepath string) (bool, error) {
args := []string{
"-c", "core.quotepath=false", // handle special chars in filenames
"status",
"--porcelain",
"--", // separator in case filename ambiguous
filepath,
}
cmd := subprocess.ExecCommand("git", args...)
outp, err := cmd.StdoutPipe()
if err != nil {
return false, fmt.Errorf("Failed to call git status: %v", err)
}
if err := cmd.Start(); err != nil {
return false, fmt.Errorf("Failed to start git status: %v", err)
}
scanner := bufio.NewScanner(outp)
for scanner.Scan() {
line := scanner.Text()
// Porcelain format is "<I><W> <filename>"
// Where <I> = index status, <W> = working copy status
if len(line) > 3 {
// Double-check even though should be only match
if strings.TrimSpace(line[3:]) == filepath {
return true, nil
}
}
}
if err := cmd.Wait(); err != nil {
return false, fmt.Errorf("Git status failed: %v", err)
}
return false, nil
}

@ -72,3 +72,52 @@ begin_test "unlocking a lock without sufficient info"
assert_server_lock "$reponame" "$id"
)
end_test
begin_test "unlocking a lock while uncommitted"
(
set -e
reponame="unlock_modified"
setup_remote_repo_with_file "$reponame" "f.dat"
GITLFSLOCKSENABLED=1 git lfs lock "f.dat" | tee lock.log
id=$(grep -oh "\((.*)\)" lock.log | tr -d "()")
assert_server_lock "$reponame" "$id"
echo "\nSomething" >> f.dat
GITLFSLOCKSENABLED=1 git lfs unlock "f.dat" 2>&1 | tee unlock.log
[ ${PIPESTATUS[0]} -ne "0" ]
grep "Cannot unlock file with uncommitted changes" unlock.log
assert_server_lock "$reponame" "$id"
# should allow after discard
git checkout f.dat
GITLFSLOCKSENABLED=1 git lfs unlock "f.dat" 2>&1 | tee unlock.log
refute_server_lock "$reponame" "$id"
)
end_test
begin_test "unlocking a lock while uncommitted with --force"
(
set -e
reponame="unlock_modified_force"
setup_remote_repo_with_file "$reponame" "g.dat"
GITLFSLOCKSENABLED=1 git lfs lock "g.dat" | tee lock.log
id=$(grep -oh "\((.*)\)" lock.log | tr -d "()")
assert_server_lock "$reponame" "$id"
echo "\nSomething" >> g.dat
# should allow with --force
GITLFSLOCKSENABLED=1 git lfs unlock --force "g.dat" 2>&1 | tee unlock.log
grep "Warning: unlocking with uncommitted changes" unlock.log
refute_server_lock "$reponame" "$id"
)
end_test