package main import ( "encoding/json" "fmt" "log" "time" "io/ioutil" "path/filepath" ) type Empty struct{} var empty Empty type RepoCommits struct { Config configCollect CommitDesc map[string]string CommitContent map[string]*CommitNode Root map[*CommitNode]Empty Leafs map[*CommitNode]Empty Head *CommitNode } type CommitNode struct { Parents map[*CommitNode]Empty Children map[*CommitNode]Empty Content Commit } type Commit struct { CommitId *string `json:"commit_id"` RootId *string `json:"root_id"` RepoId *string `json:"repo_id"` CreatorName *string `json:"creator_name"` Creator *string `json:"creator"` Description *string `json:"description"` Ctime int64 `json:"ctime"` ParentId *string `json:"parent_id"` SecondParentId *string `json:"second_parent_id"` RepoName *string `json:"repo_name"` RepoDesc *string `json:"repo_desc"` RepoCategory *string `json:"repo_category"` NoLocalHistory int `json:"no_local_history"` Version int `json:"version"` } func (c* Commit) String() string { return fmt.Sprintf(`RootId: %s CreatorName: %s Creator: %s Description: %s Ctime: %s RepoName: %s RepoDesc: %s `, *c.RootId, *c.CreatorName, *c.Creator, *c.Description, time.Unix(c.Ctime, 0), *c.RepoName, *c.RepoDesc) } func NewRepoCommits (config configCollect) *RepoCommits { rc := new(RepoCommits) rc.Config = config rc.CommitDesc = make(map[string]string) rc.CommitContent = make(map[string]*CommitNode) rc.Root = make(map[*CommitNode]Empty) rc.Leafs = make(map[*CommitNode]Empty) return rc } func (rc* RepoCommits) CollectDescs() { baseFolder := filepath.Join(rc.Config.Storage, "commits", rc.Config.RepoId) layer1, err := ioutil.ReadDir(baseFolder) if err != nil { log.Fatal(err) } for _, l1 := range layer1 { intFolder := filepath.Join(baseFolder, l1.Name()) layer2, err := ioutil.ReadDir(intFolder) if err != nil { log.Fatal(err) } for _, l2 := range layer2 { commitId := l1.Name() + l2.Name() commitPath := filepath.Join(intFolder, l2.Name()) rc.CommitDesc[commitId] = commitPath } } } func NewCommitNode(c Commit) *CommitNode { cn := new(CommitNode) cn.Content = c cn.Parents = make(map[*CommitNode]Empty) cn.Children = make(map[*CommitNode]Empty) return cn } func (rc* RepoCommits) CollectContent() { for id, path := range rc.CommitDesc { data, err := ioutil.ReadFile(path) if err != nil { log.Fatal(err) } var c Commit; json.Unmarshal(data, &c) rc.CommitContent[id] = NewCommitNode(c) } } func (rc* RepoCommits) BuildGraph() map[*CommitNode]Empty { for _, cn := range rc.CommitContent { if cn.Content.ParentId == nil && cn.Content.SecondParentId == nil { rc.Root[cn] = empty } if cn.Content.ParentId != nil { parentCn := rc.CommitContent[*cn.Content.ParentId] cn.Parents[parentCn] = empty parentCn.Children[cn] = empty } if cn.Content.SecondParentId != nil { parentCn := rc.CommitContent[*cn.Content.SecondParentId] cn.Parents[parentCn] = empty parentCn.Children[cn] = empty } } if len(rc.Root) == 0 { log.Fatal("Root commit has not been found") } return rc.Root } func (rc* RepoCommits) FindLeafs() map[*CommitNode]Empty { toProcess := make(map[*CommitNode]Empty) for cn, _ := range rc.Root { toProcess[cn] = empty } for i := 0; i < len(rc.CommitContent) && len(toProcess) > 0; i++ { nextToProcess := make(map[*CommitNode]Empty) for cn, _ := range toProcess { for ccn, _ := range cn.Children { nextToProcess[ccn] = empty } if len(cn.Children) == 0 { rc.Leafs[cn] = empty } } toProcess = nextToProcess } if len(rc.Leafs) == 0 { log.Fatal("No leafs have been found") } return rc.Leafs } func (rc* RepoCommits) ChooseHead() *CommitNode { for cn, _ := range rc.Leafs { if rc.Head == nil { rc.Head = cn } else if rc.Head.Content.Ctime < cn.Content.Ctime { rc.Head = cn } } if rc.Head == nil { log.Fatal("No HEAD has been found") } return rc.Head }