func push() {
	src.Info(statusRepoPush)
	tDir, err := src.GetTargetDir(src.GlobalConfig, targetDirFlag, false)
	if err != nil {
		src.Defeat(err)
	}
	r, err := git.PlainOpen(tDir)
	if err != nil {
		src.Defeat(err)
	}
	err = r.Push(&git.PushOptions{})
	if err != nil {
		src.Defeat(err)
	}
}
push pushes up to the remote git repo
package main
import (
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"
	"github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing/object"
	"github.com/olebedev/config"
	"github.com/senorprogrammer/til/pages"
	"github.com/senorprogrammer/til/src"
)
const (
	defaultCommitMsg = "build, save, push"
	defaultEditor = "open"
	errConfigValueRead = "could not read a required configuration value"
	errNoTitle         = "title must not be blank"
	statusDone     = "done"
	statusIdxBuild = "building index page"
	statusRepoPush = "pushing to remote"
	statusRepoSave = "saving uncommitted files"
	statusTagBuild = "building tag pages"
)
var (
	buildFlag     bool
	listFlag      bool
	saveFlag      bool
	targetDirFlag string
)
func init() {
	src.LL = log.New(os.Stdout, "", log.LstdFlags|log.Lshortfile)
	flag.BoolVar(&buildFlag, "b", false, "builds the index and tag pages (short-hand)")
	flag.BoolVar(&buildFlag, "build", false, "builds the index and tag pages")
	flag.BoolVar(&listFlag, "l", false, "lists the configured target directories (short-hand)")
	flag.BoolVar(&listFlag, "list", false, "lists the configured target directories")
	flag.BoolVar(&saveFlag, "s", false, "builds, saves, and pushes (short-hand)")
	flag.BoolVar(&saveFlag, "save", false, "builds, saves, and pushes")
	flag.StringVar(&targetDirFlag, "t", "", "specifies the target directory key (short-hand)")
	flag.StringVar(&targetDirFlag, "target", "", "specifies the target directory key")
}
func main() {
	flag.Parse()
	cnf := &src.Config{}
	cnf.Load()
	if listFlag {
		listTargetDirectories(src.GlobalConfig)
		src.Victory(statusDone)
	}
	if buildFlag {
		buildContent()
		src.Victory(statusDone)
	}
	if saveFlag {
		commitMsg := determineCommitMessage(src.GlobalConfig, os.Args)
		buildContent()
		save(commitMsg)
		push()
		src.Victory(statusDone)
	}
	src.BuildTargetDirectory()
	title := parseTitle(targetDirFlag, os.Args)
	if title == "" {
		src.Defeat(errors.New(errNoTitle))
	}
	createNewPage(title)
	src.Victory(statusDone)
}
func buildContent() {
	pages := loadPages()
	tagMap := buildTagPages(pages)
	buildIndexPage(pages, tagMap)
}
func buildIndexPage(pageSet []*pages.Page, tagMap *pages.TagMap) {
	src.Info(statusIdxBuild)
	content := ""
	tagLinks := []string{}
	for _, tagName := range tagMap.SortedTagNames() {
		tags := tagMap.Get(tagName)
		if len(tags) > 0 {
			tagLinks = append(tagLinks, tags[0].Link())
		}
	}
	content += strings.Join(tagLinks, ", ")
	content += "\n"
	content += pagesToHTMLUnorderedList(pageSet)
	content += "\n"
	content += "\n"
	content += src.Footer()
	tDir, err := src.GetTargetDir(src.GlobalConfig, targetDirFlag, true)
	if err != nil {
		src.Defeat(err)
	}
	filePath := fmt.Sprintf(
		"%s/index.%s",
		tDir,
		pages.FileExtension,
	)
	err = ioutil.WriteFile(filePath, []byte(content), 0644)
	if err != nil {
		src.Defeat(err)
	}
	src.Progress(filePath)
}
func buildTagPages(pageSet []*pages.Page) *pages.TagMap {
	src.Info(statusTagBuild)
	tagMap := pages.NewTagMap(pageSet)
	var wGroup sync.WaitGroup
	for _, tagName := range tagMap.SortedTagNames() {
		wGroup.Add(1)
		go func(tagName string) {
			defer wGroup.Done()
			content := fmt.Sprintf("## %s\n\n", tagName)
			content += pagesToHTMLUnorderedList(tagMap.PagesFor(tagName))
			content += "\n"
			content += src.Footer()
			tDir, err := src.GetTargetDir(src.GlobalConfig, targetDirFlag, true)
			if err != nil {
				src.Defeat(err)
			}
			filePath := fmt.Sprintf(
				"%s/%s.%s",
				tDir,
				tagName,
				pages.FileExtension,
			)
			err = ioutil.WriteFile(filePath, []byte(content), 0644)
			if err != nil {
				src.Defeat(err)
			}
			src.Progress(filePath)
		}(tagName)
	}
	wGroup.Wait()
	return tagMap
}
func createNewPage(title string) {
	tDir, err := src.GetTargetDir(src.GlobalConfig, targetDirFlag, true)
	if err != nil {
		src.Defeat(err)
	}
	page := pages.NewPage(title, tDir)
	err = page.Open(defaultEditor)
	if err != nil {
		src.Defeat(err)
	}
	src.Info(page.FilePath)
}
func determineCommitMessage(cfg *config.Config, args []string) string {
	if flag.NArg() == 0 {
		return cfg.UString("commitMessage", defaultCommitMsg)
	}
	msgOffset := len(args) - flag.NArg()
	msg := strings.Join(args[msgOffset:], " ")
	return msg
}
func listTargetDirectories(cfg *config.Config) {
	dirMap, err := cfg.Map("targetDirectories")
	if err != nil {
		src.Defeat(err)
	}
	for key, dir := range dirMap {
		src.Info(fmt.Sprintf("%6s\t%s\n", key, dir.(string)))
	}
}
func loadPages() []*pages.Page {
	pageSet := []*pages.Page{}
	tDir, err := src.GetTargetDir(src.GlobalConfig, targetDirFlag, true)
	if err != nil {
		src.Defeat(err)
	}
	filePaths, _ := filepath.Glob(
		fmt.Sprintf(
			"%s/*.%s",
			tDir,
			pages.FileExtension,
		),
	)
	for i := len(filePaths) - 1; i >= 0; i-- {
		page := pages.PageFromFilePath(filePaths[i])
		pageSet = append(pageSet, page)
	}
	return pageSet
}
func pagesToHTMLUnorderedList(pageSet []*pages.Page) string {
	content := ""
	prevPage := &pages.Page{}
	for _, page := range pageSet {
		if !page.IsContentPage() {
			continue
		}
		if prevPage.CreatedMonth() != page.CreatedMonth() {
			content += "\n"
		}
		content += fmt.Sprintf("* %s\n", page.Link())
		prevPage = page
	}
	return content
}
func parseTitle(targetFlag string, args []string) string {
	titleOffset := 3
	if targetFlag == "" {
		titleOffset = 1
	}
	return strings.Title(strings.Join(args[titleOffset:], " "))
}