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

     1  package commands
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path"
     7  	"path/filepath"
     8  	"strings"
     9  
    10  	"github.com/git-lfs/git-lfs/subprocess"
    11  
    12  	"github.com/git-lfs/git-lfs/git"
    13  	"github.com/git-lfs/git-lfs/tools"
    14  	"github.com/spf13/cobra"
    15  )
    16  
    17  var (
    18  	cloneFlags git.CloneFlags
    19  
    20  	cloneSkipRepoInstall bool
    21  )
    22  
    23  func cloneCommand(cmd *cobra.Command, args []string) {
    24  	requireGitVersion()
    25  
    26  	if git.IsGitVersionAtLeast("2.15.0") {
    27  		msg := []string{
    28  			"WARNING: 'git lfs clone' is deprecated and will not be updated",
    29  			"          with new flags from 'git clone'",
    30  			"",
    31  			"'git clone' has been updated in upstream Git to have comparable",
    32  			"speeds to 'git lfs clone'.",
    33  		}
    34  
    35  		fmt.Fprintln(os.Stderr, strings.Join(msg, "\n"))
    36  	}
    37  
    38  	// We pass all args to git clone
    39  	err := git.CloneWithoutFilters(cloneFlags, args)
    40  	if err != nil {
    41  		Exit("Error(s) during clone:\n%v", err)
    42  	}
    43  
    44  	// now execute pull (need to be inside dir)
    45  	cwd, err := tools.Getwd()
    46  	if err != nil {
    47  		Exit("Unable to derive current working dir: %v", err)
    48  	}
    49  
    50  	// Either the last argument was a relative or local dir, or we have to
    51  	// derive it from the clone URL
    52  	clonedir, err := filepath.Abs(args[len(args)-1])
    53  	if err != nil || !tools.DirExists(clonedir) {
    54  		// Derive from clone URL instead
    55  		base := path.Base(args[len(args)-1])
    56  		if strings.HasSuffix(base, ".git") {
    57  			base = base[:len(base)-4]
    58  		}
    59  		clonedir, _ = filepath.Abs(base)
    60  		if !tools.DirExists(clonedir) {
    61  			Exit("Unable to find clone dir at %q", clonedir)
    62  		}
    63  	}
    64  
    65  	err = os.Chdir(clonedir)
    66  	if err != nil {
    67  		Exit("Unable to change directory to clone dir %q: %v", clonedir, err)
    68  	}
    69  
    70  	// Make sure we pop back to dir we started in at the end
    71  	defer os.Chdir(cwd)
    72  
    73  	requireInRepo()
    74  
    75  	// Support --origin option to clone
    76  	if len(cloneFlags.Origin) > 0 {
    77  		cfg.SetRemote(cloneFlags.Origin)
    78  	}
    79  
    80  	if ref, err := git.CurrentRef(); err == nil {
    81  		includeArg, excludeArg := getIncludeExcludeArgs(cmd)
    82  		filter := buildFilepathFilter(cfg, includeArg, excludeArg)
    83  		if cloneFlags.NoCheckout || cloneFlags.Bare {
    84  			// If --no-checkout or --bare then we shouldn't check out, just fetch instead
    85  			fetchRef(ref.Name, filter)
    86  		} else {
    87  			pull(filter)
    88  			err := postCloneSubmodules(args)
    89  			if err != nil {
    90  				Exit("Error performing 'git lfs pull' for submodules: %v", err)
    91  			}
    92  		}
    93  	}
    94  
    95  	if !cloneSkipRepoInstall {
    96  		// If --skip-repo wasn't given, install repo-level hooks while
    97  		// we're still in the checkout directory.
    98  
    99  		if err := installHooks(false); err != nil {
   100  			ExitWithError(err)
   101  		}
   102  	}
   103  }
   104  
   105  func postCloneSubmodules(args []string) error {
   106  	// In git 2.9+ the filter option will have been passed through to submodules
   107  	// So we need to lfs pull inside each
   108  	if !git.IsGitVersionAtLeast("2.9.0") {
   109  		// In earlier versions submodules would have used smudge filter
   110  		return nil
   111  	}
   112  	// Also we only do this if --recursive or --recurse-submodules was provided
   113  	if !cloneFlags.Recursive && !cloneFlags.RecurseSubmodules {
   114  		return nil
   115  	}
   116  
   117  	// Use `git submodule foreach --recursive` to cascade into nested submodules
   118  	// Also good to call a new instance of git-lfs rather than do things
   119  	// inside this instance, since that way we get a clean env in that subrepo
   120  	cmd := subprocess.ExecCommand("git", "submodule", "foreach", "--recursive",
   121  		"git lfs pull")
   122  	cmd.Stderr = os.Stderr
   123  	cmd.Stdin = os.Stdin
   124  	cmd.Stdout = os.Stdout
   125  	return cmd.Run()
   126  }
   127  
   128  func init() {
   129  	RegisterCommand("clone", cloneCommand, func(cmd *cobra.Command) {
   130  		cmd.PreRun = nil
   131  
   132  		// Mirror all git clone flags
   133  		cmd.Flags().StringVarP(&cloneFlags.TemplateDirectory, "template", "", "", "See 'git clone --help'")
   134  		cmd.Flags().BoolVarP(&cloneFlags.Local, "local", "l", false, "See 'git clone --help'")
   135  		cmd.Flags().BoolVarP(&cloneFlags.Shared, "shared", "s", false, "See 'git clone --help'")
   136  		cmd.Flags().BoolVarP(&cloneFlags.NoHardlinks, "no-hardlinks", "", false, "See 'git clone --help'")
   137  		cmd.Flags().BoolVarP(&cloneFlags.Quiet, "quiet", "q", false, "See 'git clone --help'")
   138  		cmd.Flags().BoolVarP(&cloneFlags.NoCheckout, "no-checkout", "n", false, "See 'git clone --help'")
   139  		cmd.Flags().BoolVarP(&cloneFlags.Progress, "progress", "", false, "See 'git clone --help'")
   140  		cmd.Flags().BoolVarP(&cloneFlags.Bare, "bare", "", false, "See 'git clone --help'")
   141  		cmd.Flags().BoolVarP(&cloneFlags.Mirror, "mirror", "", false, "See 'git clone --help'")
   142  		cmd.Flags().StringVarP(&cloneFlags.Origin, "origin", "o", "", "See 'git clone --help'")
   143  		cmd.Flags().StringVarP(&cloneFlags.Branch, "branch", "b", "", "See 'git clone --help'")
   144  		cmd.Flags().StringVarP(&cloneFlags.Upload, "upload-pack", "u", "", "See 'git clone --help'")
   145  		cmd.Flags().StringVarP(&cloneFlags.Reference, "reference", "", "", "See 'git clone --help'")
   146  		cmd.Flags().StringVarP(&cloneFlags.ReferenceIfAble, "reference-if-able", "", "", "See 'git clone --help'")
   147  		cmd.Flags().BoolVarP(&cloneFlags.Dissociate, "dissociate", "", false, "See 'git clone --help'")
   148  		cmd.Flags().StringVarP(&cloneFlags.SeparateGit, "separate-git-dir", "", "", "See 'git clone --help'")
   149  		cmd.Flags().StringVarP(&cloneFlags.Depth, "depth", "", "", "See 'git clone --help'")
   150  		cmd.Flags().BoolVarP(&cloneFlags.Recursive, "recursive", "", false, "See 'git clone --help'")
   151  		cmd.Flags().BoolVarP(&cloneFlags.RecurseSubmodules, "recurse-submodules", "", false, "See 'git clone --help'")
   152  		cmd.Flags().StringVarP(&cloneFlags.Config, "config", "c", "", "See 'git clone --help'")
   153  		cmd.Flags().BoolVarP(&cloneFlags.SingleBranch, "single-branch", "", false, "See 'git clone --help'")
   154  		cmd.Flags().BoolVarP(&cloneFlags.NoSingleBranch, "no-single-branch", "", false, "See 'git clone --help'")
   155  		cmd.Flags().BoolVarP(&cloneFlags.Verbose, "verbose", "v", false, "See 'git clone --help'")
   156  		cmd.Flags().BoolVarP(&cloneFlags.Ipv4, "ipv4", "", false, "See 'git clone --help'")
   157  		cmd.Flags().BoolVarP(&cloneFlags.Ipv6, "ipv6", "", false, "See 'git clone --help'")
   158  		cmd.Flags().StringVarP(&cloneFlags.ShallowSince, "shallow-since", "", "", "See 'git clone --help'")
   159  		cmd.Flags().StringVarP(&cloneFlags.ShallowExclude, "shallow-exclude", "", "", "See 'git clone --help'")
   160  		cmd.Flags().BoolVarP(&cloneFlags.ShallowSubmodules, "shallow-submodules", "", false, "See 'git clone --help'")
   161  		cmd.Flags().BoolVarP(&cloneFlags.NoShallowSubmodules, "no-shallow-submodules", "", false, "See 'git clone --help'")
   162  		cmd.Flags().Int64VarP(&cloneFlags.Jobs, "jobs", "j", -1, "See 'git clone --help'")
   163  
   164  		cmd.Flags().StringVarP(&includeArg, "include", "I", "", "Include a list of paths")
   165  		cmd.Flags().StringVarP(&excludeArg, "exclude", "X", "", "Exclude a list of paths")
   166  
   167  		cmd.Flags().BoolVar(&cloneSkipRepoInstall, "skip-repo", false, "Skip LFS repo setup")
   168  	})
   169  }