gtx


Branch: develop

Author
Spike Lindsey <spike01@gmail.com>
Date
Jan. 31 '23 01:59:52
Commit
123eba82872f00e4620517601e194d58399844cb
Parent
74a42c11695cf9825333c659e0abec020348df79
Changes
diff --git a/branch_filter.go b/branch_filter.go
new file mode 100644
index 0000000..19f6127
--- /dev/null
+++ b/branch_filter.go
@@ -0,0 +1,234 @@
+package main
+
+import (
+	"bufio"
+	"bytes"
+	"fmt"
+	"log"
+	"os/exec"
+	"path/filepath"
+	"strings"
+	"time"
+)
+
+// Goes through list of branches and returns those that match whitelist.
+func branchFilter(repo string, options *options) ([]branch, error) {
+	cmd := exec.Command("git", "branch", "-a")
+	cmd.Dir = repo
+
+	whitelist := options.Branches
+
+	out, err := cmd.Output()
+
+	if err != nil {
+		return nil, err
+	}
+
+	var b = make(map[string]branch)
+	var m = make(map[string]bool)
+
+	scanner := bufio.NewScanner(bytes.NewReader(out))
+
+	for scanner.Scan() {
+		t := strings.TrimSpace(strings.TrimPrefix(scanner.Text(), "*"))
+		_, f := filepath.Split(t)
+
+		m[f] = true
+	}
+
+	if err := scanner.Err(); err != nil {
+		return nil, err
+	}
+
+	// Filter to match options, but return all if no branch flags given.
+	if len(whitelist) > 0 {
+		for k := range m {
+			m[k] = contains(whitelist, k)
+		}
+	} else {
+		// In git given order at this point.
+		for k := range m {
+			whitelist = append(whitelist, k)
+		}
+	}
+
+	for k, v := range m {
+		if v {
+			// TODO: Try a goroutine?
+			commits, err := commitParser(k, repo, options.Name)
+
+			if err != nil {
+				continue
+			}
+
+			b[k] = branch{commits, k, options.Name}
+		}
+	}
+
+	// Fill in resulting slice with desired branches in order.
+	var results []branch
+
+	for _, v := range whitelist {
+		results = append(results, b[v])
+	}
+
+	return results, nil
+}
+
+func commitParser(b string, repo string, name string) ([]commit, error) {
+	fst := strings.Join([]string{"%H", "%P", "%s", "%aN", "%aE", "%aD", "%h"}, SEP)
+	ref := fmt.Sprintf("origin/%s", b)
+
+	cmd := exec.Command("git", "log", fmt.Sprintf("--format=%s", fst), ref)
+	cmd.Dir = repo
+
+	out, err := cmd.Output()
+
+	if err != nil {
+		return nil, err
+	}
+
+	results := []commit{}
+	scanner := bufio.NewScanner(bytes.NewReader(out))
+
+	for scanner.Scan() {
+		text := strings.TrimSpace(scanner.Text())
+		data := strings.Split(text, SEP)
+
+		h := data[0]
+
+		var history []overview
+		var parents []string
+
+		if data[1] != "" {
+			parents = strings.Split(data[1], " ")
+		}
+
+		for _, parent := range parents {
+			diffstat, err := diffStatParser(h, parent, repo)
+
+			if err != nil {
+				log.Printf("unable to diffstat against parent: %s", err)
+
+				continue
+			}
+
+			history = append(history, overview{diffstat, h, parent})
+		}
+
+		a := author{data[4], data[3]}
+
+		date, err := time.Parse("Mon, 2 Jan 2006 15:04:05 -0700", data[5])
+
+		if err != nil {
+			log.Printf("unable to parse commit date: %s", err)
+
+			continue
+		}
+
+		body, err := bodyParser(h, repo)
+
+		if err != nil {
+			log.Printf("unable to parse commit body: %s", err)
+
+			continue
+		}
+
+		tree, err := treeParser(h, repo)
+
+		if err != nil {
+			log.Printf("unable to parse commit tree: %s", err)
+
+			continue
+		}
+
+		c := commit{
+			Abbr:    data[6],
+			Author:  a,
+			Body:    body,
+			Branch:  b,
+			Date:    date,
+			Hash:    h,
+			History: history,
+			Parents: parents,
+			Project: name,
+			Subject: data[2],
+			Tree:    tree,
+		}
+
+		results = append(results, c)
+	}
+
+	if err := scanner.Err(); err != nil {
+		return nil, err
+	}
+
+	return results, nil
+}
+
+func treeParser(h string, repo string) ([]object, error) {
+	cmd := exec.Command("git", "ls-tree", "-r", "--format=%(objectname) %(path)", h)
+	cmd.Dir = repo
+
+	out, err := cmd.Output()
+
+	if err != nil {
+		return nil, err
+	}
+
+	var results []object
+	feed := strings.Split(strings.TrimSuffix(fmt.Sprintf("%s", out), "\n"), "\n")
+
+	for _, line := range feed {
+		w := strings.Split(line, " ")
+
+		results = append(results, object{
+			Hash: w[0],
+			Path: w[1],
+		})
+	}
+
+	return results, nil
+}
+
+func diffStatParser(h, parent string, repo string) (string, error) {
+	cmd := exec.Command("git", "diff", "--stat", fmt.Sprintf("%s..%s", parent, h))
+	cmd.Dir = repo
+
+	out, err := cmd.Output()
+
+	if err != nil {
+		return "", err
+	}
+
+	var results []string
+	feed := strings.Split(strings.TrimSuffix(fmt.Sprintf("%s", out), "\n"), "\n")
+
+	for _, line := range feed {
+		// NOTE: This is hackish I know, attach to project?
+		i := strings.Index(line, "|")
+
+		if i != -1 {
+			ext := filepath.Ext(strings.TrimSpace(line[:i]))
+			types[ext] = strings.Contains(line, "Bin")
+		}
+
+		results = append(results, strings.TrimSpace(line))
+	}
+
+	return strings.Join(results, "\n"), nil
+}
+
+func bodyParser(h string, repo string) (string, error) {
+	// Because the commit message body is multiline and is tripping the scanner.
+	cmd := exec.Command("git", "show", "--no-patch", "--format=%B", h)
+	cmd.Dir = repo
+
+	out, err := cmd.Output()
+
+	if err != nil {
+		return "", err
+	}
+
+	return strings.TrimSuffix(fmt.Sprintf("%s", out), "\n"), nil
+}
diff --git a/diff_parsers.go b/diff_parsers.go
new file mode 100644
index 0000000..eac48ab
--- /dev/null
+++ b/diff_parsers.go
@@ -0,0 +1,87 @@
+package main
+
+import (
+	"fmt"
+	"html/template"
+	"regexp"
+	"strings"
+)
+
+// Match diff body @@ del, ins line numbers.
+var aline = regexp.MustCompile(`\-(.*?),`)
+var bline = regexp.MustCompile(`\+(.*?),`)
+
+// Match diff body keywords.
+var xline = regexp.MustCompile(`^(deleted|index|new|rename|similarity)`)
+
+// Helps target file specific diff blocks.
+var diffanchor = regexp.MustCompile(`b\/(.*?)$`)
+
+func diffbodyparser(d diff) template.HTML {
+	var results []string
+	feed := strings.Split(strings.TrimSuffix(template.HTMLEscapeString(d.Body), "\n"), "\n")
+
+	var a, b string
+
+	for _, line := range feed {
+		if strings.HasPrefix(line, "diff") {
+			line = diffanchor.ReplaceAllString(line, `b/<a id="$1">$1</a>`)
+			line = fmt.Sprintf("<strong>%s</strong>", line)
+		}
+
+		line = xline.ReplaceAllString(line, "<em>$1</em>")
+
+		if strings.HasPrefix(line, "@@") {
+			if a != "" && !strings.HasPrefix(a, "---") {
+				repl := fmt.Sprintf(`<a href="commit/%s/%s.html#L$1">-$1</a>,`, d.Parent, a)
+				line = aline.ReplaceAllString(line, repl)
+			}
+
+			if b != "" && !strings.HasPrefix(b, "+++") {
+				repl := fmt.Sprintf(`<a href="commit/%s/%s.html#L$1">+$1</a>,`, d.Commit.Hash, b)
+				line = bline.ReplaceAllString(line, repl)
+			}
+		}
+
+		if strings.HasPrefix(line, "---") {
+			a = strings.TrimPrefix(line, "--- a/")
+			line = fmt.Sprintf("<mark>%s</mark>", line)
+		} else if strings.HasPrefix(line, "-") {
+			line = fmt.Sprintf("<del>%s</del>", line)
+		}
+
+		if strings.HasPrefix(line, "+++") {
+			b = strings.TrimPrefix(line, "+++ b/")
+			line = fmt.Sprintf("<mark>%s</mark>", line)
+		} else if strings.HasPrefix(line, "+") {
+			line = fmt.Sprintf("<ins>%s</ins>", line)
+		}
+
+		results = append(results, line)
+	}
+
+	return template.HTML(strings.Join(results, "\n"))
+}
+
+func diffstatbodyparser(o overview) template.HTML {
+	var results []string
+	feed := strings.Split(strings.TrimSuffix(o.Body, "\n"), "\n")
+
+	for i, line := range feed {
+		if i < len(feed)-1 {
+			// Link files to corresponding diff.
+			columns := strings.Split(line, "|")
+			files := strings.Split(columns[0], "=>")
+
+			a := strings.TrimSpace(files[len(files)-1])
+			b := fmt.Sprintf(`<a href="commit/%s/diff-%s.html#%s">%s</a>`, o.Hash, o.Parent, a, a)
+			l := strings.LastIndex(line, a)
+
+			line = line[:l] + strings.Replace(line[l:], a, b, 1)
+		}
+
+		results = append(results, line)
+	}
+
+	return template.HTML(strings.Join(results, "\n"))
+}