<commit_before>package gitgo

import (
	"bufio"
	"bytes"
	"compress/zlib"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"strings"
)

// GitObject represents a commit, tree, or blob.
// Under the hood, these may be objects stored directly
// or through packfiles
type GitObject interface {
	Type() string
	//Contents() string
}

type gitObject struct {
	Type string

	// Commit fields
	Tree      string
	Parents   []string
	Author    string
	Committer string
	Message   []byte
	Size      string

	// Tree
	Blobs []objectMeta
	Trees []objectMeta

	// Blob
	Contents []byte
}

// A Blob compresses content from a file
type Blob struct {
	_type    string
	size     string
	Contents []byte
}

func (b Blob) Type() string {
	return b._type
}

type Commit struct {
	_type     string
	Name      SHA
	Tree      string
	Parents   []SHA
	Author    string
	Committer string
	Message   []byte
	size      string
}

func (c Commit) Type() string {
	return c._type
}

type Tree struct {
	_type string
	Blobs []objectMeta
	Trees []objectMeta
	size  string
}

func (t Tree) Type() string {
	return t._type
}

// objectMeta contains the metadata
// (hash, permissions, and filename)
// corresponding either to a blob (leaf) or another tree
type objectMeta struct {
	Hash     SHA
	Perms    string
	filename string
}

func NewObject(input SHA, basedir string) (obj GitObject, err error) {
	if basedir == "" {
		basedir = ".git"
	}

	if path.Base(basedir) != ".git" {
		var candidate string
		candidate = path.Join(basedir, ".git")
		for {
			_, err := os.Stat(candidate)
			if err == nil {
				basedir = candidate
				break
			}
			if !os.IsNotExist(err) {
				return nil, err
			}

			// This should not be the main condition of the for loop
			// just in case the filesystem root directory contains
			// a .git subdirectory
			// TODO check for mountpoint
			if candidate == "/" {
				return nil, fmt.Errorf("not a git repository (or any parent up to root /")
			}
			candidate = path.Clean(path.Join(candidate, "..", "..", ".git"))
		}
	}

	if len(input) < 4 {
		return nil, fmt.Errorf("input SHA must be at least 4 characters")
	}

	filename := path.Join(basedir, "objects", string(input[:2]), string(input[2:]))
	_, err = os.Stat(filename)
	if err != nil {
		if !os.IsNotExist(err) {
			return nil, err
		}

		// check the directory for a file with the SHA as a prefix
		_, err = os.Stat(path.Join(basedir, "objects", string(input[:2])))
		if err != nil {
			if !os.IsNotExist(err) {
				return nil, err
			}<commit_msg>Fix error with subdirectory search<commit_after>package gitgo

import (
	"bufio"
	"bytes"
	"compress/zlib"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"strings"
)

// GitObject represents a commit, tree, or blob.
// Under the hood, these may be objects stored directly
// or through packfiles
type GitObject interface {
	Type() string
	//Contents() string
}

type gitObject struct {
	Type string

	// Commit fields
	Tree      string
	Parents   []string
	Author    string
	Committer string
	Message   []byte
	Size      string

	// Tree
	Blobs []objectMeta
	Trees []objectMeta

	// Blob
	Contents []byte
}

// A Blob compresses content from a file
type Blob struct {
	_type    string
	size     string
	Contents []byte
}

func (b Blob) Type() string {
	return b._type
}

type Commit struct {
	_type     string
	Name      SHA
	Tree      string
	Parents   []SHA
	Author    string
	Committer string
	Message   []byte
	size      string
}

func (c Commit) Type() string {
	return c._type
}

type Tree struct {
	_type string
	Blobs []objectMeta
	Trees []objectMeta
	size  string
}

func (t Tree) Type() string {
	return t._type
}

// objectMeta contains the metadata
// (hash, permissions, and filename)
// corresponding either to a blob (leaf) or another tree
type objectMeta struct {
	Hash     SHA
	Perms    string
	filename string
}

func NewObject(input SHA, basedir string) (obj GitObject, err error) {
	if path.Base(basedir) != ".git" {
		basedir = path.Join(basedir, ".git")
	}

	candidate := basedir
	for {
		_, err := os.Stat(candidate)
		if err == nil {
			basedir = candidate
			break
		}
		if !os.IsNotExist(err) {
			panic(err)
			return nil, err
		}

		// This should not be the main condition of the for loop
		// just in case the filesystem root directory contains
		// a .git subdirectory
		// TODO check for mountpoint
		if candidate == "/" {
			return nil, fmt.Errorf("not a git repository (or any parent up to root /")
		}
		candidate = path.Join(candidate, "..", "..", ".git")
	}

	if len(input) < 4 {
		return nil, fmt.Errorf("input SHA must be at least 4 characters")
	}

	filename := path.Join(basedir, "objects", string(input[:2]), string(input[2:]))
	_, err = os.Stat(filename)
	if err != nil {
		if !os.IsNotExist(err) {
			return nil, err
		}

		// check the directory for a file with the SHA as a prefix
		_, err = os.Stat(path.Join(basedir, "objects", string(input[:2])))
		if err != nil {
			if !os.IsNotExist(err) {
				return nil, err
			}
{'fraction_non_alphanumeric': 0.11518429487179487, 'fraction_numerical': 0.002003205128205128, 'mean_word_length': 2.9099451840250588, 'pattern_counts': {'":': 0, '<': 5, '<?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': '4275720', 'n_tokens_mistral': 1803, 'n_tokens_neox': 1681, 'n_words': 580}