aboutsummaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
authorjet2tlf <jet2tlf@gmail.com>2024-06-03 03:23:19 +0000
committerjet2tlf <jet2tlf@gmail.com>2024-06-03 03:23:19 +0000
commitb73cbe26f85fd9547e0d91c9e85b6f47bb57ea0d (patch)
tree7505a311ef20f396e776f1c0037c8712df4151d0 /cmd
parent81e395cdeb0894423d0ad833a074538674a4bc2d (diff)
downloadgit-go-b73cbe26f85fd9547e0d91c9e85b6f47bb57ea0d.tar.gz
git-go-b73cbe26f85fd9547e0d91c9e85b6f47bb57ea0d.zip
codecrafters submit [skip ci]
Diffstat (limited to 'cmd')
-rw-r--r--cmd/mygit/main.go370
1 files changed, 224 insertions, 146 deletions
diff --git a/cmd/mygit/main.go b/cmd/mygit/main.go
index 1443195..686392e 100644
--- a/cmd/mygit/main.go
+++ b/cmd/mygit/main.go
@@ -4,197 +4,275 @@ import (
"bytes"
"compress/zlib"
"crypto/sha1"
- "encoding/hex"
"fmt"
"io"
- "io/ioutil"
"os"
- "path/filepath"
- "sort"
- "strings"
+ "strconv"
+ "time"
)
-func main() {
- if len(os.Args) < 2 {
- fmt.Fprintf(os.Stderr, "usage: mygit <command> [<args>...]\n")
- os.Exit(1)
+func init_repo() {
+ for _, dir := range []string{".git", ".git/objects", ".git/refs"} {
+ if err := os.MkdirAll(dir, 0755); err != nil {
+ fmt.Fprintf(os.Stderr, "Error creating directory: %s\n", err)
+ }
}
- switch command := os.Args[1]; command {
- case "init":
- for _, dir := range []string{".git", ".git/objects", ".git/refs"} {
- if err := os.MkdirAll(dir, 0755); err != nil {
- fmt.Fprintf(os.Stderr, "Error creating directory: %s\n", err)
- }
+ headFileContents := []byte("ref: refs/heads/master\n")
+ if err := os.WriteFile(".git/HEAD", headFileContents, 0644); err != nil {
+ fmt.Fprintf(os.Stderr, "Error writing file: %s\n", err)
+ }
+
+ fmt.Println("Initialized git directory")
+}
+
+func read_obj(blob_sha string) []byte {
+ path := fmt.Sprintf(".git/objects/%s/%s", blob_sha[:2], blob_sha[2:])
+
+ file, err := os.Open(path)
+ if err != nil {
+ fmt.Printf("Failed to open blob file: %s\n", err)
+ }
+
+ reader, err := zlib.NewReader(file)
+ if err != nil {
+ fmt.Printf("Failed to instantiate zlib reader: %s\n", err)
+ }
+ defer func() {
+ if err := reader.Close(); err != nil {
+ fmt.Printf("Failed to close zlib reader: %s\n", err)
}
+ }()
+
+ var buffer bytes.Buffer
+
+ _, err = io.Copy(&buffer, reader)
+ if err != nil {
+ fmt.Printf("Failed to write to stdout: %s\n", err)
+ }
+
+ _, err = buffer.ReadBytes(byte(' '))
+ if err != nil {
+ fmt.Printf("Failed to read from buffer: %s\n", err)
+ }
+
+ size_byte, err := buffer.ReadBytes(byte(0))
+ if err != nil {
+ fmt.Printf("Failed to read from buffer: %s\n", err)
+ }
+
+ size, err := strconv.Atoi(string(size_byte[:len(size_byte)-1]))
+ if err != nil {
+ fmt.Printf("Failed to convert number of bytes into integer: %s\n", err)
+ }
+
+ buffer.Truncate(size)
+ return buffer.Bytes()
+}
- headFileContents := []byte("ref: refs/heads/main\n")
- if err := os.WriteFile(".git/HEAD", headFileContents, 0644); err != nil {
- fmt.Fprintf(os.Stderr, "Error writing file: %s\n", err)
+func create_obj(content []byte) []byte {
+ hash_writer := sha1.New()
+ var blob_content_buffer bytes.Buffer
+ zlib_writer := zlib.NewWriter(&blob_content_buffer)
+ writer := io.MultiWriter(hash_writer, zlib_writer)
+ writer.Write(content)
+
+ sha := hash_writer.Sum(nil)
+ sha_string := fmt.Sprintf("%x", sha)
+
+ zlib_writer.Close()
+
+ blob_dir := fmt.Sprintf(".git/objects/%s", sha_string[:2])
+
+ err := os.MkdirAll(blob_dir, 0755)
+ if err != nil {
+ fmt.Printf("Failed to create directory for object: %s\n", err)
+ }
+
+ blob_path := fmt.Sprintf("%s/%s", blob_dir, sha_string[2:])
+
+ err = os.WriteFile(blob_path, blob_content_buffer.Bytes(), 0644)
+ if err != nil {
+ fmt.Printf("Failed to write blob to file: %s\n", err)
+ }
+
+ return sha
+}
+
+func hash_file(path string) []byte {
+ f, err := os.ReadFile(path)
+ if err != nil {
+ fmt.Printf("Failed to read from given file: %s\n", err)
+ }
+
+ content := []byte(fmt.Sprintf("blob %d\x00", len(f)))
+ content = append(content, f...)
+ return create_obj(content)
+}
+
+func read_tree(hash string) {
+ file, err := os.Open(fmt.Sprintf(".git/objects/%s/%s", hash[:2], hash[2:]))
+ if err != nil {
+ fmt.Printf("Failed to open tree object file: %s\n", err)
+ }
+
+ reader, err := zlib.NewReader(file)
+
+ if err != nil {
+ fmt.Printf("Failed to instantiate zlib reader: %s\n", err)
+ }
+
+ defer func() {
+ if err := reader.Close(); err != nil {
+ fmt.Printf("Failed to close zlib reader: %s\n", err)
}
+ }()
- fmt.Println("Initialized git directory")
+ var buffer bytes.Buffer
- case "cat-file":
- object := os.Args[3]
- filePath := fmt.Sprintf(".git/objects/%s/%s", object[:2], object[2:])
- file, err := os.Open(filePath)
+ _, err = io.Copy(&buffer, reader)
+ if err != nil {
+ fmt.Printf("Failed to write to stdout: %s\n", err)
+ }
+
+ _, err = buffer.ReadBytes(byte(' '))
+ if err != nil {
+ fmt.Printf("Failed to read from buffer: %s\n", err)
+ }
+
+ size_byte, _ := buffer.ReadBytes(byte(0))
+
+ _, err = strconv.Atoi(string(size_byte[:len(size_byte)-1]))
+ if err != nil {
+ fmt.Printf("Failed to convert tree object size to integer: %s\n", err)
+ }
+
+ sha_buffer := make([]byte, 20)
+
+ for {
+ _, err = buffer.ReadBytes(byte(' '))
if err != nil {
- fmt.Fprintf(os.Stderr, "Error opening file: %s\n", err)
- os.Exit(1)
+ fmt.Printf("Failed to read from buffer first: %s\n", err)
}
- defer file.Close()
- r, err := zlib.NewReader(file)
+ name, err := buffer.ReadBytes(byte(0))
if err != nil {
- fmt.Fprintf(os.Stderr, "Error creating zlib reader: %s\n", err)
- os.Exit(1)
+ fmt.Printf("Failed to read from buffer second: %s\n", err)
}
- defer r.Close()
- w, err := io.ReadAll(r)
+ fmt.Println(string(name[:len(name)-1]))
+
+ _, err = io.ReadFull(&buffer, sha_buffer)
if err != nil {
- fmt.Fprintf(os.Stderr, "Error reading zlib data: %s\n", err)
- os.Exit(1)
+ fmt.Printf("Failed to read 20 bytes from buffer: %s\n", err)
}
- parts := bytes.Split(w, []byte("\x00"))
- if len(parts) < 2 {
- fmt.Fprintf(os.Stderr, "Invalid zlib data\n")
- os.Exit(1)
+ if buffer.Len() == 0 {
+ break
}
+ }
+}
- fmt.Print(string(parts[1]))
+func hash_tree(dir string) []byte {
+ entries, err := os.ReadDir(dir)
+ if err != nil {
+ fmt.Printf("Failed to list directory: %s\n", err)
+ }
- case "hash-object":
- object := os.Args[3]
- file, err := os.ReadFile(object)
- if err != nil {
- fmt.Fprintf(os.Stderr, "Error reading file: %s\n", err)
- os.Exit(1)
- }
- stats, err := os.Stat(object)
- if err != nil {
- fmt.Fprintf(os.Stderr, "Error getting file stats: %s\n", err)
- os.Exit(1)
- }
- content := string(file)
- contentAndHeader := fmt.Sprintf("blob %d\x00%s", stats.Size(), content)
- sha := sha1.Sum([]byte(contentAndHeader))
- hash := fmt.Sprintf("%x", sha)
- blobName := []rune(hash)
- blobPath := ".git/objects/"
-
- for i, v := range blobName {
- blobPath += string(v)
- if i == 1 {
- blobPath += "/"
- }
- }
+ var entries_buffer bytes.Buffer
- var buffer bytes.Buffer
- z := zlib.NewWriter(&buffer)
- z.Write([]byte(contentAndHeader))
- z.Close()
+ for _, entry := range entries {
+ name := entry.Name()
+ path := fmt.Sprintf("%s/%s", dir, name)
- if err := os.MkdirAll(filepath.Dir(blobPath), os.ModePerm); err != nil {
- fmt.Fprintf(os.Stderr, "Error creating directory: %s\n", err)
- os.Exit(1)
+ if name == ".git" {
+ continue
}
- f, err := os.Create(blobPath)
- if err != nil {
- fmt.Fprintf(os.Stderr, "Error creating file: %s\n", err)
- os.Exit(1)
- }
- defer f.Close()
+ var sha []byte
+ var mode string
- if _, err := f.Write(buffer.Bytes()); err != nil {
- fmt.Fprintf(os.Stderr, "Error writing to file: %s\n", err)
- os.Exit(1)
+ if entry.IsDir() {
+ mode = "40000"
+ sha = hash_tree(path)
+ } else {
+ mode = "100644"
+ sha = hash_file(path)
}
- fmt.Print(hash)
-
- case "ls-tree":
- fileNames := []string{}
- files, err := os.ReadDir(".")
+ _, err = entries_buffer.Write([]byte(fmt.Sprintf("%s %s\x00", mode, name)))
if err != nil {
- fmt.Fprintf(os.Stderr, "Unknown error %s\n", err)
- os.Exit(1)
+ fmt.Printf("Failed to write to byte buffer: %s\n", err)
}
- for _, file := range files {
- if file.Name() != ".git" {
- fileNames = append(fileNames, file.Name())
- }
+ _, err = entries_buffer.Write(sha)
+ if err != nil {
+ fmt.Printf("Failed to write to byte buffer: %s\n", err)
}
+ }
- sort.Strings(fileNames)
- fmt.Println(strings.Join(fileNames, "\n"))
+ content := []byte(fmt.Sprintf("tree %d\x00", entries_buffer.Len()))
+ content = append(content, entries_buffer.Bytes()...)
+ return create_obj(content)
+}
- case "write-tree":
- currentDir, _ := os.Getwd()
- h, c := calcTreeHash(currentDir)
- treeHash := hex.EncodeToString(h)
- os.Mkdir(filepath.Join(".git", "objects", treeHash[:2]), 0755)
- var compressed bytes.Buffer
- w := zlib.NewWriter(&compressed)
- w.Write(c)
- w.Close()
- os.WriteFile(filepath.Join(".git", "objects", treeHash[:2], treeHash[2:]), compressed.Bytes(), 0644)
- fmt.Println(treeHash)
+func commit_tree(tree_sha, parent_sha, message string) []byte {
+ author := "Lucas Faria"
+ authorEmail := "jet2tlf@gmail.com"
+ currentUnixTime := time.Now().Unix()
+ timezone, _ := time.Now().Local().Zone()
- default:
- fmt.Fprintf(os.Stderr, "Unknown command %s\n", command)
- os.Exit(1)
- }
-}
+ commit_content := []byte(fmt.Sprintf(
+ "tree %s\nparent %s\nauthor %s <%s> %s %s\ncommitter %s <%s> %s %s\n\n%s\n",
+ tree_sha,
+ parent_sha,
+ author,
+ authorEmail,
+ fmt.Sprint(currentUnixTime),
+ timezone,
+ author,
+ authorEmail,
+ fmt.Sprint(currentUnixTime),
+ timezone,
+ message),
+ )
-func calcTreeHash(dir string) ([]byte, []byte) {
- fileInfos, _ := ioutil.ReadDir(dir)
+ content := []byte(fmt.Sprintf("commit %d\x00", len(commit_content)))
+ content = append(content, commit_content...)
+ return create_obj(content)
+}
- type entry struct {
- fileName string
- b []byte
+func main() {
+ if len(os.Args) < 2 {
+ fmt.Fprintf(os.Stderr, "usage: mygit <command> [<args>...]\n")
+ os.Exit(1)
}
- var entries []entry
- contentSize := 0
+ switch command := os.Args[1]; command {
+ case "init":
+ init_repo()
- for _, fileInfo := range fileInfos {
- if fileInfo.Name() == ".git" {
- continue
- }
+ case "cat-file":
+ content := read_obj(os.Args[3])
+ fmt.Print(string(content))
- if !fileInfo.IsDir() {
- f, _ := os.Open(filepath.Join(dir, fileInfo.Name()))
- b, _ := ioutil.ReadAll(f)
- s := fmt.Sprintf("blob %d\u0000%s", len(b), string(b))
- sha1 := sha1.New()
- io.WriteString(sha1, s)
- s = fmt.Sprintf("100644 %s\u0000", fileInfo.Name())
- b = append([]byte(s), sha1.Sum(nil)...)
- entries = append(entries, entry{fileInfo.Name(), b})
- contentSize += len(b)
- } else {
- b, _ := calcTreeHash(filepath.Join(dir, fileInfo.Name()))
- s := fmt.Sprintf("40000 %s\u0000", fileInfo.Name())
- b2 := append([]byte(s), b...)
- entries = append(entries, entry{fileInfo.Name(), b2})
- contentSize += len(b2)
- }
- }
- sort.Slice(entries, func(i, j int) bool { return entries[i].fileName < entries[j].fileName })
- s := fmt.Sprintf("tree %d\u0000", contentSize)
- b := []byte(s)
+ case "hash-object":
+ sha := hash_file(os.Args[3])
+ fmt.Printf("%x\n", sha)
- for _, entry := range entries {
- b = append(b, entry.b...)
- }
+ case "ls-tree":
+ read_tree(os.Args[3])
- sha1 := sha1.New()
- io.WriteString(sha1, string(b))
- return sha1.Sum(nil), b
+ case "write-tree":
+ sha := hash_tree(".")
+ fmt.Printf("%x\n", sha)
+
+ case "commit-tree":
+ sha := commit_tree(os.Args[2], os.Args[4], os.Args[6])
+ fmt.Printf("%x\n", sha)
+ default:
+ fmt.Fprintf(os.Stderr, "Unknown command %s\n", command)
+ os.Exit(1)
+ }
}