Skip to content

Commit

Permalink
Merge pull request #121 from JunNishimura/#120
Browse files Browse the repository at this point in the history
add reflog command
  • Loading branch information
JunNishimura committed Jun 17, 2023
2 parents 2255ed8 + d282f63 commit 499ff63
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 6 deletions.
38 changes: 38 additions & 0 deletions cmd/reflog.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/
package cmd

import (
"fmt"

"github.com/JunNishimura/Goit/internal/store"
"github.com/spf13/cobra"
)

// reflogCmd represents the reflog command
var reflogCmd = &cobra.Command{
Use: "reflog",
Short: "manage reference logs",
Long: "manage reference logs",
PreRunE: func(cmd *cobra.Command, args []string) error {
if client.RootGoitPath == "" {
return ErrGoitNotInitialized
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
reflog, err := store.NewReflog(client.RootGoitPath, client.Head, client.Refs)
if err != nil {
return fmt.Errorf("fail to get reflog: %w", err)
}

reflog.Show()

return nil
},
}

func init() {
rootCmd.AddCommand(reflogCmd)
}
24 changes: 19 additions & 5 deletions internal/log/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,29 @@ import (
"github.com/JunNishimura/Goit/internal/sha"
)

type recordType int
type RecordType int

const (
CommitRecord recordType = iota
UndefinedRecord RecordType = iota
CommitRecord
CheckoutRecord
BranchRecord
)

func (t recordType) String() string {
func NewRecordType(typeString string) RecordType {
switch typeString {
case "commit":
return CommitRecord
case "checkout":
return CheckoutRecord
case "branch":
return BranchRecord
default:
return UndefinedRecord
}
}

func (t RecordType) String() string {
switch t {
case CommitRecord:
return "commit"
Expand All @@ -32,7 +46,7 @@ func (t recordType) String() string {
}

type record struct {
recType recordType
recType RecordType
from sha.SHA1
to sha.SHA1
name string
Expand All @@ -42,7 +56,7 @@ type record struct {
message string
}

func NewRecord(recType recordType, from, to sha.SHA1, name, email string, t time.Time, message string) *record {
func NewRecord(recType RecordType, from, to sha.SHA1, name, email string, t time.Time, message string) *record {
unixtime := fmt.Sprint(t.Unix())
_, offset := t.Zone()
offsetMinutes := offset / 60
Expand Down
2 changes: 1 addition & 1 deletion internal/log/logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (

func TestNewRecord(t *testing.T) {
type args struct {
recType recordType
recType RecordType
from sha.SHA1
to sha.SHA1
name string
Expand Down
123 changes: 123 additions & 0 deletions internal/store/reflog.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package store

import (
"bufio"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/JunNishimura/Goit/internal/log"
"github.com/JunNishimura/Goit/internal/sha"
"github.com/fatih/color"
)

type logRecord struct {
hash sha.SHA1
isHead bool
references []string
recType log.RecordType
message string
}

type Reflog struct {
records []*logRecord
}

func NewReflog(rootGoitPath string, head *Head, refs *Refs) (*Reflog, error) {
reflog := newReflog()
if err := reflog.load(rootGoitPath, head, refs); err != nil {
return nil, err
}

return reflog, nil
}

func newReflog() *Reflog {
return &Reflog{
records: make([]*logRecord, 0),
}
}

func (r *Reflog) load(rootGoitPath string, head *Head, refs *Refs) error {
headPath := filepath.Join(rootGoitPath, "logs", "HEAD")
f, err := os.Open(headPath)
if err != nil {
return fmt.Errorf("fail to open %s: %w", headPath, err)
}
defer f.Close()

scanner := bufio.NewScanner(f)
for scanner.Scan() {
record := &logRecord{
references: make([]string, 0),
}

text := scanner.Text()

// extract hash
sp1 := strings.SplitN(text, " ", 3)
if len(sp1) != 3 {
continue
}
if sp1[1] == strings.Repeat("0", 40) {
record.hash = nil
} else {
hash, err := sha.ReadHash(sp1[1])
if err != nil {
return fmt.Errorf("fail to read hash %s: %w", sp1[1], err)
}
record.hash = hash

// references
if head.Commit.Hash.String() == sp1[1] {
record.isHead = true
}
branches := refs.getBranchesByHash(hash)
for _, branch := range branches {
record.references = append(record.references, color.GreenString(branch.Name))
}
}

// extract recType
sp2 := strings.Split(sp1[2], "\t")
if len(sp2) != 2 {
continue
}
sp3 := strings.Split(sp2[1], ": ")
if len(sp3) != 2 {
continue
}
recType := log.NewRecordType(sp3[0])
if recType == log.UndefinedRecord {
continue
}
record.recType = recType
record.message = sp3[1]

r.records = append(r.records, record)
}

return nil
}

func (r *Reflog) Show() {
for i := range r.records {
idx := len(r.records) - i - 1
record := r.records[idx]

var referenceString string
if len(record.references) > 0 {
referenceString = strings.Join(record.references, ", ")
}
if record.isHead {
referenceString = color.BlueString("HEAD -> ") + referenceString
}

if referenceString == "" {
fmt.Printf("%s HEAD:{%d}: %s: %s\n", color.YellowString(record.hash.String()[:7]), i, record.recType, record.message)
} else {
fmt.Printf("%s (%s) HEAD:{%d}: %s: %s\n", color.YellowString(record.hash.String()[:7]), referenceString, i, record.recType, record.message)
}
}
}
72 changes: 72 additions & 0 deletions internal/store/reflog_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package store

import (
"os"
"path/filepath"
"reflect"
"testing"
)

func TestReflogLoad(t *testing.T) {
type args struct {
head *Head
refs *Refs
}
type test struct {
name string
args args
fields string
want *Reflog
wantErr bool
}
tests := []*test{
func() *test {
return &test{
name: "success",
args: args{
head: &Head{},
refs: &Refs{},
},
fields: "",
want: &Reflog{
records: make([]*logRecord, 0),
},
wantErr: false,
}
}(),
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tmpDir := t.TempDir()

// .goit initialization
goitDir := filepath.Join(tmpDir, ".goit")
if err := os.Mkdir(goitDir, os.ModePerm); err != nil {
t.Logf("%v: %s", err, goitDir)
}
// make .goit/logs directory
logsDir := filepath.Join(goitDir, "logs")
if err := os.Mkdir(logsDir, os.ModePerm); err != nil {
t.Logf("%v: %s", err, logsDir)
}
// make HEAD file
headPath := filepath.Join(logsDir, "HEAD")
f, err := os.Create(headPath)
if err != nil {
t.Logf("%v: %s", err, headPath)
}
if _, err := f.WriteString(tt.fields); err != nil {
t.Log(err)
}
f.Close()

reflog := newReflog()
if err := reflog.load(goitDir, tt.args.head, tt.args.refs); (err != nil) != tt.wantErr {
t.Errorf("got = %v, want = %v", err, tt.wantErr)
}
if !reflect.DeepEqual(reflog, tt.want) {
t.Errorf("got = %v, want = %v", reflog, tt.want)
}
})
}
}
11 changes: 11 additions & 0 deletions internal/store/refs.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,17 @@ func (r *Refs) getBranchPos(branchName string) int {
return NewBranchFlag
}

func (r *Refs) getBranchesByHash(hash sha.SHA1) []*branch {
var branches []*branch
for _, branch := range r.Heads {
if branch.hash.String() == hash.String() {
branches = append(branches, branch)
}
}

return branches
}

func (r *Refs) DeleteBranch(rootGoitPath, headBranchName, deleteBranchName string) error {
// branch validation
if deleteBranchName == headBranchName {
Expand Down

0 comments on commit 499ff63

Please sign in to comment.