forked from git-lfs/git-lfs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrun.go
212 lines (180 loc) · 5.82 KB
/
run.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
package commands
import (
"bytes"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"sync"
"time"
"github.com/git-lfs/git-lfs/v3/config"
"github.com/git-lfs/git-lfs/v3/tools"
"github.com/git-lfs/git-lfs/v3/tr"
"github.com/spf13/cobra"
)
var (
commandFuncs []func() *cobra.Command
commandMu sync.Mutex
rootVersion bool
)
// NewCommand creates a new 'git-lfs' sub command, given a command name and
// command run function.
//
// Each command will initialize the local storage ('.git/lfs') directory when
// run, unless the PreRun hook is set to nil.
func NewCommand(name string, runFn func(*cobra.Command, []string)) *cobra.Command {
return &cobra.Command{Use: name, Run: runFn, PreRun: setupHTTPLogger}
}
// RegisterCommand creates a direct 'git-lfs' subcommand, given a command name,
// a command run function, and an optional callback during the command
// initialization process.
//
// The 'git-lfs' command initialization is deferred until the `commands.Run()`
// function is called. The fn callback is passed the output from NewCommand,
// and gives the caller the flexibility to customize the command by adding
// flags, tweaking command hooks, etc.
func RegisterCommand(name string, runFn func(cmd *cobra.Command, args []string), fn func(cmd *cobra.Command)) {
commandMu.Lock()
commandFuncs = append(commandFuncs, func() *cobra.Command {
cmd := NewCommand(name, runFn)
if fn != nil {
fn(cmd)
}
return cmd
})
commandMu.Unlock()
}
// Run initializes the 'git-lfs' command and runs it with the given stdin and
// command line args.
//
// It returns an exit code.
func Run() int {
log.SetOutput(ErrorWriter)
tr.InitializeLocale()
root := NewCommand("git-lfs", gitlfsCommand)
root.PreRun = nil
completionCmd := &cobra.Command{
Use: "completion [bash|fish|zsh]",
DisableFlagsInUseLine: true,
ValidArgs: []string{"bash", "fish", "zsh"},
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
Run: func(cmd *cobra.Command, args []string) {
switch args[0] {
case "bash":
completion := new(bytes.Buffer)
cmd.Root().GenBashCompletionV2(completion, false)
// this is needed for git bash completion to pick up the completion for the subcommand
completionSource := []byte(` local out directive
__git-lfs_get_completion_results
`)
completionReplace := []byte(` if [[ ${words[0]} == "git" && ${words[1]} == "lfs" ]]; then
words=("git-lfs" "${words[@]:2:${#words[@]}-2}")
__git-lfs_debug "Rewritten words[*]: ${words[*]},"
fi
local out directive
__git-lfs_get_completion_results
`)
newCompletion := bytes.NewBuffer(bytes.Replace(completion.Bytes(), completionSource, completionReplace, 1))
newCompletion.WriteString("_git_lfs() { __start_git-lfs; }\n")
newCompletion.WriteTo(os.Stdout)
case "fish":
cmd.Root().GenFishCompletion(os.Stdout, false)
case "zsh":
completion := new(bytes.Buffer)
cmd.Root().GenZshCompletionNoDesc(completion)
// this is needed for git zsh completion to use the right command for completion
completionSource := []byte(` requestComp="${words[1]} __completeNoDesc ${words[2,-1]}"`)
completionReplace := []byte(` requestComp="git-${words[1]#*git-} __completeNoDesc ${words[2,-1]}"`)
newCompletion := bytes.NewBuffer(bytes.Replace(completion.Bytes(), completionSource, completionReplace, 1))
newCompletion.WriteTo(os.Stdout)
}
},
}
root.AddCommand(completionCmd)
// Set up help/usage funcs based on manpage text
helpcmd := &cobra.Command{
Use: "help [command]",
Short: "Help about any command",
Long: `Help provides help for any command in the application.
Simply type ` + root.Name() + ` help [path to command] for full details.`,
Run: func(c *cobra.Command, args []string) {
cmd, _, e := c.Root().Find(args)
// In the case of "git lfs help config" or "git lfs help
// faq", pretend the last arg was "help" so our command
// lookup succeeds, since cmd will be ignored in
// helpCommand().
if e != nil && (args[0] == "config" || args[0] == "faq") {
cmd, _, e = c.Root().Find([]string{"help"})
}
if cmd == nil || e != nil {
c.Println(tr.Tr.Get("Unknown help topic %#q", args))
c.Root().Usage()
} else {
c.HelpFunc()(cmd, args)
}
},
}
root.SetHelpCommand(helpcmd)
root.SetHelpTemplate("{{.UsageString}}")
root.SetHelpFunc(helpCommand)
root.SetUsageFunc(usageCommand)
root.Flags().BoolVarP(&rootVersion, "version", "v", false, "")
canonicalizeEnvironment()
cfg = config.New()
for _, f := range commandFuncs {
if cmd := f(); cmd != nil {
root.AddCommand(cmd)
}
}
err := root.Execute()
closeAPIClient()
if err != nil {
return 127
}
return 0
}
func gitlfsCommand(cmd *cobra.Command, args []string) {
versionCommand(cmd, args)
if !rootVersion {
cmd.Usage()
}
}
func helpCommand(cmd *cobra.Command, args []string) {
if len(args) == 0 {
printHelp("git-lfs")
} else {
printHelp(args[0])
}
}
func usageCommand(cmd *cobra.Command) error {
printHelp(cmd.Name())
return nil
}
func printHelp(commandName string) {
if commandName == "--help" {
commandName = "git-lfs"
}
if txt, ok := ManPages[commandName]; ok {
fmt.Println(strings.TrimSpace(txt))
} else {
fmt.Println(tr.Tr.Get("Sorry, no usage text found for %q", commandName))
}
}
func setupHTTPLogger(cmd *cobra.Command, args []string) {
if len(os.Getenv("GIT_LOG_STATS")) < 1 {
return
}
logBase := filepath.Join(cfg.LocalLogDir(), "http")
if err := tools.MkdirAll(logBase, cfg); err != nil {
fmt.Fprintln(os.Stderr, tr.Tr.Get("Error logging HTTP stats: %s", err))
return
}
logFile := fmt.Sprintf("http-%d.log", time.Now().Unix())
file, err := os.Create(filepath.Join(logBase, logFile))
if err != nil {
fmt.Fprintln(os.Stderr, tr.Tr.Get("Error logging HTTP stats: %s", err))
} else {
getAPIClient().LogHTTPStats(file)
}
}