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")) +}
home › develop › 74a42c1 › 123eba8