f4f8fae03d
When we use certain Git environment variables to find the Git directory and the working tree, we want those values to work regardless of what directory we're in. Currently we don't change the working directory, but we soon will in order to mimic Git's behavior. In order to make relative values of these environment variables work correctly once we change directories, let's turn them into absolute canonical paths when we set up the repository. We save the old environment variables into a variable and pass this variable to the helper for the git lfs env command, which wants to print the original values, not our modified ones. Note that we can't make the canonicalization conditional on the subcommand being run because the configuration structure has been initialized by the time we get to argument parsing and the configuration code invokes Git upon initialization. Because the Cygwin path canonicalization invokes a subprocess on Windows and the subprocess code caches the environment, we must also reset the cache after adjusting these environment variables so that future subprocess invocations, like those for Git, work as expected with the updated environment.
164 lines
3.9 KiB
Go
164 lines
3.9 KiB
Go
package commands
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/git-lfs/git-lfs/config"
|
|
"github.com/git-lfs/git-lfs/tools"
|
|
"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)
|
|
|
|
root := NewCommand("git-lfs", gitlfsCommand)
|
|
root.PreRun = nil
|
|
|
|
// 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", pretend the
|
|
// last arg was "help" so our command lookup succeeds,
|
|
// since cmd will be ignored in helpCommand().
|
|
if e != nil && args[0] == "config" {
|
|
cmd, _, e = c.Root().Find([]string{"help"})
|
|
}
|
|
if cmd == nil || e != nil {
|
|
c.Printf("Unknown help topic %#q\n", 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.Fprintf(os.Stdout, "%s\n", strings.TrimSpace(txt))
|
|
} else {
|
|
fmt.Fprintf(os.Stdout, "Sorry, no usage text found for %q\n", 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.Fprintf(os.Stderr, "Error logging http stats: %s\n", err)
|
|
return
|
|
}
|
|
|
|
logFile := fmt.Sprintf("http-%d.log", time.Now().Unix())
|
|
file, err := os.Create(filepath.Join(logBase, logFile))
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error logging http stats: %s\n", err)
|
|
} else {
|
|
getAPIClient().LogHTTPStats(file)
|
|
}
|
|
}
|