Skip to content

Commit 7ed0af4

Browse files
author
L5Z12
committed
Add unuse command
1 parent 1cd8806 commit 7ed0af4

8 files changed

Lines changed: 267 additions & 0 deletions

File tree

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,30 @@ git-profile use
113113

114114
`use` must be executed inside a Git repository.
115115

116+
### `unuse`
117+
118+
Remove the applied profile entries from the current Git repository:
119+
120+
```bash
121+
git-profile unuse work
122+
```
123+
124+
Under the hood, this unsets the local Git config values, for example:
125+
126+
```bash
127+
git config --local --unset user.name
128+
git config --local --unset user.email
129+
git config --local --unset user.signingkey
130+
```
131+
132+
Run without arguments to remove the currently applied profile:
133+
134+
```bash
135+
git-profile unuse
136+
```
137+
138+
`unuse` must be executed inside a Git repository.
139+
116140
### `current`
117141

118142
Show the currently selected profile for the current repository:

cmd/interfaces.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@ type vcs interface {
2121
IsRepository() bool
2222
Get(key string) (string, error)
2323
Set(key string, value string) error
24+
Unset(key string) error
2425
}

cmd/mock.go

Lines changed: 44 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/root.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ func (c *Cmd) init() {
8787
Export(c.config),
8888
Import(c.config),
8989
Use(c.config, c.git),
90+
Unuse(c.config, c.git),
9091
Version(c),
9192
)
9293

cmd/unuse.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"os"
6+
7+
"github.com/spf13/cobra"
8+
9+
"github.com/dotzero/git-profile/internal/ui"
10+
)
11+
12+
// Unuse returns `unuse` command
13+
func Unuse(cfg storage, v vcs) *cobra.Command {
14+
return unuseCommand(cfg, v)
15+
}
16+
17+
func unuseCommand(cfg storage, v vcs) *cobra.Command {
18+
return &cobra.Command{
19+
Use: "unuse [profile]",
20+
Aliases: []string{"uu"},
21+
Short: "Unuse a profile",
22+
Long: "Removes the applied profile entries from the current git repository.",
23+
Example: multiline(
24+
`git-profile unuse`,
25+
`git-profile unuse my-profile`,
26+
),
27+
PreRun: func(cmd *cobra.Command, _ []string) {
28+
check(cmd, cfg, v)
29+
},
30+
Run: func(cmd *cobra.Command, args []string) {
31+
profile, err := unuseProfileResolve(args, v)
32+
if err != nil {
33+
ui.PrintErrln(cmd, ui.ErrorStyle, "%s", err)
34+
os.Exit(0)
35+
}
36+
37+
profileUnapply(cmd, cfg, v, profile)
38+
},
39+
}
40+
}
41+
42+
func unuseProfileResolve(args []string, v vcs) (string, error) {
43+
if len(args) > 0 {
44+
return args[0], nil
45+
}
46+
47+
profile, err := v.Get(currentProfileKey)
48+
if len(profile) == 0 || err != nil {
49+
return "", fmt.Errorf("There is no profile applied to current git repository")
50+
}
51+
52+
return profile, nil
53+
}
54+
55+
func profileUnapply(cmd *cobra.Command, cfg storage, v vcs, profile string) {
56+
entries, ok := cfg.Lookup(profile)
57+
if !ok {
58+
ui.PrintErrln(cmd, ui.ErrorStyle, "There is no profile with `%s` name", profile)
59+
os.Exit(0)
60+
}
61+
62+
for _, entry := range entries {
63+
err := v.Unset(entry.Key)
64+
if err != nil {
65+
ui.PrintErrln(cmd, ui.ErrorStyle, "Unable to interact with git to remove current profile: %s", err)
66+
os.Exit(1)
67+
}
68+
}
69+
70+
err := v.Unset(currentProfileKey)
71+
if err != nil {
72+
ui.PrintErrln(cmd, ui.ErrorStyle, "Unable to interact with git to remove current profile: %s", err)
73+
os.Exit(1)
74+
}
75+
76+
ui.Println(cmd, ui.SuccessStyle, "Successfully removed `%s` profile from current git repository.", profile)
77+
}

cmd/unuse_test.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package cmd
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"testing"
7+
8+
"github.com/matryer/is"
9+
10+
"github.com/dotzero/git-profile/internal/config"
11+
)
12+
13+
func TestUnuse(t *testing.T) {
14+
is := is.New(t)
15+
16+
cfg := &storageMock{
17+
LenFunc: func() int {
18+
return 1
19+
},
20+
LookupFunc: func(name string) ([]config.Entry, bool) {
21+
return []config.Entry{
22+
{Key: "user.email", Value: "work@example.com"},
23+
}, true
24+
},
25+
}
26+
27+
var unset []string
28+
29+
vcs := &vcsMock{
30+
IsRepositoryFunc: func() bool {
31+
return true
32+
},
33+
UnsetFunc: func(key string) error {
34+
unset = append(unset, key)
35+
return nil
36+
},
37+
}
38+
39+
var b bytes.Buffer
40+
41+
cmd := Unuse(cfg, vcs)
42+
43+
cmd.SetOut(&b)
44+
cmd.SetArgs([]string{"profile"})
45+
err := cmd.Execute()
46+
47+
is.NoErr(err)
48+
is.Equal(trim(b.String()), "Successfully removed `profile` profile from current git repository.")
49+
is.Equal(unset, []string{"user.email", currentProfileKey})
50+
}
51+
52+
func TestUnuseProfileResolveArg(t *testing.T) {
53+
is := is.New(t)
54+
55+
profile, err := unuseProfileResolve([]string{"work"}, &vcsMock{})
56+
57+
is.NoErr(err)
58+
is.Equal(profile, "work")
59+
}
60+
61+
func TestUnuseProfileResolveCurrent(t *testing.T) {
62+
is := is.New(t)
63+
64+
vcs := &vcsMock{
65+
GetFunc: func(key string) (string, error) {
66+
is.Equal(key, currentProfileKey)
67+
return "home", nil
68+
},
69+
}
70+
71+
profile, err := unuseProfileResolve(nil, vcs)
72+
73+
is.NoErr(err)
74+
is.Equal(profile, "home")
75+
}
76+
77+
func TestUnuseProfileResolveNoneApplied(t *testing.T) {
78+
is := is.New(t)
79+
80+
vcs := &vcsMock{
81+
GetFunc: func(_ string) (string, error) {
82+
return "", fmt.Errorf("boom")
83+
},
84+
}
85+
86+
_, err := unuseProfileResolve(nil, vcs)
87+
88+
is.True(err != nil)
89+
is.Equal(err.Error(), "There is no profile applied to current git repository")
90+
}

internal/git/git.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
package git
22

33
import (
4+
"errors"
45
"os/exec"
56
"strings"
67
)
78

9+
// gitConfigKeyNotFound is the exit code git returns from
10+
// `git config --unset` when the key is not present.
11+
const gitConfigKeyNotFound = 5
12+
813
// Git is a vcs
914
type Git struct {
1015
exec func(name string, arg ...string) *exec.Cmd
@@ -37,3 +42,19 @@ func (g *Git) Set(key string, value string) error {
3742

3843
return err
3944
}
45+
46+
// Unset removes a key from git local config.
47+
// A missing key is not treated as an error.
48+
func (g *Git) Unset(key string) error {
49+
_, err := g.exec("git", "config", "--local", "--unset", key).CombinedOutput()
50+
if err != nil {
51+
var exitErr *exec.ExitError
52+
if errors.As(err, &exitErr) && exitErr.ExitCode() == gitConfigKeyNotFound {
53+
return nil
54+
}
55+
56+
return err
57+
}
58+
59+
return nil
60+
}

internal/git/git_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,12 @@ func TestSet(t *testing.T) {
8888

8989
is.NoErr(err)
9090
}
91+
92+
func TestUnset(t *testing.T) {
93+
is := is.New(t)
94+
95+
g := &Git{exec: mockExecCommand}
96+
err := g.Unset("user.name")
97+
98+
is.NoErr(err)
99+
}

0 commit comments

Comments
 (0)