github.com/saracen/git-lfs@v2.5.2+incompatible/commands/command_unlock.go (about)

     1  package commands
     2  
     3  import (
     4  	"encoding/json"
     5  	"os"
     6  
     7  	"github.com/git-lfs/git-lfs/errors"
     8  	"github.com/git-lfs/git-lfs/git"
     9  	"github.com/git-lfs/git-lfs/locking"
    10  	"github.com/spf13/cobra"
    11  )
    12  
    13  var (
    14  	unlockCmdFlags unlockFlags
    15  )
    16  
    17  // unlockFlags holds the flags given to the `git lfs unlock` command
    18  type unlockFlags struct {
    19  	// Id is the Id of the lock that is being unlocked.
    20  	Id string
    21  	// Force specifies whether or not the `lfs unlock` command was invoked
    22  	// with "--force", signifying the user's intent to break another
    23  	// individual's lock(s).
    24  	Force bool
    25  }
    26  
    27  var unlockUsage = "Usage: git lfs unlock (--id my-lock-id | <path>)"
    28  
    29  func unlockCommand(cmd *cobra.Command, args []string) {
    30  	hasPath := len(args) > 0
    31  	hasId := len(unlockCmdFlags.Id) > 0
    32  	if hasPath == hasId {
    33  		// If there is both an `--id` AND a `<path>`, or there is
    34  		// neither, print the usage and quit.
    35  		Exit(unlockUsage)
    36  	}
    37  
    38  	if len(lockRemote) > 0 {
    39  		cfg.SetRemote(lockRemote)
    40  	}
    41  
    42  	refUpdate := git.NewRefUpdate(cfg.Git, cfg.PushRemote(), cfg.CurrentRef(), nil)
    43  	lockClient := newLockClient()
    44  	lockClient.RemoteRef = refUpdate.Right()
    45  	defer lockClient.Close()
    46  
    47  	if hasPath {
    48  		path, err := lockPath(args[0])
    49  		if err != nil {
    50  			if !unlockCmdFlags.Force {
    51  				Exit("Unable to determine path: %v", err.Error())
    52  			}
    53  			path = args[0]
    54  		}
    55  
    56  		// This call can early-out
    57  		unlockAbortIfFileModified(path, !os.IsNotExist(err))
    58  
    59  		err = lockClient.UnlockFile(path, unlockCmdFlags.Force)
    60  		if err != nil {
    61  			Exit("%s", errors.Cause(err))
    62  		}
    63  
    64  		if !locksCmdFlags.JSON {
    65  			Print("Unlocked %s", path)
    66  			return
    67  		}
    68  	} else if unlockCmdFlags.Id != "" {
    69  		// This call can early-out
    70  		unlockAbortIfFileModifiedById(unlockCmdFlags.Id, lockClient)
    71  
    72  		err := lockClient.UnlockFileById(unlockCmdFlags.Id, unlockCmdFlags.Force)
    73  		if err != nil {
    74  			Exit("Unable to unlock %v: %v", unlockCmdFlags.Id, errors.Cause(err))
    75  		}
    76  
    77  		if !locksCmdFlags.JSON {
    78  			Print("Unlocked Lock %s", unlockCmdFlags.Id)
    79  			return
    80  		}
    81  	} else {
    82  		Error(unlockUsage)
    83  	}
    84  
    85  	if err := json.NewEncoder(os.Stdout).Encode(struct {
    86  		Unlocked bool `json:"unlocked"`
    87  	}{true}); err != nil {
    88  		Error(err.Error())
    89  	}
    90  	return
    91  }
    92  
    93  func unlockAbortIfFileModified(path string, exists bool) {
    94  	modified, err := git.IsFileModified(path)
    95  
    96  	if err != nil {
    97  		if !exists && unlockCmdFlags.Force {
    98  			// Since git/git@b9a7d55, `git-status(1)` causes an
    99  			// error when asked about files that don't exist,
   100  			// causing `err != nil`, as above.
   101  			//
   102  			// Unlocking a files that does not exist with
   103  			// --force is OK.
   104  			return
   105  		}
   106  		Exit(err.Error())
   107  	}
   108  
   109  	if modified {
   110  		if unlockCmdFlags.Force {
   111  			// Only a warning
   112  			Error("Warning: unlocking with uncommitted changes because --force")
   113  		} else {
   114  			Exit("Cannot unlock file with uncommitted changes")
   115  		}
   116  
   117  	}
   118  }
   119  
   120  func unlockAbortIfFileModifiedById(id string, lockClient *locking.Client) {
   121  	// Get the path so we can check the status
   122  	filter := map[string]string{"id": id}
   123  	// try local cache first
   124  	locks, _ := lockClient.SearchLocks(filter, 0, true)
   125  	if len(locks) == 0 {
   126  		// Fall back on calling server
   127  		locks, _ = lockClient.SearchLocks(filter, 0, false)
   128  	}
   129  
   130  	if len(locks) == 0 {
   131  		// Don't block if we can't determine the path, may be cleaning up old data
   132  		return
   133  	}
   134  
   135  	unlockAbortIfFileModified(locks[0].Path, true)
   136  }
   137  
   138  func init() {
   139  	RegisterCommand("unlock", unlockCommand, func(cmd *cobra.Command) {
   140  		cmd.Flags().StringVarP(&lockRemote, "remote", "r", "", lockRemoteHelp)
   141  		cmd.Flags().StringVarP(&unlockCmdFlags.Id, "id", "i", "", "unlock a lock by its ID")
   142  		cmd.Flags().BoolVarP(&unlockCmdFlags.Force, "force", "f", false, "forcibly break another user's lock(s)")
   143  		cmd.Flags().BoolVarP(&locksCmdFlags.JSON, "json", "", false, "print output in json")
   144  	})
   145  }