<commit_before>package status

import (
	"bufio"
	"bytes"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
)

// Process takes the raw output of `git status --porcelain -b -z` and turns it
// into a structured data type.
func Process(gitStatusOutput []byte, root string) *StatusList {
	// initialize a statuslist to hold the results
	results := NewStatusList()

	// put the output into a bufferreader+scanner so we can consume it iteratively
	scanner := bufio.NewScanner(bytes.NewReader(gitStatusOutput))

	// the scanner needs a custom split function for splitting on NUL
	scanNul := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
		for i, b := range data {
			if b == '\x00' {
				return i + 1, data[:i], nil
			}
		}
		return 0, nil, nil
	}
	scanner.Split(scanNul)

	// branch output is first line
	if !scanner.Scan() {
		log.Println("Failed to read buffer when expecting branch status")
		log.Fatal(scanner.Err())
	}
	branchBytes := scanner.Bytes()
	results.branch = ExtractBranch(branchBytes)

	// give ProcessChanges the scanner and let it handle the rest
	// (it does complicated stuff so it needs the entire scanner)
	for _, r := range ProcessChanges(scanner, root) {
		results.groups[r.group].items = append(results.groups[r.group].items, r)
	}

	return results
}

// ExtractBranch handles parsing the branch status from `status --porcelain -b`.
//
// Examples of stuff we will want to parse:
//
// 		## Initial commit on master
// 		## master
// 		## master...origin/master
// 		## master...origin/master [ahead 1]
//
func ExtractBranch(bs []byte) *BranchInfo {
	b := BranchInfo{}

	b.name = decodeBranchName(bs)
	b.ahead, b.behind = decodeBranchPosition(bs)

	return &b
}

func decodeBranchName(bs []byte) (branch string) {
	branchRegex := regexp.MustCompile(`^## (?:Initial commit on )?(\S+?)(?:\.{3}|$)`)
	headRegex := regexp.MustCompile(`^## (HEAD \(no branch\))`)

	branchMatch := branchRegex.FindSubmatch(bs)
	if branchMatch != nil {
		branch = string(branchMatch[1])
	}

	headMatch := headRegex.FindSubmatch(bs)
	if headMatch != nil {
		branch = string(headMatch[1])
	}

	if headMatch == nil && branchMatch == nil {
		log.Fatalf("Failed to parse branch name for output: [%s]", bs)
	}

	return
}

func decodeBranchPosition(bs []byte) (ahead, behind int) {
	reA := regexp.MustCompile(`\[ahead ?(\d+).*\]`)
	reB := regexp.MustCompile(`\[.*behind ?(\d+)\]`)<commit_msg>fix git init issue<commit_after>package status

import (
	"bufio"
	"bytes"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
)

// Process takes the raw output of `git status --porcelain -b -z` and turns it
// into a structured data type.
func Process(gitStatusOutput []byte, root string) *StatusList {
	// initialize a statuslist to hold the results
	results := NewStatusList()

	// put the output into a bufferreader+scanner so we can consume it iteratively
	scanner := bufio.NewScanner(bytes.NewReader(gitStatusOutput))

	// the scanner needs a custom split function for splitting on NUL
	scanNul := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
		for i, b := range data {
			if b == '\x00' {
				return i + 1, data[:i], nil
			}
		}
		return 0, nil, nil
	}
	scanner.Split(scanNul)

	// branch output is first line
	if !scanner.Scan() {
		log.Println("Failed to read buffer when expecting branch status")
		log.Fatal(scanner.Err())
	}
	branchBytes := scanner.Bytes()
	results.branch = ExtractBranch(branchBytes)

	// give ProcessChanges the scanner and let it handle the rest
	// (it does complicated stuff so it needs the entire scanner)
	for _, r := range ProcessChanges(scanner, root) {
		results.groups[r.group].items = append(results.groups[r.group].items, r)
	}

	return results
}

// ExtractBranch handles parsing the branch status from `status --porcelain -b`.
//
// Examples of stuff we will want to parse:
//
// 		## Initial commit on master
// 		## master
// 		## master...origin/master
// 		## master...origin/master [ahead 1]
//
func ExtractBranch(bs []byte) *BranchInfo {
	b := BranchInfo{}

	b.name = decodeBranchName(bs)
	b.ahead, b.behind = decodeBranchPosition(bs)

	return &b
}

func decodeBranchName(bs []byte) (branch string) {
	branchRegex := regexp.MustCompile(`^## (?:Initial commit on )?(?:No commits yet on )?(\S+?)(?:\.{3}|$)`)
	headRegex := regexp.MustCompile(`^## (HEAD \(no branch\))`)

	branchMatch := branchRegex.FindSubmatch(bs)
	if branchMatch != nil {
		branch = string(branchMatch[1])
	}

	headMatch := headRegex.FindSubmatch(bs)
	if headMatch != nil {
		branch = string(headMatch[1])
	}

	if headMatch == nil && branchMatch == nil {
		log.Fatalf("Failed to parse branch name for output: [%s]", bs)
	}

	return
}

func decodeBranchPosition(bs []byte) (ahead, behind int) {
	reA := regexp.MustCompile(`\[ahead ?(\d+).*\]`)
	reB := regexp.MustCompile(`\[.*behind ?(\d+)\]`)
{'fraction_non_alphanumeric': 0.15047344586249486, 'fraction_numerical': 0.0032935364347468094, 'mean_word_length': 3.78248031496063, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4594777', 'n_tokens_mistral': 1718, 'n_tokens_neox': 1622, 'n_words': 498}