From 589f04dd9f7a4f07961824cc4a74f6b22c7cfaae Mon Sep 17 00:00:00 2001 From: Taylor Blau Date: Thu, 23 Mar 2017 10:54:52 -0600 Subject: [PATCH] commands/clone: install repo-level hooks after `git lfs clone` --- commands/command_clone.go | 14 ++++++++++++++ docs/man/git-lfs-clone.1.ronn | 8 ++++++++ test/test-clone.sh | 14 ++++++++++++++ test/testhelpers.sh | 22 ++++++++++++++++++++++ 4 files changed, 58 insertions(+) diff --git a/commands/command_clone.go b/commands/command_clone.go index a22c9232..8eb14e76 100644 --- a/commands/command_clone.go +++ b/commands/command_clone.go @@ -6,6 +6,7 @@ import ( "path/filepath" "strings" + "github.com/git-lfs/git-lfs/lfs" "github.com/git-lfs/git-lfs/localstorage" "github.com/git-lfs/git-lfs/subprocess" @@ -16,6 +17,8 @@ import ( var ( cloneFlags git.CloneFlags + + cloneSkipRepoInstall bool ) func cloneCommand(cmd *cobra.Command, args []string) { @@ -82,6 +85,15 @@ func cloneCommand(cmd *cobra.Command, args []string) { Exit("Error performing 'git lfs pull' for submodules: %v", err) } } + + if !cloneSkipRepoInstall { + // If --skip-repo wasn't given, install repo-level hooks while + // we're still in the checkout directory. + + if err := lfs.InstallHooks(false); err != nil { + ExitWithError(err) + } + } } func postCloneSubmodules(args []string) error { @@ -139,5 +151,7 @@ func init() { cmd.Flags().StringVarP(&includeArg, "include", "I", "", "Include a list of paths") cmd.Flags().StringVarP(&excludeArg, "exclude", "X", "", "Exclude a list of paths") + + cmd.Flags().BoolVar(&cloneSkipRepoInstall, "skip-repo", false, "Skip LFS repo setup") }) } diff --git a/docs/man/git-lfs-clone.1.ronn b/docs/man/git-lfs-clone.1.ronn index ac257d46..d6c5c6c3 100644 --- a/docs/man/git-lfs-clone.1.ronn +++ b/docs/man/git-lfs-clone.1.ronn @@ -10,6 +10,10 @@ git-lfs-clone(1) -- Efficiently clone a LFS-enabled repository Clone an LFS enabled Git repository more efficiently by disabling LFS during the git clone, then performing a 'git lfs pull' directly afterwards. +'git lfs clone' also installs all of the repo-level hooks (.git/hooks) that LFS +requires to operate. If `--separate-git-dir` is given, the hooks will be +installed there. + This is faster than a regular 'git clone' because that will download LFS content using the smudge filter, which is executed individually per file in the working copy. This is relatively inefficient compared to the batch mode and parallel @@ -25,6 +29,10 @@ All options supported by 'git clone' * `-X` `--exclude=`: See [INCLUDE AND EXCLUDE] +* `--skip-repo`: + Skip installing repo-level hooks (.git/hooks) that LFS requires. Disabled by + default. + ## INCLUDE AND EXCLUDE You can configure Git LFS to only fetch objects to satisfy references in certain diff --git a/test/test-clone.sh b/test/test-clone.sh index b404d62d..93f3e2b9 100755 --- a/test/test-clone.sh +++ b/test/test-clone.sh @@ -58,6 +58,7 @@ begin_test "clone" [ $(wc -c < "file1.dat") -eq 110 ] [ $(wc -c < "file2.dat") -eq 75 ] [ $(wc -c < "file3.dat") -eq 66 ] + assert_hooks "$(dot_git_dir)" [ ! -e "lfs" ] popd # Now check clone with implied dir @@ -74,6 +75,7 @@ begin_test "clone" [ $(wc -c < "file1.dat") -eq 110 ] [ $(wc -c < "file2.dat") -eq 75 ] [ $(wc -c < "file3.dat") -eq 66 ] + assert_hooks "$(dot_git_dir)" [ ! -e "lfs" ] popd @@ -130,6 +132,7 @@ begin_test "cloneSSL" [ $(wc -c < "file1.dat") -eq 100 ] [ $(wc -c < "file2.dat") -eq 75 ] [ $(wc -c < "file3.dat") -eq 30 ] + assert_hooks "$(dot_git_dir)" popd @@ -188,6 +191,7 @@ begin_test "clone ClientCert" [ $(wc -c < "file1.dat") -eq 100 ] [ $(wc -c < "file2.dat") -eq 75 ] [ $(wc -c < "file3.dat") -eq 30 ] + assert_hooks "$(dot_git_dir)" popd @@ -257,6 +261,7 @@ begin_test "clone with flags" [ -e "fileonbranch2.dat" ] # confirm remote is called differentorigin git remote get-url differentorigin + assert_hooks "$(dot_git_dir)" popd rm -rf "$newclonedir" @@ -269,6 +274,7 @@ begin_test "clone with flags" fi [ -e "$newclonedir/.git" ] [ -d "$gitdir/objects" ] + assert_hooks "$gitdir" rm -rf "$newclonedir" rm -rf "$gitdir" @@ -329,6 +335,7 @@ begin_test "clone (with include/exclude args)" [ "a" = "$(cat a-dupe.dat)" ] [ "$(pointer $contents_a_oid 1)" = "$(cat dupe-a.dat)" ] [ "$(pointer $contents_b_oid 1)" = "$(cat b.dat)" ] + assert_hooks "$(dot_git_dir)" popd local_reponame="clone_with_excludes" @@ -338,6 +345,7 @@ begin_test "clone (with include/exclude args)" refute_local_object "$contents_a_oid" [ "$(pointer $contents_a_oid 1)" = "$(cat a.dat)" ] [ "b" = "$(cat b.dat)" ] + assert_hooks "$(dot_git_dir)" popd ) end_test @@ -401,6 +409,7 @@ begin_test "clone (with .lfsconfig)" pushd "$local_reponame" assert_local_object "$contents_a_oid" 1 refute_local_object "$contents_b_oid" + assert_hooks "$(dot_git_dir)" popd echo "test: clone with lfs.fetchinclude in .lfsconfig, and args" @@ -409,6 +418,7 @@ begin_test "clone (with .lfsconfig)" pushd "$local_reponame" refute_local_object "$contents_a_oid" assert_local_object "$contents_b_oid" 1 + assert_hooks "$(dot_git_dir)" popd popd @@ -431,6 +441,7 @@ begin_test "clone (with .lfsconfig)" cat ".lfsconfig" assert_local_object "$contents_b_oid" 1 refute_local_object "$contents_a_oid" + assert_hooks "$(dot_git_dir)" popd echo "test: clone with lfs.fetchexclude in .lfsconfig, and args" @@ -439,6 +450,7 @@ begin_test "clone (with .lfsconfig)" pushd "$local_reponame" assert_local_object "$contents_a_oid" 1 refute_local_object "$contents_b_oid" + assert_hooks "$(dot_git_dir)" popd popd @@ -506,6 +518,7 @@ begin_test "clone with submodules" # check everything is where it should be cd $local_reponame + assert_hooks "$(dot_git_dir)" # check LFS store and working copy assert_local_object "$contents_root_oid" "${#contents_root}" [ $(wc -c < "root.dat") -eq ${#contents_root} ] @@ -557,6 +570,7 @@ begin_test "clone in current directory" git lfs clone $GITSERVER/$reponame "." 2>&1 | grep "Git LFS" assert_local_object "$contents_oid" 8 + assert_hooks "$(dot_git_dir)" [ ! -f ./lfs ] popd ) diff --git a/test/testhelpers.sh b/test/testhelpers.sh index 81c37099..d98337c9 100644 --- a/test/testhelpers.sh +++ b/test/testhelpers.sh @@ -194,6 +194,28 @@ refute_file_writable() { ls -l "$1" | grep -e "^-r-" } +git_root() { + git rev-parse --show-toplevel 2>/dev/null +} + +dot_git_dir() { + echo "$(git_root)/.git" +} + +assert_hooks() { + local git_root="$1" + + if [ -z "$git_root" ]; then + echo >&2 "fatal: (assert_hooks) not in git repository" + exit 1 + fi + + [ -x "$git_root/hooks/post-checkout" ] + [ -x "$git_root/hooks/post-commit" ] + [ -x "$git_root/hooks/post-merge" ] + [ -x "$git_root/hooks/pre-push" ] +} + # pointer returns a string Git LFS pointer file. # # $ pointer abc-some-oid 123