diff --git a/commands/command_init.go b/commands/command_init.go index bf1bba3a..f6595b06 100644 --- a/commands/command_init.go +++ b/commands/command_init.go @@ -17,18 +17,21 @@ var ( Short: "Initialize hooks for the current repository", Run: initHooksCommand, } + + forceInit = false ) func initCommand(cmd *cobra.Command, args []string) { - if err := lfs.InstallFilters(); err != nil { + if err := lfs.InstallFilters(forceInit); err != nil { Error(err.Error()) + Exit("Run `git lfs init --force` to reset git config.") } if lfs.InRepo() { initHooksCommand(cmd, args) } - Print("git lfs initialized") + Print("Git LFS initialized") } func initHooksCommand(cmd *cobra.Command, args []string) { @@ -38,6 +41,7 @@ func initHooksCommand(cmd *cobra.Command, args []string) { } func init() { + initCmd.Flags().BoolVarP(&forceInit, "force", "f", false, "Set the Git LFS global config, overwriting previous values.") initCmd.AddCommand(initHooksCmd) RootCmd.AddCommand(initCmd) } diff --git a/commands/commands_uninit.go b/commands/commands_uninit.go new file mode 100644 index 00000000..44bfaaa0 --- /dev/null +++ b/commands/commands_uninit.go @@ -0,0 +1,45 @@ +package commands + +import ( + "github.com/github/git-lfs/lfs" + "github.com/github/git-lfs/vendor/_nuts/github.com/spf13/cobra" +) + +var ( + uninitCmd = &cobra.Command{ + Use: "uninit", + Short: "Clear the Git LFS configuration", + Run: uninitCommand, + } + + uninitHooksCmd = &cobra.Command{ + Use: "hooks", + Short: "Clear only the Git hooks for the current repository", + Run: uninitHooksCommand, + } +) + +func uninitCommand(cmd *cobra.Command, args []string) { + if err := lfs.UninstallFilters(); err != nil { + Error(err.Error()) + } + + Print("Global Git LFS configuration has been removed.") + + if lfs.InRepo() { + uninitHooksCommand(cmd, args) + } +} + +func uninitHooksCommand(cmd *cobra.Command, args []string) { + if err := lfs.UninstallHooks(); err != nil { + Error(err.Error()) + } + + Print("Hooks for this repository have been removed.") +} + +func init() { + uninitCmd.AddCommand(uninitHooksCmd) + RootCmd.AddCommand(uninitCmd) +} diff --git a/docs/man/git-lfs-init.1.ronn b/docs/man/git-lfs-init.1.ronn index 421bf388..12168ff0 100644 --- a/docs/man/git-lfs-init.1.ronn +++ b/docs/man/git-lfs-init.1.ronn @@ -3,15 +3,28 @@ git-lfs-init(1) -- Ensure Git LFS is configured properly ## SYNOPSIS -`git lfs init` +`git lfs init`
+`git lfs init` --force ## DESCRIPTION Perform the following actions to ensure that Git LFS is setup properly: -* Set up the clean and smudge filters under the name "lfs". -* Install a pre-push hook to run git-lfs-push(1) +* Set up the clean and smudge filters under the name "lfs" in the global Git + config. +* Install a pre-push hook to run git-lfs-pre-push(1) for the current repository, + if run from inside one. + +## OPTIONS + +Without any options, `git lfs init` will only setup the "lfs" smudge and clean +filters if they are not already set. + +* `--force`: + Sets the "lfs" smudge and clean filters, overwriting existing values. ## SEE ALSO +git-lfs-uninit(1). + Part of the git-lfs(1) suite. diff --git a/docs/man/git-lfs-uninit.1.ronn b/docs/man/git-lfs-uninit.1.ronn new file mode 100644 index 00000000..e14644e7 --- /dev/null +++ b/docs/man/git-lfs-uninit.1.ronn @@ -0,0 +1,19 @@ +git-lfs-uninit(1) -- Remove Git LFS configuration +================================================= + +## SYNOPSIS + +`git lfs uninit` + +## DESCRIPTION + +Perform the following actions to remove the Git LFS configuration: + +* Remove the "lfs" clean and smudge filters from the global Git config. +* Uninstall the Git LFS pre-push hook if run from inside a Git repository. + +## SEE ALSO + +git-lfs-init(1). + +Part of the git-lfs(1) suite. diff --git a/lfs/setup.go b/lfs/setup.go index 0d97150b..e9f07f33 100644 --- a/lfs/setup.go +++ b/lfs/setup.go @@ -52,6 +52,32 @@ func InstallHooks(force bool) error { return ioutil.WriteFile(hookPath, []byte(prePushHook+"\n"), 0755) } +func UninstallHooks() error { + if !InRepo() { + return NotInARepositoryError + } + + prePushHookPath := filepath.Join(LocalGitDir, "hooks", "pre-push") + file, err := os.Open(prePushHookPath) + if err != nil { + // hook doesn't exist, our work here is done + return nil + } + + by, err := ioutil.ReadAll(io.LimitReader(file, 1024)) + file.Close() + if err != nil { + return err + } + + contents := strings.TrimSpace(string(by)) + if contents == prePushHook || prePushUpgrades[contents] { + return os.RemoveAll(prePushHookPath) + } + + return nil +} + func upgradeHookOrError(hookPath, hookName, hook string, upgrades map[string]bool) error { file, err := os.Open(hookPath) if err != nil { @@ -76,25 +102,35 @@ func upgradeHookOrError(hookPath, hookName, hook string, upgrades map[string]boo return &HookExists{hookName, hookPath, contents} } -func InstallFilters() error { - if err := setFilter("clean"); err != nil { +func InstallFilters(force bool) error { + if err := setFilter("clean", force); err != nil { return err } - if err := setFilter("smudge"); err != nil { + if err := setFilter("smudge", force); err != nil { return err } - if err := requireFilters(); err != nil { + if err := requireFilters(force); err != nil { return err } return nil } -func setFilter(filterName string) error { +func UninstallFilters() error { + keys := []string{"clean", "smudge", "required"} + keyFmt := "filter.lfs.%s" + for _, name := range keys { + key := fmt.Sprintf(keyFmt, name) + git.Config.UnsetGlobal(key) + } + return nil +} + +func setFilter(filterName string, force bool) error { key := fmt.Sprintf("filter.lfs.%s", filterName) value := fmt.Sprintf("git-lfs %s %%f", filterName) existing := git.Config.Find(key) - if shouldReset(existing) { + if force || shouldReset(existing) { git.Config.UnsetGlobal(key) git.Config.SetGlobal(key, value) } else if existing != value { @@ -104,12 +140,12 @@ func setFilter(filterName string) error { return nil } -func requireFilters() error { +func requireFilters(force bool) error { key := "filter.lfs.required" value := "true" existing := git.Config.Find(key) - if shouldReset(existing) { + if force || shouldReset(existing) { git.Config.UnsetGlobal(key) git.Config.SetGlobal(key, value) } else if existing != value { diff --git a/test/test-init.sh b/test/test-init.sh index 5d23ae98..10d1aae2 100755 --- a/test/test-init.sh +++ b/test/test-init.sh @@ -34,11 +34,21 @@ begin_test "init with old settings" git config --global filter.lfs.smudge "git lfs smudge %f" git config --global filter.lfs.clean "git lfs clean %f" + set +e git lfs init 2> init.log + res=$? + set -e + + [ "$res" = 2 ] grep "clean filter should be" init.log + [ `grep -c "(MISSING)" init.log` = "0" ] [ "git lfs smudge %f" = "$(git config filter.lfs.smudge)" ] [ "git lfs clean %f" = "$(git config filter.lfs.clean)" ] + + git lfs init --force + [ "git-lfs smudge %f" = "$(git config filter.lfs.smudge)" ] + [ "git-lfs clean %f" = "$(git config filter.lfs.clean)" ] ) end_test diff --git a/test/test-uninit.sh b/test/test-uninit.sh new file mode 100755 index 00000000..f212d8b0 --- /dev/null +++ b/test/test-uninit.sh @@ -0,0 +1,123 @@ +#!/bin/sh + +. "test/testlib.sh" + +begin_test "uninit outside repository" +( + set -e + + tmphome="$(basename "$0" ".sh")" + mkdir -p $tmphome + cp $HOME/.gitconfig $tmphome/ + HOME=$PWD/$tmphome + cd $HOME + + [ "git-lfs smudge %f" = "$(git config filter.lfs.smudge)" ] + [ "git-lfs clean %f" = "$(git config filter.lfs.clean)" ] + + git lfs uninit | tee uninit.log + grep "configuration has been removed" uninit.log + + [ "" = "$(git config filter.lfs.smudge)" ] + [ "" = "$(git config filter.lfs.clean)" ] +) +end_test + +begin_test "uninit inside repository with default pre-push hook" +( + set -e + + tmphome="$(basename "$0" ".sh")" + mkdir -p $tmphome + cp $HOME/.gitconfig $tmphome/ + HOME=$PWD/$tmphome + cd $HOME + + reponame="$(basename "$0" ".sh")-hook" + mkdir "$reponame" + cd "$reponame" + git init + git lfs init + + [ -f .git/hooks/pre-push ] + grep "git-lfs" .git/hooks/pre-push + + [ "git-lfs smudge %f" = "$(git config filter.lfs.smudge)" ] + [ "git-lfs clean %f" = "$(git config filter.lfs.clean)" ] + + git lfs uninit + + [ -f .git/hooks/pre-push ] && { + echo "expected .git/hooks/pre-push to be deleted" + exit 1 + } + [ "" = "$(git config filter.lfs.smudge)" ] + [ "" = "$(git config filter.lfs.clean)" ] +) +end_test + +begin_test "uninit inside repository without git lfs pre-push hook" +( + set -e + + tmphome="$(basename "$0" ".sh")" + mkdir -p $tmphome + cp $HOME/.gitconfig $tmphome/ + HOME=$PWD/$tmphome + cd $HOME + + reponame="$(basename "$0" ".sh")-no-hook" + mkdir "$reponame" + cd "$reponame" + git init + git lfs init + echo "something something git-lfs" > .git/hooks/pre-push + + + [ -f .git/hooks/pre-push ] + [ "something something git-lfs" = "$(cat .git/hooks/pre-push)" ] + + [ "git-lfs smudge %f" = "$(git config filter.lfs.smudge)" ] + [ "git-lfs clean %f" = "$(git config filter.lfs.clean)" ] + + git lfs uninit + + [ -f .git/hooks/pre-push ] + [ "" = "$(git config filter.lfs.smudge)" ] + [ "" = "$(git config filter.lfs.clean)" ] +) +end_test + +begin_test "uninit hooks inside repository" +( + set -e + + tmphome="$(basename "$0" ".sh")" + mkdir -p $tmphome + cp $HOME/.gitconfig $tmphome/ + HOME=$PWD/$tmphome + cd $HOME + + reponame="$(basename "$0" ".sh")-only-hook" + mkdir "$reponame" + cd "$reponame" + git init + git lfs init + + [ -f .git/hooks/pre-push ] + grep "git-lfs" .git/hooks/pre-push + + [ "git-lfs smudge %f" = "$(git config filter.lfs.smudge)" ] + [ "git-lfs clean %f" = "$(git config filter.lfs.clean)" ] + + git lfs uninit hooks + + [ -f .git/hooks/pre-push ] && { + echo "expected .git/hooks/pre-push to be deleted" + exit 1 + } + + [ "git-lfs smudge %f" = "$(git config filter.lfs.smudge)" ] + [ "git-lfs clean %f" = "$(git config filter.lfs.clean)" ] +) +end_test