github.com/shved/got@v0.0.0-20230322140632-a4bfa1e99685/got/got.go (about)

     1  // Package got holds all the global repo vars and functions.
     2  package got
     3  
     4  import (
     5  	"errors"
     6  	"io/ioutil"
     7  	"log"
     8  	"os"
     9  	"path"
    10  	"path/filepath"
    11  	"sort"
    12  	"strings"
    13  )
    14  
    15  var (
    16  	ErrRepoAlreadyInited = errors.New("repo already initialized")
    17  	ErrInvalidObjType    = errors.New("invalid object type")
    18  	ErrNotGotRepo        = errors.New("not a got repo")
    19  	ErrWrongRootType     = errors.New("only commit could be an object graph root")
    20  	ErrWrongLogEntryType = errors.New("only commit could be saved in repo logs")
    21  	ErrObjDoesNotExist   = errors.New("object does not exist")
    22  )
    23  
    24  var DefaultIgnoreEntries = []string{
    25  	".gitignore",
    26  	".gitkeep",
    27  	".git",
    28  	".got",
    29  	".DS_Store",
    30  }
    31  
    32  var AbsRepoRoot string
    33  
    34  var EmptyCommitRef = []byte("0000000000000000000000000000000000000000")
    35  
    36  var (
    37  	gotPath     string = ".got"
    38  	objectsPath string = path.Join(gotPath, "objects")
    39  	headPath    string = path.Join(gotPath, "HEAD")
    40  	logPath     string = path.Join(gotPath, "LOG")
    41  
    42  	CommitPath string = path.Join(objectsPath, "commit")
    43  	TreePath   string = path.Join(objectsPath, "tree")
    44  	BlobPath   string = path.Join(objectsPath, "blob")
    45  
    46  	logsHeader string = "Time\t\t\tCommit hash\t\t\t\t\tParent hash\t\t\t\t\tCommit message\n"
    47  )
    48  
    49  // InitRepo initializes a repo in a current working directory by creating a .got dir with all the needing content.
    50  func InitRepo() {
    51  	if _, err := os.Stat(gotPath); !os.IsNotExist(err) {
    52  		log.Fatal(ErrRepoAlreadyInited)
    53  	}
    54  
    55  	err := os.Mkdir(gotPath, 0755)
    56  	err = os.Mkdir(objectsPath, 0755)
    57  	err = os.Mkdir(CommitPath, 0755)
    58  	err = os.Mkdir(TreePath, 0755)
    59  	err = os.Mkdir(BlobPath, 0755)
    60  	err = ioutil.WriteFile(headPath, EmptyCommitRef, 0644)
    61  	_, err = os.Create(logPath)
    62  
    63  	if err != nil {
    64  		log.Fatal(err)
    65  	}
    66  }
    67  
    68  // SetRepoRoot finds closer .got dir in parent paths and sets its absolute path into an exported variable.
    69  func SetRepoRoot() {
    70  	AbsRepoRoot = getRepoRoot()
    71  }
    72  
    73  // CommitDirAbsPath returns absolute path holding commit objects.
    74  func CommitDirAbsPath() string {
    75  	return path.Join(AbsRepoRoot, CommitPath)
    76  }
    77  
    78  // TreeDirAbsPath returns absolute path holding tree objects.
    79  func TreeDirAbsPath() string {
    80  	return path.Join(AbsRepoRoot, TreePath)
    81  }
    82  
    83  // BlobDirAbsPath returns absolute path holding blob objects.
    84  func BlobDirAbsPath() string {
    85  	return path.Join(AbsRepoRoot, BlobPath)
    86  }
    87  
    88  // HeadsAbsPath returns absolute HEAD file path.
    89  func HeadAbsPath() string {
    90  	return path.Join(AbsRepoRoot, headPath)
    91  }
    92  
    93  // HeadsAbsPath returns absolute LOG file path.
    94  func LogAbsPath() string {
    95  	return path.Join(AbsRepoRoot, logPath)
    96  }
    97  
    98  // ReadLog reads all LOG file contents, reverses it for the right historical order, ads headers
    99  // and returns the result as a string.
   100  func ReadLog() string {
   101  	contents, err := ioutil.ReadFile(LogAbsPath())
   102  	if err != nil {
   103  		log.Fatal(err)
   104  	}
   105  	withHeaders := string(contents) + logsHeader
   106  	entries := strings.Split(withHeaders, "\n")
   107  	sort.Sort(sort.Reverse(sort.StringSlice(entries)))
   108  	logs := strings.Join(entries, "\n")
   109  	return logs
   110  }
   111  
   112  // UpdateLog adds a log entry into a LOG file.
   113  func UpdateLog(entry string) {
   114  	f, err := os.OpenFile(LogAbsPath(), os.O_APPEND|os.O_WRONLY, 0644)
   115  	defer f.Close()
   116  	_, err = f.WriteString(entry)
   117  
   118  	if err != nil {
   119  		log.Fatal(err)
   120  	}
   121  }
   122  
   123  // UpdateHead refresh last commit hash string in a HEAD file.
   124  func UpdateHead(sha string) {
   125  	if err := ioutil.WriteFile(HeadAbsPath(), []byte(sha), 0644); err != nil {
   126  		log.Fatal(err)
   127  	}
   128  }
   129  
   130  // ReadHead reads commit hash string from a HEAD file.
   131  func ReadHead() string {
   132  	commitSha, err := ioutil.ReadFile(HeadAbsPath())
   133  	if err != nil {
   134  		log.Fatal(err)
   135  	}
   136  	return string(commitSha)
   137  }
   138  
   139  func getRepoRoot() string {
   140  	relPath := getRootRelPath()
   141  	absPath, err := filepath.Abs(relPath)
   142  	if err != nil {
   143  		log.Fatal(err)
   144  	}
   145  	return absPath
   146  }
   147  
   148  func getRootRelPath() string {
   149  	path := "."
   150  	if isRepoRoot(path) {
   151  		return path
   152  	}
   153  
   154  	path = ".."
   155  
   156  	for {
   157  		if abs, _ := filepath.Abs(path); abs == string(filepath.Separator) {
   158  			log.Fatal(ErrNotGotRepo)
   159  		}
   160  		if isRepoRoot(path) {
   161  			return path
   162  		}
   163  		path = path + string(filepath.Separator) + ".."
   164  	}
   165  
   166  	log.Fatal(ErrNotGotRepo)
   167  	panic("never reach")
   168  }
   169  
   170  func isRepoRoot(path string) bool {
   171  	files, err := ioutil.ReadDir(path)
   172  	if err != nil {
   173  		log.Fatal(err)
   174  	}
   175  
   176  	for _, file := range files {
   177  		if file.Name() == gotPath {
   178  			return true
   179  		}
   180  	}
   181  
   182  	return false
   183  }