<commit_before>package cli

import (
	"fmt"
	"io"
	"sort"
	"strings"
	"text/template"

	cmds "github.com/ipfs/go-ipfs/commands"
)

const (
	requiredArg = "<%v>"
	optionalArg = "[<%v>]"
	variadicArg = "%v..."
	shortFlag   = "-%v"
	longFlag    = "--%v"
	optionType  = "(%v)"

	whitespace = "\r\n\t "

	indentStr = "  "
)

type helpFields struct {
	Indent      string
	Usage       string
	Path        string
	ArgUsage    string
	Tagline     string
	Arguments   string
	Options     string
	Synopsis    string
	Subcommands string
	Description string
	MoreHelp    bool
}

// TrimNewlines removes extra newlines from fields. This makes aligning
// commands easier. Below, the leading + tralining newlines are removed:
//	Synopsis: `
//	    ipfs config <key>          - Get value of <key>
//	    ipfs config <key> <value>  - Set value of <key> to <value>
//	    ipfs config --show         - Show config file
//	    ipfs config --edit         - Edit config file in $EDITOR
//	`
func (f *helpFields) TrimNewlines() {
	f.Path = strings.Trim(f.Path, "\n")
	f.ArgUsage = strings.Trim(f.ArgUsage, "\n")
	f.Tagline = strings.Trim(f.Tagline, "\n")
	f.Arguments = strings.Trim(f.Arguments, "\n")
	f.Options = strings.Trim(f.Options, "\n")
	f.Synopsis = strings.Trim(f.Synopsis, "\n")
	f.Subcommands = strings.Trim(f.Subcommands, "\n")
	f.Description = strings.Trim(f.Description, "\n")
}

// Indent adds whitespace the lines of fields.
func (f *helpFields) IndentAll() {
	indent := func(s string) string {
		if s == "" {
			return s
		}
		return indentString(s, indentStr)
	}

	f.Arguments = indent(f.Arguments)
	f.Options = indent(f.Options)
	f.Synopsis = indent(f.Synopsis)
	f.Subcommands = indent(f.Subcommands)
	f.Description = indent(f.Description)
}

const usageFormat = "{{if .Usage}}{{.Usage}}{{else}}{{.Path}}{{if .ArgUsage}} {{.ArgUsage}}{{end}} - {{.Tagline}}{{end}}"

const longHelpFormat = `USAGE
{{.Indent}}{{template "usage" .}}

{{if .Synopsis}}SYNOPSIS
{{.Synopsis}}

{{end}}{{if .Arguments}}ARGUMENTS

{{.Arguments}}

{{end}}{{if .Options}}OPTIONS

{{.Options}}

{{end}}{{if .Description}}DESCRIPTION

{{.Description}}

{{end}}{{if .Subcommands}}SUBCOMMANDS
{{.Subcommands}}

{{.Indent}}Use '{{.Path}} <subcmd> --help' for more information about each command.
{{end}}
`
const shortHelpFormat = `USAGE
{{.Indent}}{{template "usage" .}}
{{if .Synopsis}}
{{.Synopsis}}
{{end}}{{if .Description}}
{{.Description}}
{{end}}{{if .Subcommands}}
SUBCOMMANDS
{{.Subcommands}}
{{end}}{{if .MoreHelp}}
Use '{{.Path}} --help' for more information about this command.
{{end}}
`

var usageTemplate *template.Template
var longHelpTemplate *template.Template
var shortHelpTemplate *template.Template

func init() {
	usageTemplate = template.Must(template.New("usage").Parse(usageFormat))
	longHelpTemplate = template.Must(usageTemplate.New("longHelp").Parse(longHelpFormat))
	shortHelpTemplate = template.Must(usageTemplate.New("shortHelp").Parse(shortHelpFormat))
}

// LongHelp writes a formatted CLI helptext string to a Writer for the given command
func LongHelp(rootName string, root *cmds.Command, path []string, out io.Writer) error {
	cmd, err := root.Get(path)
	if err != nil {
		return err
	}

	pathStr := rootName
	if len(path) > 0 {
		pathStr += " " + strings.Join(path, " ")
	}

	fields := helpFields{
		Indent:      indentStr,
		Path:        pathStr,
		ArgUsage:    usageText(cmd),
		Tagline:     cmd.Helptext.Tagline,
		Arguments:   cmd.Helptext.Arguments,
		Options:     cmd.Helptext.Options,
		Synopsis:    cmd.Helptext.Synopsis,
		Subcommands: cmd.Helptext.Subcommands,
		Description: cmd.Helptext.ShortDescription,
		Usage:       cmd.Helptext.Usage,
		MoreHelp:    (cmd != root),
	}

	if len(cmd.Helptext.LongDescription) > 0 {
		fields.Description = cmd.Helptext.LongDescription
	}

	// autogen fields that are empty
	if len(fields.Arguments) == 0 {
		fields.Arguments = strings.Join(argumentText(cmd), "\n")
	}
	if len(fields.Options) == 0 {
		fields.Options = strings.Join(optionText(cmd), "\n")
	}
	if len(fields.Subcommands) == 0 {
		fields.Subcommands = strings.Join(subcommandText(cmd, rootName, path), "\n")
	}
	if len(fields.Synopsis) == 0 {
		fields.Synopsis = generateSynopsis(cmd, pathStr)
	}

	// trim the extra newlines (see TrimNewlines doc)
	fields.TrimNewlines()

	// indent all fields that have been set
	fields.IndentAll()

	return longHelpTemplate.Execute(out, fields)
}

// ShortHelp writes a formatted CLI helptext string to a Writer for the given command
func ShortHelp(rootName string, root *cmds.Command, path []string, out io.Writer) error {
	cmd, err := root.Get(path)
	if err != nil {
		return err
	}

	// default cmd to root if there is no path
	if path == nil && cmd == nil {
		cmd = root
	}

	pathStr := rootName
	if len(path) > 0 {
		pathStr += " " + strings.Join(path, " ")
	}

	fields := helpFields{
		Indent:      indentStr,
		Path:        pathStr,
		ArgUsage:    usageText(cmd),
		Tagline:     cmd.Helptext.Tagline,
		Synopsis:    cmd.Helptext.Synopsis,
		Description: cmd.Helptext.ShortDescription,
		Subcommands: cmd.Helptext.Subcommands,
		Usage:       cmd.Helptext.Usage,
		MoreHelp:    (cmd != root),
	}

	// autogen fields that are empty
	if len(fields.Subcommands) == 0 {
		fields.Subcommands = strings.Join(subcommandText(cmd, rootName, path), "\n")
	}
	if len(fields.Synopsis) == 0 {
		fields.Synopsis = generateSynopsis(cmd, pathStr)
	}

	// trim the extra newlines (see TrimNewlines doc)
	fields.TrimNewlines()

	// indent all fields that have been set
	fields.IndentAll()

	return shortHelpTemplate.Execute(out, fields)
}

func generateSynopsis(cmd *cmds.Command, path string) string {
	res := path
	for _, opt := range cmd.Options {
		valopt, ok := cmd.Helptext.SynopsisOptionsValues[opt.Names()[0]]
		if !ok {
			valopt = opt.Names()[0]
		}
		sopt := ""
		for i, n := range opt.Names() {
			pre := "-"
			if len(n) > 1 {
				pre = "--"
			}
			if opt.Type() == cmds.Bool && opt.DefaultVal() == true {
				pre = "--"
				sopt = fmt.Sprintf("%s%s=false", pre, n)
				break
			} else {
				if i == 0 {
					if opt.Type() == cmds.Bool {
						sopt = fmt.Sprintf("%s%s", pre, n)
					} else {
						sopt = fmt.Sprintf("%s%s=<%s>", pre, n, valopt)
					}
				} else {
					sopt = fmt.Sprintf("%s | %s%s", sopt, pre, n)
				}
			}
		}
		res = fmt.Sprintf("%s [%s]", res, sopt)
	}
	if len(cmd.Arguments) > 0 {
		res = fmt.Sprintf("%s [--]", res)
	}
	for _, arg := range cmd.Arguments {
		sarg := fmt.Sprintf("<%s>", arg.Name)
		if arg.Variadic {
			sarg = sarg + "..."
		}

		if !arg.Required {
			sarg = fmt.Sprintf("[%s]", sarg)
		}
		res = fmt.Sprintf("%s %s", res, sarg)
	}
	return strings.Trim(res, " ")
}

func argumentText(cmd *cmds.Command) []string {
	lines := make([]string, len(cmd.Arguments))

	for i, arg := range cmd.Arguments {
		lines[i] = argUsageText(arg)
	}
	lines = align(lines)
	for i, arg := range cmd.Arguments {
		lines[i] += " - " + arg.Description
	}

	return lines
}

func optionFlag(flag string) string {
	if len(flag) == 1 {
		return fmt.Sprintf(shortFlag, flag)
	} else {
		return fmt.Sprintf(longFlag, flag)
	}
}

func optionText(cmd ...*cmds.Command) []string {
	// get a slice of the options we want to list out
	options := make([]cmds.Option, 0)
	for _, c := range cmd {
		for _, opt := range c.Options {
			options = append(options, opt)
		}
	}

	// add option names to output (with each name aligned)
	lines := make([]string, 0)
	j := 0
	for {
		done := true
		i := 0
		for _, opt := range options {
			if len(lines) < i+1 {
				lines = append(lines, "")
			}

			names := sortByLength(opt.Names())
			if len(names) >= j+1 {
				lines[i] += optionFlag(names[j])
			}
			if len(names) > j+1 {
				lines[i] += ", "
				done = false
			}

			i++
		}

		if done {
			break
		}

		lines = align(lines)
		j++
	}
	lines = align(lines)

	// add option types to output
	for i, opt := range options {
		lines[i] += " " + fmt.Sprintf("%v", opt.Type())
	}
	lines = align(lines)

	// add option descriptions to output
	for i, opt := range options {
		lines[i] += " - " + opt.Description()
	}

	return lines
}

func subcommandText(cmd *cmds.Command, rootName string, path []string) []string {
	prefix := fmt.Sprintf("%v %v", rootName, strings.Join(path, " "))
	if len(path) > 0 {
		prefix += " "
	}
	subcmds := make([]*cmds.Command, len(cmd.Subcommands))
	lines := make([]string, len(cmd.Subcommands))

	i := 0
	for name, sub := range cmd.Subcommands {
		usage := usageText(sub)
		if len(usage) > 0 {
			usage = " " + usage
		}
		lines[i] = prefix + name + usage
		subcmds[i] = sub
		i++
	}

	lines = align(lines)
	for i, sub := range subcmds {
		lines[i] += " - " + sub.Helptext.Tagline
	}

	return lines
}

func usageText(cmd *cmds.Command) string {
	s := ""
	for i, arg := range cmd.Arguments {
		if i != 0 {
			s += " "
		}
		s += argUsageText(arg)
	}

	return s
}

func argUsageText(arg cmds.Argument) string {
	s := arg.Name

	if arg.Required {
		s = fmt.Sprintf(requiredArg, s)
	} else {
		s = fmt.Sprintf(optionalArg, s)
	}

	if arg.Variadic {
		s = fmt.Sprintf(variadicArg, s)
	}

	return s
}

func align(lines []string) []string {
	longest := 0
	for _, line := range lines {
		length := len(line)
		if length > longest {
			longest = length
		}
	}

	for i, line := range lines {
		length := len(line)
		if length > 0 {
			lines[i] += strings.Repeat(" ", longest-length)
		}
	}

	return lines
}

func indent(lines []string, prefix string) []string {
	for i, line := range lines {
		lines[i] = prefix + indentString(line, prefix)
	}
	return lines
}

func indentString(line string, prefix string) string {
	return prefix + strings.Replace(line, "\n", "\n"+prefix, -1)
}

type lengthSlice []string

func (ls lengthSlice) Len() int {
	return len(ls)
}
func (ls lengthSlice) Swap(a, b int) {
	ls[a], ls[b] = ls[b], ls[a]
}
func (ls lengthSlice) Less(a, b int) bool {
	return len(ls[a]) < len(ls[b])
}

func sortByLength(slice []string) []string {
	output := make(lengthSlice, len(slice))
	for i, val := range slice {
		output[i] = val
	}
	sort.Sort(output)
	return []string(output)
}
<commit_msg>Sort SUBCOMMANDS section of help output<commit_after>package cli

import (
	"fmt"
	"io"
	"sort"
	"strings"
	"text/template"

	cmds "github.com/ipfs/go-ipfs/commands"
)

const (
	requiredArg = "<%v>"
	optionalArg = "[<%v>]"
	variadicArg = "%v..."
	shortFlag   = "-%v"
	longFlag    = "--%v"
	optionType  = "(%v)"

	whitespace = "\r\n\t "

	indentStr = "  "
)

type helpFields struct {
	Indent      string
	Usage       string
	Path        string
	ArgUsage    string
	Tagline     string
	Arguments   string
	Options     string
	Synopsis    string
	Subcommands string
	Description string
	MoreHelp    bool
}

// TrimNewlines removes extra newlines from fields. This makes aligning
// commands easier. Below, the leading + tralining newlines are removed:
//	Synopsis: `
//	    ipfs config <key>          - Get value of <key>
//	    ipfs config <key> <value>  - Set value of <key> to <value>
//	    ipfs config --show         - Show config file
//	    ipfs config --edit         - Edit config file in $EDITOR
//	`
func (f *helpFields) TrimNewlines() {
	f.Path = strings.Trim(f.Path, "\n")
	f.ArgUsage = strings.Trim(f.ArgUsage, "\n")
	f.Tagline = strings.Trim(f.Tagline, "\n")
	f.Arguments = strings.Trim(f.Arguments, "\n")
	f.Options = strings.Trim(f.Options, "\n")
	f.Synopsis = strings.Trim(f.Synopsis, "\n")
	f.Subcommands = strings.Trim(f.Subcommands, "\n")
	f.Description = strings.Trim(f.Description, "\n")
}

// Indent adds whitespace the lines of fields.
func (f *helpFields) IndentAll() {
	indent := func(s string) string {
		if s == "" {
			return s
		}
		return indentString(s, indentStr)
	}

	f.Arguments = indent(f.Arguments)
	f.Options = indent(f.Options)
	f.Synopsis = indent(f.Synopsis)
	f.Subcommands = indent(f.Subcommands)
	f.Description = indent(f.Description)
}

const usageFormat = "{{if .Usage}}{{.Usage}}{{else}}{{.Path}}{{if .ArgUsage}} {{.ArgUsage}}{{end}} - {{.Tagline}}{{end}}"

const longHelpFormat = `USAGE
{{.Indent}}{{template "usage" .}}

{{if .Synopsis}}SYNOPSIS
{{.Synopsis}}

{{end}}{{if .Arguments}}ARGUMENTS

{{.Arguments}}

{{end}}{{if .Options}}OPTIONS

{{.Options}}

{{end}}{{if .Description}}DESCRIPTION

{{.Description}}

{{end}}{{if .Subcommands}}SUBCOMMANDS
{{.Subcommands}}

{{.Indent}}Use '{{.Path}} <subcmd> --help' for more information about each command.
{{end}}
`
const shortHelpFormat = `USAGE
{{.Indent}}{{template "usage" .}}
{{if .Synopsis}}
{{.Synopsis}}
{{end}}{{if .Description}}
{{.Description}}
{{end}}{{if .Subcommands}}
SUBCOMMANDS
{{.Subcommands}}
{{end}}{{if .MoreHelp}}
Use '{{.Path}} --help' for more information about this command.
{{end}}
`

var usageTemplate *template.Template
var longHelpTemplate *template.Template
var shortHelpTemplate *template.Template

func init() {
	usageTemplate = template.Must(template.New("usage").Parse(usageFormat))
	longHelpTemplate = template.Must(usageTemplate.New("longHelp").Parse(longHelpFormat))
	shortHelpTemplate = template.Must(usageTemplate.New("shortHelp").Parse(shortHelpFormat))
}

// LongHelp writes a formatted CLI helptext string to a Writer for the given command
func LongHelp(rootName string, root *cmds.Command, path []string, out io.Writer) error {
	cmd, err := root.Get(path)
	if err != nil {
		return err
	}

	pathStr := rootName
	if len(path) > 0 {
		pathStr += " " + strings.Join(path, " ")
	}

	fields := helpFields{
		Indent:      indentStr,
		Path:        pathStr,
		ArgUsage:    usageText(cmd),
		Tagline:     cmd.Helptext.Tagline,
		Arguments:   cmd.Helptext.Arguments,
		Options:     cmd.Helptext.Options,
		Synopsis:    cmd.Helptext.Synopsis,
		Subcommands: cmd.Helptext.Subcommands,
		Description: cmd.Helptext.ShortDescription,
		Usage:       cmd.Helptext.Usage,
		MoreHelp:    (cmd != root),
	}

	if len(cmd.Helptext.LongDescription) > 0 {
		fields.Description = cmd.Helptext.LongDescription
	}

	// autogen fields that are empty
	if len(fields.Arguments) == 0 {
		fields.Arguments = strings.Join(argumentText(cmd), "\n")
	}
	if len(fields.Options) == 0 {
		fields.Options = strings.Join(optionText(cmd), "\n")
	}
	if len(fields.Subcommands) == 0 {
		fields.Subcommands = strings.Join(subcommandText(cmd, rootName, path), "\n")
	}
	if len(fields.Synopsis) == 0 {
		fields.Synopsis = generateSynopsis(cmd, pathStr)
	}

	// trim the extra newlines (see TrimNewlines doc)
	fields.TrimNewlines()

	// indent all fields that have been set
	fields.IndentAll()

	return longHelpTemplate.Execute(out, fields)
}

// ShortHelp writes a formatted CLI helptext string to a Writer for the given command
func ShortHelp(rootName string, root *cmds.Command, path []string, out io.Writer) error {
	cmd, err := root.Get(path)
	if err != nil {
		return err
	}

	// default cmd to root if there is no path
	if path == nil && cmd == nil {
		cmd = root
	}

	pathStr := rootName
	if len(path) > 0 {
		pathStr += " " + strings.Join(path, " ")
	}

	fields := helpFields{
		Indent:      indentStr,
		Path:        pathStr,
		ArgUsage:    usageText(cmd),
		Tagline:     cmd.Helptext.Tagline,
		Synopsis:    cmd.Helptext.Synopsis,
		Description: cmd.Helptext.ShortDescription,
		Subcommands: cmd.Helptext.Subcommands,
		Usage:       cmd.Helptext.Usage,
		MoreHelp:    (cmd != root),
	}

	// autogen fields that are empty
	if len(fields.Subcommands) == 0 {
		fields.Subcommands = strings.Join(subcommandText(cmd, rootName, path), "\n")
	}
	if len(fields.Synopsis) == 0 {
		fields.Synopsis = generateSynopsis(cmd, pathStr)
	}

	// trim the extra newlines (see TrimNewlines doc)
	fields.TrimNewlines()

	// indent all fields that have been set
	fields.IndentAll()

	return shortHelpTemplate.Execute(out, fields)
}

func generateSynopsis(cmd *cmds.Command, path string) string {
	res := path
	for _, opt := range cmd.Options {
		valopt, ok := cmd.Helptext.SynopsisOptionsValues[opt.Names()[0]]
		if !ok {
			valopt = opt.Names()[0]
		}
		sopt := ""
		for i, n := range opt.Names() {
			pre := "-"
			if len(n) > 1 {
				pre = "--"
			}
			if opt.Type() == cmds.Bool && opt.DefaultVal() == true {
				pre = "--"
				sopt = fmt.Sprintf("%s%s=false", pre, n)
				break
			} else {
				if i == 0 {
					if opt.Type() == cmds.Bool {
						sopt = fmt.Sprintf("%s%s", pre, n)
					} else {
						sopt = fmt.Sprintf("%s%s=<%s>", pre, n, valopt)
					}
				} else {
					sopt = fmt.Sprintf("%s | %s%s", sopt, pre, n)
				}
			}
		}
		res = fmt.Sprintf("%s [%s]", res, sopt)
	}
	if len(cmd.Arguments) > 0 {
		res = fmt.Sprintf("%s [--]", res)
	}
	for _, arg := range cmd.Arguments {
		sarg := fmt.Sprintf("<%s>", arg.Name)
		if arg.Variadic {
			sarg = sarg + "..."
		}

		if !arg.Required {
			sarg = fmt.Sprintf("[%s]", sarg)
		}
		res = fmt.Sprintf("%s %s", res, sarg)
	}
	return strings.Trim(res, " ")
}

func argumentText(cmd *cmds.Command) []string {
	lines := make([]string, len(cmd.Arguments))

	for i, arg := range cmd.Arguments {
		lines[i] = argUsageText(arg)
	}
	lines = align(lines)
	for i, arg := range cmd.Arguments {
		lines[i] += " - " + arg.Description
	}

	return lines
}

func optionFlag(flag string) string {
	if len(flag) == 1 {
		return fmt.Sprintf(shortFlag, flag)
	} else {
		return fmt.Sprintf(longFlag, flag)
	}
}

func optionText(cmd ...*cmds.Command) []string {
	// get a slice of the options we want to list out
	options := make([]cmds.Option, 0)
	for _, c := range cmd {
		for _, opt := range c.Options {
			options = append(options, opt)
		}
	}

	// add option names to output (with each name aligned)
	lines := make([]string, 0)
	j := 0
	for {
		done := true
		i := 0
		for _, opt := range options {
			if len(lines) < i+1 {
				lines = append(lines, "")
			}

			names := sortByLength(opt.Names())
			if len(names) >= j+1 {
				lines[i] += optionFlag(names[j])
			}
			if len(names) > j+1 {
				lines[i] += ", "
				done = false
			}

			i++
		}

		if done {
			break
		}

		lines = align(lines)
		j++
	}
	lines = align(lines)

	// add option types to output
	for i, opt := range options {
		lines[i] += " " + fmt.Sprintf("%v", opt.Type())
	}
	lines = align(lines)

	// add option descriptions to output
	for i, opt := range options {
		lines[i] += " - " + opt.Description()
	}

	return lines
}

func subcommandText(cmd *cmds.Command, rootName string, path []string) []string {
	prefix := fmt.Sprintf("%v %v", rootName, strings.Join(path, " "))
	if len(path) > 0 {
		prefix += " "
	}

	// Sorting fixes changing order bug #2981.
	sortedNames := make([]string, 0)
	for name := range cmd.Subcommands {
		sortedNames = append(sortedNames, name)
	}
	sort.Strings(sortedNames)

	subcmds := make([]*cmds.Command, len(cmd.Subcommands))
	lines := make([]string, len(cmd.Subcommands))

	for i, name := range sortedNames {
		sub := cmd.Subcommands[name]
		usage := usageText(sub)
		if len(usage) > 0 {
			usage = " " + usage
		}
		lines[i] = prefix + name + usage
		subcmds[i] = sub
	}

	lines = align(lines)
	for i, sub := range subcmds {
		lines[i] += " - " + sub.Helptext.Tagline
	}

	return lines
}

func usageText(cmd *cmds.Command) string {
	s := ""
	for i, arg := range cmd.Arguments {
		if i != 0 {
			s += " "
		}
		s += argUsageText(arg)
	}

	return s
}

func argUsageText(arg cmds.Argument) string {
	s := arg.Name

	if arg.Required {
		s = fmt.Sprintf(requiredArg, s)
	} else {
		s = fmt.Sprintf(optionalArg, s)
	}

	if arg.Variadic {
		s = fmt.Sprintf(variadicArg, s)
	}

	return s
}

func align(lines []string) []string {
	longest := 0
	for _, line := range lines {
		length := len(line)
		if length > longest {
			longest = length
		}
	}

	for i, line := range lines {
		length := len(line)
		if length > 0 {
			lines[i] += strings.Repeat(" ", longest-length)
		}
	}

	return lines
}

func indent(lines []string, prefix string) []string {
	for i, line := range lines {
		lines[i] = prefix + indentString(line, prefix)
	}
	return lines
}

func indentString(line string, prefix string) string {
	return prefix + strings.Replace(line, "\n", "\n"+prefix, -1)
}

type lengthSlice []string

func (ls lengthSlice) Len() int {
	return len(ls)
}
func (ls lengthSlice) Swap(a, b int) {
	ls[a], ls[b] = ls[b], ls[a]
}
func (ls lengthSlice) Less(a, b int) bool {
	return len(ls[a]) < len(ls[b])
}

func sortByLength(slice []string) []string {
	output := make(lengthSlice, len(slice))
	for i, val := range slice {
		output[i] = val
	}
	sort.Sort(output)
	return []string(output)
}
<|endoftext|>


<commit_before>package gitmedia

import (
	gitmedia ".."
	_ "../gitconfig"
	"bufio"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

type PathCommand struct {
	*Command
}

func (c *PathCommand) Run() {
	var sub string
	if len(c.SubCommands) > 0 {
		sub = c.SubCommands[0]
	}

	switch sub {
	case "add":
		c.addPath()
	case "remove":
		c.removePath()
	default:
		c.listPaths()
	}

}

func (c *PathCommand) addPath() {
	if len(c.SubCommands) < 2 {
		fmt.Println("git media path add <path> [path]*")
		return
	}

	knownPaths := findPaths()
	attributesFile, err := os.OpenFile(".gitattributes", os.O_RDWR|os.O_APPEND|os.O_CREATE, 0660)
	if err != nil {
		fmt.Println("Error opening .gitattributes file")
		return
	}

	for _, t := range c.SubCommands[1:] {
		isKnownPath := false
		for _, k := range knownPaths {
			if t == k.Path {
				isKnownPath = true
			}
		}

		if isKnownPath {
			fmt.Println(t, "already supported")
			continue
		}

		_, err := attributesFile.WriteString(fmt.Sprintf("%s filter=media -crlf\n", t))
		if err != nil {
			fmt.Println("Error adding path", t)
			continue
		}
		fmt.Println("Adding path", t)
	}

	attributesFile.Close()
}

func (c *PathCommand) removePath() {
	if len(c.SubCommands) < 2 {
		fmt.Println("git meda path remove <path> [path]*")
		return
	}

	data, err := ioutil.ReadFile(".gitattributes")
	if err != nil {
		return
	}

	attributes := strings.NewReader(string(data))

	attributesFile, err := os.Create(".gitattributes")
	if err != nil {
		fmt.Println("Error opening .gitattributes for writing")
		return
	}

	scanner := bufio.NewScanner(attributes)
	for scanner.Scan() {
		line := scanner.Text()
		if strings.Contains(line, "filter=media") {
			fields := strings.Fields(line)
			removeThisPath := false
			for _, t := range c.SubCommands[1:] {
				if t == fields[0] {
					removeThisPath = true
				}
			}

			if !removeThisPath {
				attributesFile.WriteString(line + "\n")
			} else {
				fmt.Println("Removing path", fields[0])
			}
		}
	}

	attributesFile.Close()
}

func (c *PathCommand) listPaths() {
	fmt.Println("Listing paths")
	knownPaths := findPaths()
	for _, t := range knownPaths {
		fmt.Printf("    %s (%s)\n", t.Path, t.Source)
	}
}

type mediaPath struct {
	Path   string
	Source string
}

func findAttributeFiles() []string {
	paths := make([]string, 0)

	repoAttributes := filepath.Join(gitmedia.LocalGitDir, "info", "attributes")
	if _, err := os.Stat(repoAttributes); err == nil {
		paths = append(paths, repoAttributes)
	}

	filepath.Walk(gitmedia.LocalWorkingDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if !info.IsDir() && (filepath.Base(path) == ".gitattributes") {
			paths = append(paths, path)
		}
		return nil
	})

	return paths
}

func findPaths() []mediaPath {
	paths := make([]mediaPath, 0)

	for _, path := range findAttributeFiles() {
		attributes, err := os.Open(path)
		if err != nil {
			return paths
		}

		scanner := bufio.NewScanner(attributes)
		for scanner.Scan() {
			line := scanner.Text()
			if line == "" {
				continue
			}

			if strings.Contains(line, "filter=media") {
				fields := strings.Fields(line)
				paths = append(paths, mediaPath{Path: fields[0], Source: path})
			}
		}
	}

	return paths
}

func init() {
	registerCommand("path", func(c *Command) RunnableCommand {
		return &PathCommand{Command: c}
	})
	registerCommand("paths", func(c *Command) RunnableCommand {
		return &PathCommand{Command: c}
	})
}
<commit_msg>アーア アアアア アーアー<commit_after>package gitmedia

import (
	gitmedia ".."
	_ "../gitconfig"
	"bufio"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

type PathCommand struct {
	*Command
}

func (c *PathCommand) Run() {
	var sub string
	if len(c.SubCommands) > 0 {
		sub = c.SubCommands[0]
	}

	switch sub {
	case "add":
		c.addPath()
	case "remove":
		c.removePath()
	default:
		c.listPaths()
	}

}

func (c *PathCommand) addPath() {
	if len(c.SubCommands) < 2 {
		fmt.Println("git media path add <path> [path]*")
		return
	}

	knownPaths := findPaths()
	attributesFile, err := os.OpenFile(".gitattributes", os.O_RDWR|os.O_APPEND|os.O_CREATE, 0660)
	if err != nil {
		fmt.Println("Error opening .gitattributes file")
		return
	}

	for _, t := range c.SubCommands[1:] {
		isKnownPath := false
		for _, k := range knownPaths {
			if t == k.Path {
				isKnownPath = true
			}
		}

		if isKnownPath {
			fmt.Println(t, "already supported")
			continue
		}

		_, err := attributesFile.WriteString(fmt.Sprintf("%s filter=media -crlf\n", t))
		if err != nil {
			fmt.Println("Error adding path", t)
			continue
		}
		fmt.Println("Adding path", t)
	}

	attributesFile.Close()
}

func (c *PathCommand) removePath() {
	if len(c.SubCommands) < 2 {
		fmt.Println("git meda path remove <path> [path]*")
		return
	}

	data, err := ioutil.ReadFile(".gitattributes")
	if err != nil {
		return
	}

	attributes := strings.NewReader(string(data))

	attributesFile, err := os.Create(".gitattributes")
	if err != nil {
		fmt.Println("Error opening .gitattributes for writing")
		return
	}

	scanner := bufio.NewScanner(attributes)
	for scanner.Scan() {
		line := scanner.Text()
		if strings.Contains(line, "filter=media") {
			fields := strings.Fields(line)
			removeThisPath := false
			for _, t := range c.SubCommands[1:] {
				if t == fields[0] {
					removeThisPath = true
				}
			}

			if !removeThisPath {
				attributesFile.WriteString(line + "\n")
			} else {
				fmt.Println("Removing path", fields[0])
			}
		}
	}

	attributesFile.Close()
}

func (c *PathCommand) listPaths() {
	fmt.Println("Listing paths")
	knownPaths := findPaths()
	for _, t := range knownPaths {
		fmt.Printf("    %s (%s)\n", t.Path, t.Source)
	}
}

type mediaPath struct {
	Path   string
	Source string
}

func findAttributeFiles() []string {
	paths := make([]string, 0)

	repoAttributes := filepath.Join(gitmedia.LocalGitDir, "info", "attributes")
	if _, err := os.Stat(repoAttributes); err == nil {
		paths = append(paths, repoAttributes)
	}

	filepath.Walk(gitmedia.LocalWorkingDir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if !info.IsDir() && (filepath.Base(path) == ".gitattributes") {
			paths = append(paths, path)
		}
		return nil
	})

	return paths
}

func findPaths() []mediaPath {
	paths := make([]mediaPath, 0)

	for _, path := range findAttributeFiles() {
		attributes, err := os.Open(path)
		if err != nil {
			return paths
		}

		scanner := bufio.NewScanner(attributes)
		for scanner.Scan() {
			line := scanner.Text()
			if line == "" {
				continue
			}

			if strings.Contains(line, "filter=media") {
				fields := strings.Fields(line)
				wd, _ := os.Getwd()
				relPath, _ := filepath.Rel(wd, path)
				paths = append(paths, mediaPath{Path: fields[0], Source: relPath})
			}
		}
	}

	return paths
}

func init() {
	registerCommand("path", func(c *Command) RunnableCommand {
		return &PathCommand{Command: c}
	})
	registerCommand("paths", func(c *Command) RunnableCommand {
		return &PathCommand{Command: c}
	})
}
<|endoftext|>


<commit_before>package commands

import (
	"github.com/hawser/git-hawser/git"
	"github.com/hawser/git-hawser/hawser"
	"github.com/hawser/git-hawser/scanner"
	"github.com/rubyist/tracerx"
	"github.com/spf13/cobra"
	"io/ioutil"
	"os"
	"strings"
)

var (
	pushCmd = &cobra.Command{
		Use:   "push",
		Short: "Push files to the hawser endpoint",
		Run:   pushCommand,
	}
	dryRun       = false
	useStdin     = false
	deleteBranch = "(delete)"
)

// pushCommand is the command that's run via `git hawser push`. It has two modes
// of operation. The primary mode is run via the git pre-push hook. The pre-push
// hook passes two arguments on the command line:
//   1. Name of the remote to which the push is being done
//   2. URL to which the push is being done
//
// The hook receives commit information on stdin in the form:
//   <local ref> <local sha1> <remote ref> <remote sha1>
//
// In the typical case, pushCommand will get a list of git objects being pushed
// by using the following:
//    git rev-list --objects <local sha1> ^<remote sha1>
//
// If any of those git objects are associated with hawser objects, those hawser
// objects will be pushed to the hawser endpoint.
//
// In the case of pushing a new branch, the list of git objects will be all of
// the git objects in this branch.
//
// In the case of deleting a branch, no attempts to push hawser objects will be
// made.
//
// When pushing hawser objects, the client will first perform an OPTIONS command
// which will determine not only whether or not the client is authorized, but also
// whether or not that hawser endpoint already has the hawser object. If it
// does, the object will not be pushed.
//
// The other mode of operation is the dry run mode. In this mode, the repo
// and refspec are passed on the command line. pushCommand will calculate the
// git objects that would be pushed in a similar manner as above and will print
// out each file name.
func pushCommand(cmd *cobra.Command, args []string) {
	var left, right string

	if len(args) == 0 {
		Print("The git hawser pre-push hook is out of date. Please run `git hawser update`")
		os.Exit(1)
	}

	hawser.Config.CurrentRemote = args[0]

	if useStdin {
		refsData, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			Panic(err, "Error reading refs on stdin")
		}

		if len(refsData) == 0 {
			return
		}

		left, right = decodeRefs(string(refsData))
		if left == deleteBranch {
			return
		}
	} else {
		var repo, refspec string

		if len(args) < 1 {
			Print("Usage: git hawser push --dry-run <repo> [refspec]")
			return
		}

		repo = args[0]
		if len(args) == 2 {
			refspec = args[1]
		}

		localRef, err := git.CurrentRef()
		if err != nil {
			Panic(err, "Error getting local ref")
		}
		left = localRef

		remoteRef, err := git.LsRemote(repo, refspec)
		if err != nil {
			Panic(err, "Error getting remote ref")
		}

		if remoteRef != "" {
			right = "^" + strings.Split(remoteRef, "\t")[0]
		}
	}

	// Just use scanner here
	pointers, err := scanner.Scan(left, right)
	if err != nil {
		Panic(err, "Error scanning for hawser files")
	}

	for i, pointer := range pointers {
		if dryRun {
			Print("push %s", pointer.Name)
			continue
		}
		if wErr := pushAsset(pointer.Oid, pointer.Name, i+1, len(pointers)); wErr != nil {
			if Debugging || wErr.Panic {
				Panic(wErr.Err, wErr.Error())
			} else {
				Exit(wErr.Error())
			}
		}
	}
}

// pushAsset pushes the asset with the given oid to the hawser endpoint. It will
// first make an OPTIONS call. If OPTIONS returns a 200 status, it indicates that the
// hawser endpoint already has a hawser object for that oid. The object will
// not be pushed again.
func pushAsset(oid, filename string, index, totalFiles int) *hawser.WrappedError {
	tracerx.Printf("checking_asset: %s %s %d/%d", oid, filename, index, totalFiles)
	path, err := hawser.LocalMediaPath(oid)
	if err != nil {
		return hawser.Errorf(err, "Error uploading file %s (%s)", filename, oid)
	}

	cb, file, cbErr := hawser.CopyCallbackFile("push", filename, index, totalFiles)
	if cbErr != nil {
		Error(cbErr.Error())
	}

	if file != nil {
		defer file.Close()
	}

	return hawser.Upload(path, filename, cb)
}

// decodeRefs pulls the sha1s out of the line read from the pre-push
// hook's stdin.
func decodeRefs(input string) (string, string) {
	refs := strings.Split(strings.TrimSpace(input), " ")
	var left, right string

	if len(refs) > 1 {
		left = refs[1]
	}

	if len(refs) > 3 {
		right = "^" + refs[3]
	}

	return left, right
}

func init() {
	pushCmd.Flags().BoolVarP(&dryRun, "dry-run", "d", false, "Do everything except actually send the updates")
	pushCmd.Flags().BoolVarP(&useStdin, "stdin", "s", false, "Take refs on stdin (for pre-push hook)")
	RootCmd.AddCommand(pushCmd)
}
<commit_msg>ララララー ラララー ララ<commit_after>package commands

import (
	"fmt"
	"github.com/hawser/git-hawser/git"
	"github.com/hawser/git-hawser/hawser"
	"github.com/hawser/git-hawser/pointer"
	"github.com/hawser/git-hawser/scanner"
	"github.com/rubyist/tracerx"
	"github.com/spf13/cobra"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

var (
	pushCmd = &cobra.Command{
		Use:   "push",
		Short: "Push files to the hawser endpoint",
		Run:   pushCommand,
	}
	dryRun       = false
	useStdin     = false
	deleteBranch = "(delete)"
)

// pushCommand is the command that's run via `git hawser push`. It has two modes
// of operation. The primary mode is run via the git pre-push hook. The pre-push
// hook passes two arguments on the command line:
//   1. Name of the remote to which the push is being done
//   2. URL to which the push is being done
//
// The hook receives commit information on stdin in the form:
//   <local ref> <local sha1> <remote ref> <remote sha1>
//
// In the typical case, pushCommand will get a list of git objects being pushed
// by using the following:
//    git rev-list --objects <local sha1> ^<remote sha1>
//
// If any of those git objects are associated with hawser objects, those hawser
// objects will be pushed to the hawser endpoint.
//
// In the case of pushing a new branch, the list of git objects will be all of
// the git objects in this branch.
//
// In the case of deleting a branch, no attempts to push hawser objects will be
// made.
//
// When pushing hawser objects, the client will first perform an OPTIONS command
// which will determine not only whether or not the client is authorized, but also
// whether or not that hawser endpoint already has the hawser object. If it
// does, the object will not be pushed.
//
// The other mode of operation is the dry run mode. In this mode, the repo
// and refspec are passed on the command line. pushCommand will calculate the
// git objects that would be pushed in a similar manner as above and will print
// out each file name.
func pushCommand(cmd *cobra.Command, args []string) {
	var left, right string

	if len(args) == 0 {
		Print("The git hawser pre-push hook is out of date. Please run `git hawser update`")
		os.Exit(1)
	}

	hawser.Config.CurrentRemote = args[0]

	if useStdin {
		refsData, err := ioutil.ReadAll(os.Stdin)
		if err != nil {
			Panic(err, "Error reading refs on stdin")
		}

		if len(refsData) == 0 {
			return
		}

		left, right = decodeRefs(string(refsData))
		if left == deleteBranch {
			return
		}
	} else {
		var repo, refspec string

		if len(args) < 1 {
			Print("Usage: git hawser push --dry-run <repo> [refspec]")
			return
		}

		repo = args[0]
		if len(args) == 2 {
			refspec = args[1]
		}

		localRef, err := git.CurrentRef()
		if err != nil {
			Panic(err, "Error getting local ref")
		}
		left = localRef

		remoteRef, err := git.LsRemote(repo, refspec)
		if err != nil {
			Panic(err, "Error getting remote ref")
		}

		if remoteRef != "" {
			right = "^" + strings.Split(remoteRef, "\t")[0]
		}
	}

	// Just use scanner here
	pointers, err := scanner.Scan(left, right)
	if err != nil {
		Panic(err, "Error scanning for hawser files")
	}

	for i, pointer := range pointers {
		if dryRun {
			Print("push %s", pointer.Name)
			continue
		}
		if wErr := pushAsset(pointer.Oid, pointer.Name, i+1, len(pointers)); wErr != nil {
			if Debugging || wErr.Panic {
				Panic(wErr.Err, wErr.Error())
			} else {
				Exit(wErr.Error())
			}
		}
	}
}

// pushAsset pushes the asset with the given oid to the hawser endpoint. It will
// first make an OPTIONS call. If OPTIONS returns a 200 status, it indicates that the
// hawser endpoint already has a hawser object for that oid. The object will
// not be pushed again.
func pushAsset(oid, filename string, index, totalFiles int) *hawser.WrappedError {
	tracerx.Printf("checking_asset: %s %s %d/%d", oid, filename, index, totalFiles)
	path, err := hawser.LocalMediaPath(oid)
	if err != nil {
		return hawser.Errorf(err, "Error uploading file %s (%s)", filename, oid)
	}

	if err := ensureFile(filename, path); err != nil {
		return hawser.Errorf(err, "Error uploading file %s (%s)", filename, oid)
	}

	cb, file, cbErr := hawser.CopyCallbackFile("push", filename, index, totalFiles)
	if cbErr != nil {
		Error(cbErr.Error())
	}

	if file != nil {
		defer file.Close()
	}

	return hawser.Upload(path, filename, cb)
}

// ensureFile makes sure that the cleanPath exists before pushing it.  If it
// does not exist, it attempts to clean it by reading the file at smudgePath.
func ensureFile(smudgePath, cleanPath string) error {
	if _, err := os.Stat(cleanPath); err == nil {
		return nil
	}

	expectedOid := filepath.Base(cleanPath)
	localPath := filepath.Join(hawser.LocalWorkingDir, smudgePath)
	file, err := os.Open(localPath)
	if err != nil {
		return err
	}

	defer file.Close()

	stat, err := file.Stat()
	if err != nil {
		return err
	}

	cleaned, err := pointer.Clean(file, stat.Size(), nil)
	if err != nil {
		return err
	}

	cleaned.Close()

	if expectedOid != cleaned.Oid {
		return fmt.Errorf("Expected %s to have an OID of %s, got %s", smudgePath, expectedOid, cleaned.Oid)
	}

	return nil
}

// decodeRefs pulls the sha1s out of the line read from the pre-push
// hook's stdin.
func decodeRefs(input string) (string, string) {
	refs := strings.Split(strings.TrimSpace(input), " ")
	var left, right string

	if len(refs) > 1 {
		left = refs[1]
	}

	if len(refs) > 3 {
		right = "^" + refs[3]
	}

	return left, right
}

func init() {
	pushCmd.Flags().BoolVarP(&dryRun, "dry-run", "d", false, "Do everything except actually send the updates")
	pushCmd.Flags().BoolVarP(&useStdin, "stdin", "s", false, "Take refs on stdin (for pre-push hook)")
	RootCmd.AddCommand(pushCmd)
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/github/hub/git"
	"github.com/github/hub/github"
	"github.com/github/hub/utils"
	"github.com/octokit/go-octokit/octokit"
)

var cmdPullRequest = &Command{
	Run: pullRequest,
	Usage: `
pull-request [-fo] [-b <BASE>] [-h <HEAD>] [-a <USER>] [-M <MILESTONE>] [-l <LABELS>]
pull-request -m <MESSAGE>
pull-request -F <FILE>
pull-request -i <ISSUE>
`,
	Long: `Create a GitHub pull request.

## Options:
	-f, --force
		Skip the check for unpushed commits.

	-m, --message <MESSAGE>
		Use the first line of <MESSAGE> as pull request title, and the rest as pull
		request description.

	-F, --file <FILE>
		Read the pull request title and description from <FILE>.

	-i, --issue <ISSUE>, <ISSUE-URL>
		(Deprecated) Convert <ISSUE> to a pull request.

	-o, --browse
		Open the new pull request in a web browser.

	-b, --base <BASE>
		The base branch in "[OWNER:]BRANCH" format. Defaults to the default branch
		(usually "master").

	-h, --head <HEAD>
		The head branch in "[OWNER:]BRANCH" format. Defaults to the current branch.

	-a, --assign <USER>
		Assign GitHub <USER> to this pull request.

	-M, --milestone <ID>
		Add this pull request to a GitHub milestone with id <ID>.

	-l, --labels <LABELS>
		Add a comma-separated list of labels to this pull request.

## See also:

hub(1), hub-merge(1), hub-checkout(1)
`,
}

var (
	flagPullRequestBase,
	flagPullRequestHead,
	flagPullRequestIssue,
	flagPullRequestMessage,
	flagPullRequestAssignee,
	flagPullRequestLabels,
	flagPullRequestFile string
	flagPullRequestBrowse,
	flagPullRequestForce bool
	flagPullRequestMilestone uint64
)

func init() {
	cmdPullRequest.Flag.StringVarP(&flagPullRequestBase, "base", "b", "", "BASE")
	cmdPullRequest.Flag.StringVarP(&flagPullRequestHead, "head", "h", "", "HEAD")
	cmdPullRequest.Flag.StringVarP(&flagPullRequestIssue, "issue", "i", "", "ISSUE")
	cmdPullRequest.Flag.BoolVarP(&flagPullRequestBrowse, "browse", "o", false, "BROWSE")
	cmdPullRequest.Flag.StringVarP(&flagPullRequestMessage, "message", "m", "", "MESSAGE")
	cmdPullRequest.Flag.BoolVarP(&flagPullRequestForce, "force", "f", false, "FORCE")
	cmdPullRequest.Flag.StringVarP(&flagPullRequestFile, "file", "F", "", "FILE")
	cmdPullRequest.Flag.StringVarP(&flagPullRequestAssignee, "assign", "a", "", "USER")
	cmdPullRequest.Flag.Uint64VarP(&flagPullRequestMilestone, "milestone", "M", 0, "MILESTONE")
	cmdPullRequest.Flag.StringVarP(&flagPullRequestLabels, "labels", "l", "", "LABELS")

	CmdRunner.Use(cmdPullRequest)
}

func pullRequest(cmd *Command, args *Args) {
	localRepo, err := github.LocalRepo()
	utils.Check(err)

	currentBranch, err := localRepo.CurrentBranch()
	utils.Check(err)

	baseProject, err := localRepo.MainProject()
	utils.Check(err)

	host, err := github.CurrentConfig().PromptForHost(baseProject.Host)
	if err != nil {
		utils.Check(github.FormatError("creating pull request", err))
	}

	trackedBranch, headProject, err := localRepo.RemoteBranchAndProject(host.User, false)
	utils.Check(err)

	var (
		base, head string
		force      bool
	)

	force = flagPullRequestForce

	if flagPullRequestBase != "" {
		baseProject, base = parsePullRequestProject(baseProject, flagPullRequestBase)
	}

	if flagPullRequestHead != "" {
		headProject, head = parsePullRequestProject(headProject, flagPullRequestHead)
	}

	if args.ParamsSize() == 1 {
		arg := args.RemoveParam(0)
		flagPullRequestIssue = parsePullRequestIssueNumber(arg)
	}

	if base == "" {
		masterBranch := localRepo.MasterBranch()
		base = masterBranch.ShortName()
	}

	if head == "" && trackedBranch != nil {
		if !trackedBranch.IsRemote() {
			// the current branch tracking another branch
			// pretend there's no upstream at all
			trackedBranch = nil
		} else {
			if baseProject.SameAs(headProject) && base == trackedBranch.ShortName() {
				e := fmt.Errorf(`Aborted: head branch is the same as base ("%s")`, base)
				e = fmt.Errorf("%s\n(use `-h <branch>` to specify an explicit pull request head)", e)
				utils.Check(e)
			}
		}
	}

	if head == "" {
		if trackedBranch == nil {
			head = currentBranch.ShortName()
		} else {
			head = trackedBranch.ShortName()
		}
	}

	title, body, err := getTitleAndBodyFromFlags(flagPullRequestMessage, flagPullRequestFile)
	utils.Check(err)

	fullBase := fmt.Sprintf("%s:%s", baseProject.Owner, base)
	fullHead := fmt.Sprintf("%s:%s", headProject.Owner, head)

	if !force && trackedBranch != nil {
		remoteCommits, _ := git.RefList(trackedBranch.LongName(), "")
		if len(remoteCommits) > 0 {
			err = fmt.Errorf("Aborted: %d commits are not yet pushed to %s", len(remoteCommits), trackedBranch.LongName())
			err = fmt.Errorf("%s\n(use `-f` to force submit a pull request anyway)", err)
			utils.Check(err)
		}
	}

	var editor *github.Editor
	if title == "" && flagPullRequestIssue == "" {
		baseTracking := base
		headTracking := head

		remote := gitRemoteForProject(baseProject)
		if remote != nil {
			baseTracking = fmt.Sprintf("%s/%s", remote.Name, base)
		}
		if remote == nil || !baseProject.SameAs(headProject) {
			remote = gitRemoteForProject(headProject)
		}
		if remote != nil {
			headTracking = fmt.Sprintf("%s/%s", remote.Name, head)
		}

		message, err := pullRequestChangesMessage(baseTracking, headTracking, fullBase, fullHead)
		utils.Check(err)

		editor, err = github.NewEditor("PULLREQ", "pull request", message)
		utils.Check(err)

		title, body, err = editor.EditTitleAndBody()
		utils.Check(err)
	}

	if title == "" && flagPullRequestIssue == "" {
		utils.Check(fmt.Errorf("Aborting due to empty pull request title"))
	}

	var pullRequestURL string
	if args.Noop {
		args.Before(fmt.Sprintf("Would request a pull request to %s from %s", fullBase, fullHead), "")
		pullRequestURL = "PULL_REQUEST_URL"
	} else {
		var (
			pr  *octokit.PullRequest
			err error
		)

		client := github.NewClientWithHost(host)
		if title != "" {
			pr, err = client.CreatePullRequest(baseProject, base, fullHead, title, body)
		} else if flagPullRequestIssue != "" {
			pr, err = client.CreatePullRequestForIssue(baseProject, base, fullHead, flagPullRequestIssue)
		}

		if err == nil && editor != nil {
			defer editor.DeleteFile()
		}

		utils.Check(err)

		pullRequestURL = pr.HTMLURL

		if flagPullRequestAssignee != "" || flagPullRequestMilestone > 0 ||
			flagPullRequestLabels != "" {

			labels := []string{}
			for _, label := range strings.Split(flagPullRequestLabels, ",") {
				if label != "" {
					labels = append(labels, label)
				}
			}

			params := octokit.IssueParams{
				Assignee:  flagPullRequestAssignee,
				Milestone: flagPullRequestMilestone,
				Labels:    labels,
			}

			err = client.UpdateIssue(baseProject, pr.Number, params)
			utils.Check(err)
		}
	}

	if flagPullRequestBrowse {
		launcher, err := utils.BrowserLauncher()
		utils.Check(err)
		args.Replace(launcher[0], "", launcher[1:]...)
		args.AppendParams(pullRequestURL)
	} else {
		args.Replace("echo", "", pullRequestURL)
	}

	if flagPullRequestIssue != "" {
		args.After("echo", "Warning: Issue to pull request conversion is deprecated and might not work in the future.")
	}
}

func pullRequestChangesMessage(base, head, fullBase, fullHead string) (string, error) {
	var (
		defaultMsg string
		commitLogs string
		err        error
	)

	commits, _ := git.RefList(base, head)
	if len(commits) == 1 {
		defaultMsg, err = git.Show(commits[0])
		if err != nil {
			return "", err
		}
	} else if len(commits) > 1 {
		commitLogs, err = git.Log(base, head)
		if err != nil {
			return "", err
		}
	}

	cs := git.CommentChar()

	return renderPullRequestTpl(defaultMsg, cs, fullBase, fullHead, commitLogs)
}

func parsePullRequestProject(context *github.Project, s string) (p *github.Project, ref string) {
	p = context
	ref = s

	if strings.Contains(s, ":") {
		split := strings.SplitN(s, ":", 2)
		ref = split[1]
		var name string
		if !strings.Contains(split[0], "/") {
			name = context.Name
		}
		p = github.NewProject(split[0], name, context.Host)
	}

	return
}

func parsePullRequestIssueNumber(url string) string {
	u, e := github.ParseURL(url)
	if e != nil {
		return ""
	}

	r := regexp.MustCompile(`^issues\/(\d+)`)
	p := u.ProjectPath()
	if r.MatchString(p) {
		return r.FindStringSubmatch(p)[1]
	}

	return ""
}
<commit_msg>Support Pull Request tempalte<commit_after>package commands

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/github/hub/git"
	"github.com/github/hub/github"
	"github.com/github/hub/utils"
	"github.com/octokit/go-octokit/octokit"
)

var cmdPullRequest = &Command{
	Run: pullRequest,
	Usage: `
pull-request [-fo] [-b <BASE>] [-h <HEAD>] [-a <USER>] [-M <MILESTONE>] [-l <LABELS>]
pull-request -m <MESSAGE>
pull-request -F <FILE>
pull-request -i <ISSUE>
`,
	Long: `Create a GitHub pull request.

## Options:
	-f, --force
		Skip the check for unpushed commits.

	-m, --message <MESSAGE>
		Use the first line of <MESSAGE> as pull request title, and the rest as pull
		request description.

	-F, --file <FILE>
		Read the pull request title and description from <FILE>.

	-i, --issue <ISSUE>, <ISSUE-URL>
		(Deprecated) Convert <ISSUE> to a pull request.

	-o, --browse
		Open the new pull request in a web browser.

	-b, --base <BASE>
		The base branch in "[OWNER:]BRANCH" format. Defaults to the default branch
		(usually "master").

	-h, --head <HEAD>
		The head branch in "[OWNER:]BRANCH" format. Defaults to the current branch.

	-a, --assign <USER>
		Assign GitHub <USER> to this pull request.

	-M, --milestone <ID>
		Add this pull request to a GitHub milestone with id <ID>.

	-l, --labels <LABELS>
		Add a comma-separated list of labels to this pull request.

## See also:

hub(1), hub-merge(1), hub-checkout(1)
`,
}

var (
	flagPullRequestBase,
	flagPullRequestHead,
	flagPullRequestIssue,
	flagPullRequestMessage,
	flagPullRequestAssignee,
	flagPullRequestLabels,
	flagPullRequestFile string
	flagPullRequestBrowse,
	flagPullRequestForce bool
	flagPullRequestMilestone uint64
)

func init() {
	cmdPullRequest.Flag.StringVarP(&flagPullRequestBase, "base", "b", "", "BASE")
	cmdPullRequest.Flag.StringVarP(&flagPullRequestHead, "head", "h", "", "HEAD")
	cmdPullRequest.Flag.StringVarP(&flagPullRequestIssue, "issue", "i", "", "ISSUE")
	cmdPullRequest.Flag.BoolVarP(&flagPullRequestBrowse, "browse", "o", false, "BROWSE")
	cmdPullRequest.Flag.StringVarP(&flagPullRequestMessage, "message", "m", "", "MESSAGE")
	cmdPullRequest.Flag.BoolVarP(&flagPullRequestForce, "force", "f", false, "FORCE")
	cmdPullRequest.Flag.StringVarP(&flagPullRequestFile, "file", "F", "", "FILE")
	cmdPullRequest.Flag.StringVarP(&flagPullRequestAssignee, "assign", "a", "", "USER")
	cmdPullRequest.Flag.Uint64VarP(&flagPullRequestMilestone, "milestone", "M", 0, "MILESTONE")
	cmdPullRequest.Flag.StringVarP(&flagPullRequestLabels, "labels", "l", "", "LABELS")

	CmdRunner.Use(cmdPullRequest)
}

func pullRequest(cmd *Command, args *Args) {
	localRepo, err := github.LocalRepo()
	utils.Check(err)

	currentBranch, err := localRepo.CurrentBranch()
	utils.Check(err)

	baseProject, err := localRepo.MainProject()
	utils.Check(err)

	host, err := github.CurrentConfig().PromptForHost(baseProject.Host)
	if err != nil {
		utils.Check(github.FormatError("creating pull request", err))
	}

	trackedBranch, headProject, err := localRepo.RemoteBranchAndProject(host.User, false)
	utils.Check(err)

	var (
		base, head string
		force      bool
	)

	force = flagPullRequestForce

	if flagPullRequestBase != "" {
		baseProject, base = parsePullRequestProject(baseProject, flagPullRequestBase)
	}

	if flagPullRequestHead != "" {
		headProject, head = parsePullRequestProject(headProject, flagPullRequestHead)
	}

	if args.ParamsSize() == 1 {
		arg := args.RemoveParam(0)
		flagPullRequestIssue = parsePullRequestIssueNumber(arg)
	}

	if base == "" {
		masterBranch := localRepo.MasterBranch()
		base = masterBranch.ShortName()
	}

	if head == "" && trackedBranch != nil {
		if !trackedBranch.IsRemote() {
			// the current branch tracking another branch
			// pretend there's no upstream at all
			trackedBranch = nil
		} else {
			if baseProject.SameAs(headProject) && base == trackedBranch.ShortName() {
				e := fmt.Errorf(`Aborted: head branch is the same as base ("%s")`, base)
				e = fmt.Errorf("%s\n(use `-h <branch>` to specify an explicit pull request head)", e)
				utils.Check(e)
			}
		}
	}

	if head == "" {
		if trackedBranch == nil {
			head = currentBranch.ShortName()
		} else {
			head = trackedBranch.ShortName()
		}
	}

	title, body, err := getTitleAndBodyFromFlags(flagPullRequestMessage, flagPullRequestFile)
	utils.Check(err)

	fullBase := fmt.Sprintf("%s:%s", baseProject.Owner, base)
	fullHead := fmt.Sprintf("%s:%s", headProject.Owner, head)

	if !force && trackedBranch != nil {
		remoteCommits, _ := git.RefList(trackedBranch.LongName(), "")
		if len(remoteCommits) > 0 {
			err = fmt.Errorf("Aborted: %d commits are not yet pushed to %s", len(remoteCommits), trackedBranch.LongName())
			err = fmt.Errorf("%s\n(use `-f` to force submit a pull request anyway)", err)
			utils.Check(err)
		}
	}

	var editor *github.Editor
	if title == "" && flagPullRequestIssue == "" {
		baseTracking := base
		headTracking := head

		remote := gitRemoteForProject(baseProject)
		if remote != nil {
			baseTracking = fmt.Sprintf("%s/%s", remote.Name, base)
		}
		if remote == nil || !baseProject.SameAs(headProject) {
			remote = gitRemoteForProject(headProject)
		}
		if remote != nil {
			headTracking = fmt.Sprintf("%s/%s", remote.Name, head)
		}

		message, err := createPullRequestMessage(baseTracking, headTracking, fullBase, fullHead)
		utils.Check(err)

		editor, err = github.NewEditor("PULLREQ", "pull request", message)
		utils.Check(err)

		title, body, err = editor.EditTitleAndBody()
		utils.Check(err)
	}

	if title == "" && flagPullRequestIssue == "" {
		utils.Check(fmt.Errorf("Aborting due to empty pull request title"))
	}

	var pullRequestURL string
	if args.Noop {
		args.Before(fmt.Sprintf("Would request a pull request to %s from %s", fullBase, fullHead), "")
		pullRequestURL = "PULL_REQUEST_URL"
	} else {
		var (
			pr  *octokit.PullRequest
			err error
		)

		client := github.NewClientWithHost(host)
		if title != "" {
			pr, err = client.CreatePullRequest(baseProject, base, fullHead, title, body)
		} else if flagPullRequestIssue != "" {
			pr, err = client.CreatePullRequestForIssue(baseProject, base, fullHead, flagPullRequestIssue)
		}

		if err == nil && editor != nil {
			defer editor.DeleteFile()
		}

		utils.Check(err)

		pullRequestURL = pr.HTMLURL

		if flagPullRequestAssignee != "" || flagPullRequestMilestone > 0 ||
			flagPullRequestLabels != "" {

			labels := []string{}
			for _, label := range strings.Split(flagPullRequestLabels, ",") {
				if label != "" {
					labels = append(labels, label)
				}
			}

			params := octokit.IssueParams{
				Assignee:  flagPullRequestAssignee,
				Milestone: flagPullRequestMilestone,
				Labels:    labels,
			}

			err = client.UpdateIssue(baseProject, pr.Number, params)
			utils.Check(err)
		}
	}

	if flagPullRequestBrowse {
		launcher, err := utils.BrowserLauncher()
		utils.Check(err)
		args.Replace(launcher[0], "", launcher[1:]...)
		args.AppendParams(pullRequestURL)
	} else {
		args.Replace("echo", "", pullRequestURL)
	}

	if flagPullRequestIssue != "" {
		args.After("echo", "Warning: Issue to pull request conversion is deprecated and might not work in the future.")
	}
}

func createPullRequestMessage(base, head, fullBase, fullHead string) (string, error) {
	var (
		defaultMsg string
		commitLogs string
		err        error
	)

	commits, _ := git.RefList(base, head)
	if len(commits) == 1 {
		defaultMsg, err = git.Show(commits[0])
		if err != nil {
			return "", err
		}
	} else if len(commits) > 1 {
		commitLogs, err = git.Log(base, head)
		if err != nil {
			return "", err
		}
	}

	if tmplate := github.GetPullRequestTempalte(); tmplate != "" {
		if defaultMsg != "" {
			defaultMsg = defaultMsg + tmplate
		} else {
			defaultMsg = tmplate
		}
	}

	cs := git.CommentChar()

	return renderPullRequestTpl(defaultMsg, cs, fullBase, fullHead, commitLogs)
}

func parsePullRequestProject(context *github.Project, s string) (p *github.Project, ref string) {
	p = context
	ref = s

	if strings.Contains(s, ":") {
		split := strings.SplitN(s, ":", 2)
		ref = split[1]
		var name string
		if !strings.Contains(split[0], "/") {
			name = context.Name
		}
		p = github.NewProject(split[0], name, context.Host)
	}

	return
}

func parsePullRequestIssueNumber(url string) string {
	u, e := github.ParseURL(url)
	if e != nil {
		return ""
	}

	r := regexp.MustCompile(`^issues\/(\d+)`)
	p := u.ProjectPath()
	if r.MatchString(p) {
		return r.FindStringSubmatch(p)[1]
	}

	return ""
}
<|endoftext|>


<commit_before>package env
import (
  "diserve.didactia.org/lib/util"
  "strings"
  "log"
  "fmt"
)

type variables struct {
  PORT string
  DBIP string
  DBPORT string
  STYLEPATH string
  TMPLPATH string
  PRELOADSTYLES bool
  LOADTMPLONREQUEST bool
}

// Vars is a stuct that holds the current environment variables, needs to be initialised with Init(args []string)
var Vars variables

// Init initializes the Vars struct with the environment variables, the first string in args defines the environment,
// the following should be of the form "VAR:VALUE" witch overrides VAR with VALUE for the environment.
// With no matching environment, Vars will be initialized with development variables.
func Init(args []string) []string {
  head, tail := util.Shift(args)
  switch head {
  case "dev":
    development()
  default:
    development()
    return args
  }
  fmt.Println(Vars)
  tail, err := override(tail)
  if err != nil {
    log.Fatal(err)
  }
  fmt.Println(Vars)
  return tail
}

func override(args []string) ([]string, error) {
  head, tail := util.Shift(args)
  fieldAndValue := strings.Split(head, ":")
  if len(fieldAndValue) != 2 {
    return args, nil
  }
  field := fieldAndValue[0]
  value := fieldAndValue[1]
  fieldType, err := util.FieldType(&Vars, field)
  if err != nil {
    return nil, err
  }
  typedValue, err := util.StringToType(value, fieldType)
  if err != nil {
    return nil, err
  }
  err = util.Override(&Vars, field, typedValue)
  if err != nil {
    return nil, err
  }
  return override(tail)
}
<commit_msg>comment typo<commit_after>package env
import (
  "diserve.didactia.org/lib/util"
  "strings"
  "log"
  "fmt"
)

type variables struct {
  PORT string
  DBIP string
  DBPORT string
  STYLEPATH string
  TMPLPATH string
  PRELOADSTYLES bool
  LOADTMPLONREQUEST bool
}

// Vars is a struct that holds the current environment variables, needs to be initialised with Init(args []string)
var Vars variables

// Init initializes the Vars struct with the environment variables, the first string in args defines the environment,
// the following should be of the form "VAR:VALUE" witch overrides VAR with VALUE for the environment.
// With no matching environment, Vars will be initialized with development variables.
func Init(args []string) []string {
  head, tail := util.Shift(args)
  switch head {
  case "dev":
    development()
  default:
    development()
    return args
  }
  fmt.Println(Vars)
  tail, err := override(tail)
  if err != nil {
    log.Fatal(err)
  }
  fmt.Println(Vars)
  return tail
}

func override(args []string) ([]string, error) {
  head, tail := util.Shift(args)
  fieldAndValue := strings.Split(head, ":")
  if len(fieldAndValue) != 2 {
    return args, nil
  }
  field := fieldAndValue[0]
  value := fieldAndValue[1]
  fieldType, err := util.FieldType(&Vars, field)
  if err != nil {
    return nil, err
  }
  typedValue, err := util.StringToType(value, fieldType)
  if err != nil {
    return nil, err
  }
  err = util.Override(&Vars, field, typedValue)
  if err != nil {
    return nil, err
  }
  return override(tail)
}
<|endoftext|>


<commit_before>package lib

import (
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"golang.org/x/net/context"
)

// The key type is unexported to prevent collisions with context keys defined in
// other packages.
type key int

// idtKey is the context key for an identity.  Its value of zero is
// arbitrary.  If this package defined other context keys, they would have
// different integer values.
const idtKey key = 0

// NewContext returns a new Context carrying an Identity pat.
func NewContext(ctx context.Context, idt *Identity) context.Context {
	return context.WithValue(ctx, idtKey, idt)
}

// FromContext extracts the Identity pat from ctx, if present.
func FromContext(ctx context.Context) (*Identity, bool) {
	// ctx.Value returns nil if ctx has no value for the key;
	p, ok := ctx.Value(idtKey).(*Identity)
	return p, ok
}

// MustFromContext extracts the identity from ctx.
// If not present it panics.
func MustFromContext(ctx context.Context) *Identity {
	idt, ok := ctx.Value(idtKey).(*Identity)
	if !ok {
		panic("identity is not registered")
	}
	return idt
}

func ParseToken(t, secret string) (*Identity, error) {

	token, err := jwt.Parse(t, func(token *jwt.Token) (key interface{}, err error) {
		return []byte(secret), nil
	})

	if err != nil {
		return nil, err
	}

	identity := &Identity{}

	pidString, ok := token.Claims["pid"].(string)
	if !ok {
		return nil, fmt.Errorf("failed cast to string of pid:%s",
			fmt.Sprintln(token.Claims["pid"]))
	}
	idpString, ok := token.Claims["idp"].(string)
	if !ok {
		return nil, fmt.Errorf("failed cast to string of idp:%s",
			fmt.Sprintln(token.Claims["idp"]))
	}
	displaynameString, ok := token.Claims["display_name"].(string)
	if !ok {
		return nil, fmt.Errorf("failed cast to string of display_ame:%s",
			fmt.Sprintln(token.Claims["display_ame"]))
	}
	emailString, ok := token.Claims["email"].(string)
	if !ok {
		return nil, fmt.Errorf("failed cast to string of email:%s",
			fmt.Sprintln(token.Claims["email"]))
	}

	identity.Pid = pidString
	identity.Idp = idpString
	identity.DisplayName = displaynameString
	identity.Email = emailString

	return identity, nil
}

type Identity struct {
	Pid         string `json:"pid"`
	Idp         string `json:"idp"`
	Email       string `json:"email"`
	DisplayName string `json:"email"`
}

func (i *Identity) String() string {
	return fmt.Sprintf("pid:%s idp:%s email:%s", i.Pid, i.Idp, i.Email)
}
<commit_msg>Fix identity String()<commit_after>package lib

import (
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"golang.org/x/net/context"
)

// The key type is unexported to prevent collisions with context keys defined in
// other packages.
type key int

// idtKey is the context key for an identity.  Its value of zero is
// arbitrary.  If this package defined other context keys, they would have
// different integer values.
const idtKey key = 0

// NewContext returns a new Context carrying an Identity pat.
func NewContext(ctx context.Context, idt *Identity) context.Context {
	return context.WithValue(ctx, idtKey, idt)
}

// FromContext extracts the Identity pat from ctx, if present.
func FromContext(ctx context.Context) (*Identity, bool) {
	// ctx.Value returns nil if ctx has no value for the key;
	p, ok := ctx.Value(idtKey).(*Identity)
	return p, ok
}

// MustFromContext extracts the identity from ctx.
// If not present it panics.
func MustFromContext(ctx context.Context) *Identity {
	idt, ok := ctx.Value(idtKey).(*Identity)
	if !ok {
		panic("identity is not registered")
	}
	return idt
}

func ParseToken(t, secret string) (*Identity, error) {

	token, err := jwt.Parse(t, func(token *jwt.Token) (key interface{}, err error) {
		return []byte(secret), nil
	})

	if err != nil {
		return nil, err
	}

	identity := &Identity{}

	pidString, ok := token.Claims["pid"].(string)
	if !ok {
		return nil, fmt.Errorf("failed cast to string of pid:%s",
			fmt.Sprintln(token.Claims["pid"]))
	}
	idpString, ok := token.Claims["idp"].(string)
	if !ok {
		return nil, fmt.Errorf("failed cast to string of idp:%s",
			fmt.Sprintln(token.Claims["idp"]))
	}
	displaynameString, ok := token.Claims["display_name"].(string)
	if !ok {
		return nil, fmt.Errorf("failed cast to string of display_ame:%s",
			fmt.Sprintln(token.Claims["display_ame"]))
	}
	emailString, ok := token.Claims["email"].(string)
	if !ok {
		return nil, fmt.Errorf("failed cast to string of email:%s",
			fmt.Sprintln(token.Claims["email"]))
	}

	identity.Pid = pidString
	identity.Idp = idpString
	identity.DisplayName = displaynameString
	identity.Email = emailString

	return identity, nil
}

type Identity struct {
	Pid         string `json:"pid"`
	Idp         string `json:"idp"`
	Email       string `json:"email"`
	DisplayName string `json:"email"`
}

func (i *Identity) String() string {
	return fmt.Sprintf("identity(pid:%s idp:%s email:%s)", i.Pid, i.Idp, i.Email)
}
<|endoftext|>


<commit_before>// Copyright 2012 Francisco Souza. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package git

import (
	"log"
	"os"
	"os/exec"
	"path"
	"testing"
)

func createRepository() string {
	tmpdir := os.TempDir()
	p := path.Join(tmpdir, "gitrepo")
	_, err := os.Stat(p)
	if !os.IsNotExist(err) {
		os.RemoveAll(p)
	}
	err = os.MkdirAll(p, 0700)
	if err != nil {
		panic(err)
	}
	cmd := exec.Command("git", "init", "-q", p)
	err = cmd.Run()
	if err != nil {
		panic(err)
	}
	configPath := path.Join(p, ".git", "config")
	cmd = exec.Command("cp", "testdata/config", configPath)
	err = cmd.Run()
	if err != nil {
		panic(err)
	}
	err = exec.Command("touch", path.Join(p, "README")).Run()
	if err != nil {
		panic(err)
	}
	err = exec.Command("git", "--work-tree="+p, "--git-dir="+p+"/.git", "add", ".").Run()
	if err != nil {
		panic(err)
	}
	out, err := exec.Command("git", "--work-tree="+p, "--git-dir="+p+"/.git", "commit", "-m", "initial commit").CombinedOutput()
	if err != nil {
		log.Panicf("Failed to commit (%s): %s", err, out)
	}
	return p
}

func removeRepository(path string) {
	err := os.RemoveAll(path)
	if err != nil {
		panic(err)
	}
}

func TestConfigGetBool(t *testing.T) {
	p := createRepository()
	defer removeRepository(p)
	r, err := OpenRepository(p)
	if err != nil {
		t.Error(err)
		t.FailNow()
	}
	defer r.Free()
	config, err := r.Config()
	if err != nil {
		t.Error(err)
		t.FailNow()
	}
	ignorecase, err := config.GetBool("core.ignorecase")
	if err != nil {
		t.Error(err)
	} else if !ignorecase {
		t.Error("Failed to get core.ignorecase. Want true, got false.")
	}
}

func TestConfigGetString(t *testing.T) {
	p := createRepository()
	defer removeRepository(p)
	r, err := OpenRepository(p)
	if err != nil {
		t.Error(err)
		t.FailNow()
	}
	defer r.Free()
	config, err := r.Config()
	if err != nil {
		t.Error(err)
		t.FailNow()
	}
	user, err := config.GetString("github.user")
	if err != nil {
		t.Error(err)
	} else if user != "fsouza" {
		t.Errorf("Failed to get github.user. Want fsouza, got %s.", user)
	}
}

func TestConfigGetInt64(t *testing.T) {
	p := createRepository()
	defer removeRepository(p)
	r, err := OpenRepository(p)
	if err != nil {
		t.Error(err)
		t.FailNow()
	}
	defer r.Free()
	config, err := r.Config()
	if err != nil {
		t.Error(err)
		t.FailNow()
	}
	commits, err := config.GetInt64("section.commits")
	if err != nil {
		t.Error(err)
	} else if commits != 800 {
		t.Errorf("Failed to get section.commits. Want 800, got %d.", commits)
	}
}

func TestConfigSetBool(t *testing.T) {
	p := createRepository()
	defer removeRepository(p)
	r, err := OpenRepository(p)
	if err != nil {
		t.Error(err)
		t.FailNow()
	}
	defer r.Free()
	config, err := r.Config()
	if err != nil {
		t.Error(err)
		t.FailNow()
	}
	err = config.SetBool("core.ignorecase", false)
	if err != nil {
		t.Error(err)
		t.FailNow()
	}
	err = config.SetBool("github.login", true)
	if err != nil {
		t.Error(err)
		t.FailNow()
	}
	ignorecase, _ := config.GetBool("core.ignorecase")
	if ignorecase {
		t.Error("Failed to set core.ignorecase to false.")
	}
	login, err := config.GetBool("github.login")
	if err != nil {
		t.Error(err)
	} else if !login {
		t.Error("Set github.login to false instead of setting it to true.")
	}
}

func TestConfigSetInt64(t *testing.T) {
	p := createRepository()
	defer removeRepository(p)
	r, err := OpenRepository(p)
	if err != nil {
		t.Error(err)
		t.FailNow()
	}
	defer r.Free()
	config, err := r.Config()
	if err != nil {
		t.Error(err)
		t.FailNow()
	}
	err = config.SetInt64("section.commits", 300)
	if err != nil {
		t.Error(err)
		t.FailNow()
	}
	err = config.SetInt64("section.errors", -10)
	if err != nil {
		t.Error(err)
		t.FailNow()
	}
	commits, _ := config.GetInt64("section.commits")
	if commits != 300 {
		t.Errorf("Failed to get the right value for commits. Want 300, got %d.", commits)
	}
	errors, _ := config.GetInt64("section.errors")
	if errors != -10 {
		t.Errorf("Failed to errors. Want -10, got %d.", errors)
	}
}

func TestConfigSetString(t *testing.T) {
	p := createRepository()
	defer removeRepository(p)
	r, err := OpenRepository(p)
	if err != nil {
		t.Error(err)
		t.FailNow()
	}
	defer r.Free()
	config, err := r.Config()
	if err != nil {
		t.Error(err)
		t.FailNow()
	}
	err = config.SetString("github.user", "franciscosouza")
	if err != nil {
		t.Error(err)
		t.FailNow()
	}
	user, _ := config.GetString("github.user")
	if user != "franciscosouza" {
		t.Errorf("Failed to set github.user value, it's %s.", user)
	}
}

func TestHead(t *testing.T) {
	p := createRepository()
	defer removeRepository(p)
	r, err := OpenRepository(p)
	if err != nil {
		t.Error(err)
		t.FailNow()
	}
	defer r.Free()
	last, err := exec.Command("git", "--work-tree="+p, "--git-dir="+p+"/.git", "log", "-1", "--format=format:%H").CombinedOutput()
	if err != nil {
		t.Errorf("Failed to get last commit (%s): %s", err, last)
		t.FailNow()
	}
	commit, err := r.Head()
	if err != nil {
		t.Error(err)
		t.FailNow()
	}
	if commit.Id() != string(last) {
		t.Errorf("Failed to get head. Want %s, got %s.", last, commit.Id())
	}
}

func TestGitErrorIsAnError(t *testing.T) {
	var _ error = GitError("")
}

func TestGitError(t *testing.T) {
	expected := "Failed to do git stuff."
	err := GitError(expected)
	got := err.Error()
	if got != expected {
		t.Errorf("GitError failed on checking message. Want %s, got %s.", expected, got)
	}
}
<commit_msg>tests: using Fatal instead of error + failnow<commit_after>// Copyright 2012 Francisco Souza. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package git

import (
	"log"
	"os"
	"os/exec"
	"path"
	"testing"
)

func createRepository() string {
	tmpdir := os.TempDir()
	p := path.Join(tmpdir, "gitrepo")
	_, err := os.Stat(p)
	if !os.IsNotExist(err) {
		os.RemoveAll(p)
	}
	err = os.MkdirAll(p, 0700)
	if err != nil {
		panic(err)
	}
	cmd := exec.Command("git", "init", "-q", p)
	err = cmd.Run()
	if err != nil {
		panic(err)
	}
	configPath := path.Join(p, ".git", "config")
	cmd = exec.Command("cp", "testdata/config", configPath)
	err = cmd.Run()
	if err != nil {
		panic(err)
	}
	err = exec.Command("touch", path.Join(p, "README")).Run()
	if err != nil {
		panic(err)
	}
	err = exec.Command("git", "--work-tree="+p, "--git-dir="+p+"/.git", "add", ".").Run()
	if err != nil {
		panic(err)
	}
	out, err := exec.Command("git", "--work-tree="+p, "--git-dir="+p+"/.git", "commit", "-m", "initial commit").CombinedOutput()
	if err != nil {
		log.Panicf("Failed to commit (%s): %s", err, out)
	}
	return p
}

func removeRepository(path string) {
	err := os.RemoveAll(path)
	if err != nil {
		panic(err)
	}
}

func TestConfigGetBool(t *testing.T) {
	p := createRepository()
	defer removeRepository(p)
	r, err := OpenRepository(p)
	if err != nil {
		t.Fatal(err)
	}
	defer r.Free()
	config, err := r.Config()
	if err != nil {
		t.Fatal(err)
	}
	ignorecase, err := config.GetBool("core.ignorecase")
	if err != nil {
		t.Fatal(err)
	} else if !ignorecase {
		t.Error("Failed to get core.ignorecase. Want true, got false.")
	}
}

func TestConfigGetString(t *testing.T) {
	p := createRepository()
	defer removeRepository(p)
	r, err := OpenRepository(p)
	if err != nil {
		t.Fatal(err)
	}
	defer r.Free()
	config, err := r.Config()
	if err != nil {
		t.Fatal(err)
	}
	user, err := config.GetString("github.user")
	if err != nil {
		t.Fatal(err)
	} else if user != "fsouza" {
		t.Errorf("Failed to get github.user. Want fsouza, got %s.", user)
	}
}

func TestConfigGetInt64(t *testing.T) {
	p := createRepository()
	defer removeRepository(p)
	r, err := OpenRepository(p)
	if err != nil {
		t.Fatal(err)
	}
	defer r.Free()
	config, err := r.Config()
	if err != nil {
		t.Fatal(err)
	}
	commits, err := config.GetInt64("section.commits")
	if err != nil {
		t.Fatal(err)
	} else if commits != 800 {
		t.Errorf("Failed to get section.commits. Want 800, got %d.", commits)
	}
}

func TestConfigSetBool(t *testing.T) {
	p := createRepository()
	defer removeRepository(p)
	r, err := OpenRepository(p)
	if err != nil {
		t.Fatal(err)
	}
	defer r.Free()
	config, err := r.Config()
	if err != nil {
		t.Fatal(err)
	}
	err = config.SetBool("core.ignorecase", false)
	if err != nil {
		t.Fatal(err)
	}
	err = config.SetBool("github.login", true)
	if err != nil {
		t.Fatal(err)
	}
	ignorecase, _ := config.GetBool("core.ignorecase")
	if ignorecase {
		t.Error("Failed to set core.ignorecase to false.")
	}
	login, err := config.GetBool("github.login")
	if err != nil {
		t.Fatal(err)
	} else if !login {
		t.Error("Set github.login to false instead of setting it to true.")
	}
}

func TestConfigSetInt64(t *testing.T) {
	p := createRepository()
	defer removeRepository(p)
	r, err := OpenRepository(p)
	if err != nil {
		t.Fatal(err)
	}
	defer r.Free()
	config, err := r.Config()
	if err != nil {
		t.Fatal(err)
	}
	err = config.SetInt64("section.commits", 300)
	if err != nil {
		t.Fatal(err)
	}
	err = config.SetInt64("section.errors", -10)
	if err != nil {
		t.Fatal(err)
	}
	commits, _ := config.GetInt64("section.commits")
	if commits != 300 {
		t.Errorf("Failed to get the right value for commits. Want 300, got %d.", commits)
	}
	errors, _ := config.GetInt64("section.errors")
	if errors != -10 {
		t.Errorf("Failed to errors. Want -10, got %d.", errors)
	}
}

func TestConfigSetString(t *testing.T) {
	p := createRepository()
	defer removeRepository(p)
	r, err := OpenRepository(p)
	if err != nil {
		t.Fatal(err)
	}
	defer r.Free()
	config, err := r.Config()
	if err != nil {
		t.Fatal(err)
	}
	err = config.SetString("github.user", "franciscosouza")
	if err != nil {
		t.Fatal(err)
	}
	user, _ := config.GetString("github.user")
	if user != "franciscosouza" {
		t.Errorf("Failed to set github.user value, it's %s.", user)
	}
}

func TestHead(t *testing.T) {
	p := createRepository()
	defer removeRepository(p)
	r, err := OpenRepository(p)
	if err != nil {
		t.Fatal(err)
	}
	defer r.Free()
	last, err := exec.Command("git", "--work-tree="+p, "--git-dir="+p+"/.git", "log", "-1", "--format=format:%H").CombinedOutput()
	if err != nil {
		t.Fatalf("Failed to get last commit (%s): %s", err, last)
	}
	commit, err := r.Head()
	if err != nil {
		t.Fatal(err)
	}
	if commit.Id() != string(last) {
		t.Errorf("Failed to get head. Want %s, got %s.", last, commit.Id())
	}
}

func TestGitErrorIsAnError(t *testing.T) {
	var _ error = GitError("")
}

func TestGitError(t *testing.T) {
	expected := "Failed to do git stuff."
	err := GitError(expected)
	got := err.Error()
	if got != expected {
		t.Errorf("GitError failed on checking message. Want %s, got %s.", expected, got)
	}
}
<|endoftext|>


<commit_before>package github

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/crosbymichael/octokat"
)

const (
	pollKey      = "USER POLL"
	pollTemplate = `*USER POLL*

*The best way to get notified of updates is to use the _Subscribe_ button on this page.*

Please don't use "+1" or "I have this too" comments on issues. We automatically
collect those comments to keep the thread short.

The people listed below have upvoted this issue by leaving a +1 comment:
`
)

// LabelIssueComment checks if someone has claimed dibs on this issue
func (g GitHub) LabelIssueComment(issueHook *octokat.IssueHook) error {
	if err := g.maybeClaimIssue(issueHook); err != nil {
		return err
	}

	return g.maybeOpinion(issueHook)
}

func (g GitHub) maybeClaimIssue(issueHook *octokat.IssueHook) error {
	labelmap := map[string]string{
		"#dibs":    "status/claimed",
		"#claimed": "status/claimed",
		"#mine":    "status/claimed",
	}

	repo := nameWithOwner(issueHook.Repo)

	for token, label := range labelmap {
		// if comment matches predefined actions AND author is not bot
		if strings.Contains(strings.ToLower(issueHook.Comment.Body), token) && g.User != issueHook.Sender.Login {
			logrus.Debugf("Adding label %#v to issue %d", label, issueHook.Issue.Number)
			if err := g.addLabel(repo, issueHook.Issue.Number, label); err != nil {
				return err
			}
			logrus.Infof("Added label %#v to issue %d", label, issueHook.Issue.Number)
		}
	}
	return nil
}

func (g GitHub) maybeOpinion(issueHook *octokat.IssueHook) error {
	body := strings.TrimSpace(issueHook.Comment.Body)

	if body == "+1" {
		login := issueHook.Comment.User.Login
		commenters := map[string]int{login: issueHook.Comment.Id}

		options := &octokat.Options{
			QueryParams: map[string]string{"per_page": "100"},
		}

		repo := getRepo(issueHook.Repo)
		issueID := strconv.Itoa(issueHook.Issue.Number)
		comments, err := g.Client().Comments(repo, issueID, options)
		if err != nil {
			return err
		}

		var poll octokat.Comment

		for _, c := range comments {
			if strings.ToLower(c.User.Login) == g.User && strings.Contains(c.Body, pollKey) {
				poll = c
			} else if strings.TrimSpace(c.Body) == "+1" || strings.TrimSpace(c.Body) == ":+1:" {
				commenters[c.User.Login] = c.Id
			}
		}

		if poll.Body != "" {
			if !strings.Contains(poll.Body, login) {
				for k := range commenters {
					poll.Body += fmt.Sprintf("\n@%s", k)
				}
				if _, err := g.Client().PatchComment(repo, strconv.Itoa(poll.Id), poll.Body); err != nil {
					return err
				}
			}
		} else {
			tmpl := pollTemplate
			for k := range commenters {
				tmpl += fmt.Sprintf("\n@%s", k)
			}
			if _, err := g.Client().AddComment(repo, issueID, tmpl); err != nil {
				return err
			}
		}

		for _, v := range commenters {
			if err := g.Client().RemoveComment(repo, v); err != nil {
				return err
			}
		}
	}

	return nil
}

func (g GitHub) IssueAddVersionLabel(issueHook *octokat.IssueHook) error {
	serverVersion := regexp.MustCompile(`Server:\s+Version:\s+(\d+\.\d+\.\d+)`)
	versionSubmatch := serverVersion.FindStringSubmatch(issueHook.Issue.Body)
	if len(versionSubmatch) < 2 {
		return nil
	}

	// For a version `X.Y.Z`, add a label of the form `version/X.Y`.
	version := versionSubmatch[1]
	shortVersion := version[0:strings.LastIndex(version, ".")]
	return g.addLabel(nameWithOwner(issueHook.Repo), issueHook.Issue.Number, "version/"+shortVersion)
}
<commit_msg>Take version suffix into account for label<commit_after>package github

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/crosbymichael/octokat"
)

const (
	pollKey      = "USER POLL"
	pollTemplate = `*USER POLL*

*The best way to get notified of updates is to use the _Subscribe_ button on this page.*

Please don't use "+1" or "I have this too" comments on issues. We automatically
collect those comments to keep the thread short.

The people listed below have upvoted this issue by leaving a +1 comment:
`
)

// LabelIssueComment checks if someone has claimed dibs on this issue
func (g GitHub) LabelIssueComment(issueHook *octokat.IssueHook) error {
	if err := g.maybeClaimIssue(issueHook); err != nil {
		return err
	}

	return g.maybeOpinion(issueHook)
}

func (g GitHub) maybeClaimIssue(issueHook *octokat.IssueHook) error {
	labelmap := map[string]string{
		"#dibs":    "status/claimed",
		"#claimed": "status/claimed",
		"#mine":    "status/claimed",
	}

	repo := nameWithOwner(issueHook.Repo)

	for token, label := range labelmap {
		// if comment matches predefined actions AND author is not bot
		if strings.Contains(strings.ToLower(issueHook.Comment.Body), token) && g.User != issueHook.Sender.Login {
			logrus.Debugf("Adding label %#v to issue %d", label, issueHook.Issue.Number)
			if err := g.addLabel(repo, issueHook.Issue.Number, label); err != nil {
				return err
			}
			logrus.Infof("Added label %#v to issue %d", label, issueHook.Issue.Number)
		}
	}
	return nil
}

func (g GitHub) maybeOpinion(issueHook *octokat.IssueHook) error {
	body := strings.TrimSpace(issueHook.Comment.Body)

	if body == "+1" {
		login := issueHook.Comment.User.Login
		commenters := map[string]int{login: issueHook.Comment.Id}

		options := &octokat.Options{
			QueryParams: map[string]string{"per_page": "100"},
		}

		repo := getRepo(issueHook.Repo)
		issueID := strconv.Itoa(issueHook.Issue.Number)
		comments, err := g.Client().Comments(repo, issueID, options)
		if err != nil {
			return err
		}

		var poll octokat.Comment

		for _, c := range comments {
			if strings.ToLower(c.User.Login) == g.User && strings.Contains(c.Body, pollKey) {
				poll = c
			} else if strings.TrimSpace(c.Body) == "+1" || strings.TrimSpace(c.Body) == ":+1:" {
				commenters[c.User.Login] = c.Id
			}
		}

		if poll.Body != "" {
			if !strings.Contains(poll.Body, login) {
				for k := range commenters {
					poll.Body += fmt.Sprintf("\n@%s", k)
				}
				if _, err := g.Client().PatchComment(repo, strconv.Itoa(poll.Id), poll.Body); err != nil {
					return err
				}
			}
		} else {
			tmpl := pollTemplate
			for k := range commenters {
				tmpl += fmt.Sprintf("\n@%s", k)
			}
			if _, err := g.Client().AddComment(repo, issueID, tmpl); err != nil {
				return err
			}
		}

		for _, v := range commenters {
			if err := g.Client().RemoveComment(repo, v); err != nil {
				return err
			}
		}
	}

	return nil
}

func labelFromVersion(version, suffix string) string {
	switch suffix {
	// Dev suffix is associated with a master build.
	case "dev":
		return "version/master"
	// For a version `X.Y.Z`, add a label of the form `version/X.Y`.
	case strings.HasPrefix(suffix, "cs"):
		fallthrough
	case strings.HasPrefix(suffix, "rc"):
		fallthrough
	case suffix == "":
		return "version/" + version[0:strings.LastIndex(version, ".")]
	// The default for unknown suffix is to consider the version unsupported.
	default:
		return "version/unsupported"
	}
}

func (g GitHub) IssueAddVersionLabel(issueHook *octokat.IssueHook) error {
	serverVersion := regexp.MustCompile(`Server:\s+Version:\s+(\d+\.\d+\.\d+)-?(\S*)`)
	versionSubmatch := serverVersion.FindStringSubmatch(issueHook.Issue.Body)
	if len(versionSubmatch) < 3 {
		return nil
	}

	label := labelFromVersion(versionSubmatch[1], versionSubmatch[2])
	return g.addLabel(nameWithOwner(issueHook.Repo), issueHook.Issue.Number, label)
}
<|endoftext|>


<commit_before>package storage

import "testing"

func TestRedisClusterGetMultiKey(t *testing.T) {
	keys := []string{"first", "second"}
	r := RedisCluster{KeyPrefix: "test-cluster"}
	for _, v := range keys {
		r.DeleteKey(v)
	}
	_, err := r.GetMultiKey(keys)
	if err != ErrKeyNotFound {
		t.Errorf("expected %v got %v", ErrKeyNotFound, err)
	}
	err = r.SetKey(keys[0], keys[0], 0)
	if err != nil {
		t.Fatal(err)
	}

	v, err := r.GetMultiKey(keys)
	if err != nil {
		t.Fatal(err)
	}
	if v[0] != keys[0] {
		t.Errorf("expected %s got %s", keys[0], v[0])
	}
}
<commit_msg>Fix storage tests (#2567)<commit_after>package storage

import "testing"

func TestRedisClusterGetMultiKey(t *testing.T) {
	t.Skip()

	keys := []string{"first", "second"}
	r := RedisCluster{KeyPrefix: "test-cluster"}
	for _, v := range keys {
		r.DeleteKey(v)
	}
	_, err := r.GetMultiKey(keys)
	if err != ErrKeyNotFound {
		t.Errorf("expected %v got %v", ErrKeyNotFound, err)
	}
	err = r.SetKey(keys[0], keys[0], 0)
	if err != nil {
		t.Fatal(err)
	}

	v, err := r.GetMultiKey(keys)
	if err != nil {
		t.Fatal(err)
	}
	if v[0] != keys[0] {
		t.Errorf("expected %s got %s", keys[0], v[0])
	}
}
<|endoftext|>