Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,30 @@ git-profile use

`use` must be executed inside a Git repository.

### `unuse`

Remove the applied profile entries from the current Git repository:

```bash
git-profile unuse work
```

Under the hood, this unsets the local Git config values, for example:

```bash
git config --local --unset user.name
git config --local --unset user.email
git config --local --unset user.signingkey
```

Run without arguments to remove the currently applied profile:

```bash
git-profile unuse
```

`unuse` must be executed inside a Git repository.

### `current`

Show the currently selected profile for the current repository:
Expand Down
1 change: 1 addition & 0 deletions cmd/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ type vcs interface {
IsRepository() bool
Get(key string) (string, error)
Set(key string, value string) error
Unset(key string) error
}
44 changes: 44 additions & 0 deletions cmd/mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ func (c *Cmd) init() {
Export(c.config),
Import(c.config),
Use(c.config, c.git),
Unuse(c.config, c.git),
Version(c),
)

Expand Down
77 changes: 77 additions & 0 deletions cmd/unuse.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package cmd

import (
"fmt"
"os"

"github.com/spf13/cobra"

"github.com/dotzero/git-profile/internal/ui"
)

// Unuse returns `unuse` command
func Unuse(cfg storage, v vcs) *cobra.Command {
return unuseCommand(cfg, v)
}

func unuseCommand(cfg storage, v vcs) *cobra.Command {
return &cobra.Command{
Use: "unuse [profile]",
Aliases: []string{"uu"},
Short: "Unuse a profile",
Long: "Removes the applied profile entries from the current git repository.",
Example: multiline(
`git-profile unuse`,
`git-profile unuse my-profile`,
),
PreRun: func(cmd *cobra.Command, _ []string) {
check(cmd, cfg, v)
},
Run: func(cmd *cobra.Command, args []string) {
profile, err := unuseProfileResolve(args, v)
if err != nil {
ui.PrintErrln(cmd, ui.ErrorStyle, "%s", err)
os.Exit(0)
}

profileUnapply(cmd, cfg, v, profile)
},
}
}

func unuseProfileResolve(args []string, v vcs) (string, error) {
if len(args) > 0 {
return args[0], nil
}

profile, err := v.Get(currentProfileKey)
if len(profile) == 0 || err != nil {
return "", fmt.Errorf("There is no profile applied to current git repository")
}

return profile, nil
}

func profileUnapply(cmd *cobra.Command, cfg storage, v vcs, profile string) {
entries, ok := cfg.Lookup(profile)
if !ok {
ui.PrintErrln(cmd, ui.ErrorStyle, "There is no profile with `%s` name", profile)
os.Exit(0)
}

for _, entry := range entries {
err := v.Unset(entry.Key)
if err != nil {
ui.PrintErrln(cmd, ui.ErrorStyle, "Unable to interact with git to remove current profile: %s", err)
os.Exit(1)
}
}

err := v.Unset(currentProfileKey)
if err != nil {
ui.PrintErrln(cmd, ui.ErrorStyle, "Unable to interact with git to remove current profile: %s", err)
os.Exit(1)
}

ui.Println(cmd, ui.SuccessStyle, "Successfully removed `%s` profile from current git repository.", profile)
}
90 changes: 90 additions & 0 deletions cmd/unuse_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package cmd

import (
"bytes"
"fmt"
"testing"

"github.com/matryer/is"

"github.com/dotzero/git-profile/internal/config"
)

func TestUnuse(t *testing.T) {
is := is.New(t)

cfg := &storageMock{
LenFunc: func() int {
return 1
},
LookupFunc: func(name string) ([]config.Entry, bool) {
return []config.Entry{
{Key: "user.email", Value: "work@example.com"},
}, true
},
}

var unset []string

vcs := &vcsMock{
IsRepositoryFunc: func() bool {
return true
},
UnsetFunc: func(key string) error {
unset = append(unset, key)
return nil
},
}

var b bytes.Buffer

cmd := Unuse(cfg, vcs)

cmd.SetOut(&b)
cmd.SetArgs([]string{"profile"})
err := cmd.Execute()

is.NoErr(err)
is.Equal(trim(b.String()), "Successfully removed `profile` profile from current git repository.")
is.Equal(unset, []string{"user.email", currentProfileKey})
}

func TestUnuseProfileResolveArg(t *testing.T) {
is := is.New(t)

profile, err := unuseProfileResolve([]string{"work"}, &vcsMock{})

is.NoErr(err)
is.Equal(profile, "work")
}

func TestUnuseProfileResolveCurrent(t *testing.T) {
is := is.New(t)

vcs := &vcsMock{
GetFunc: func(key string) (string, error) {
is.Equal(key, currentProfileKey)
return "home", nil
},
}

profile, err := unuseProfileResolve(nil, vcs)

is.NoErr(err)
is.Equal(profile, "home")
}

func TestUnuseProfileResolveNoneApplied(t *testing.T) {
is := is.New(t)

vcs := &vcsMock{
GetFunc: func(_ string) (string, error) {
return "", fmt.Errorf("boom")
},
}

_, err := unuseProfileResolve(nil, vcs)

is.True(err != nil)
is.Equal(err.Error(), "There is no profile applied to current git repository")
}
21 changes: 21 additions & 0 deletions internal/git/git.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package git

import (
"errors"
"os/exec"
"strings"
)

// gitConfigKeyNotFound is the exit code git returns from
// `git config --unset` when the key is not present.
const gitConfigKeyNotFound = 5

// Git is a vcs
type Git struct {
exec func(name string, arg ...string) *exec.Cmd
Expand Down Expand Up @@ -37,3 +42,19 @@ func (g *Git) Set(key string, value string) error {

return err
}

// Unset removes a key from git local config.
// A missing key is not treated as an error.
func (g *Git) Unset(key string) error {
_, err := g.exec("git", "config", "--local", "--unset", key).CombinedOutput()
if err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) && exitErr.ExitCode() == gitConfigKeyNotFound {
return nil
}

return err
}

return nil
}
9 changes: 9 additions & 0 deletions internal/git/git_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,12 @@ func TestSet(t *testing.T) {

is.NoErr(err)
}

func TestUnset(t *testing.T) {
is := is.New(t)

g := &Git{exec: mockExecCommand}
err := g.Unset("user.name")

is.NoErr(err)
}