1// Package repository contains helper methods for working with a Git repo.
2package repository
3
4import (
5 "bytes"
6 "errors"
7 "strings"
8
9 "github.com/MichaelMure/git-bug/util/lamport"
10)
11
12var (
13 ErrNoConfigEntry = errors.New("no config entry for the given key")
14 ErrMultipleConfigEntry = errors.New("multiple config entry for the given key")
15 // ErrNotARepo is the error returned when the git repo root wan't be found
16 ErrNotARepo = errors.New("not a git repository")
17 // ErrClockNotExist is the error returned when a clock can't be found
18 ErrClockNotExist = errors.New("clock doesn't exist")
19)
20
21// RepoConfig access the configuration of a repository
22type RepoConfig interface {
23 // LocalConfig give access to the repository scoped configuration
24 LocalConfig() Config
25
26 // GlobalConfig give access to the git global configuration
27 GlobalConfig() Config
28}
29
30// RepoCommon represent the common function the we want all the repo to implement
31type RepoCommon interface {
32 // GetPath returns the path to the repo.
33 GetPath() string
34
35 // GetUserName returns the name the the user has used to configure git
36 GetUserName() (string, error)
37
38 // GetUserEmail returns the email address that the user has used to configure git.
39 GetUserEmail() (string, error)
40
41 // GetCoreEditor returns the name of the editor that the user has used to configure git.
42 GetCoreEditor() (string, error)
43
44 // GetRemotes returns the configured remotes repositories.
45 GetRemotes() (map[string]string, error)
46}
47
48// Repo represents a source code repository.
49type Repo interface {
50 RepoConfig
51 RepoCommon
52
53 // FetchRefs fetch git refs from a remote
54 FetchRefs(remote string, refSpec string) (string, error)
55
56 // PushRefs push git refs to a remote
57 PushRefs(remote string, refSpec string) (string, error)
58
59 // StoreData will store arbitrary data and return the corresponding hash
60 StoreData(data []byte) (Hash, error)
61
62 // ReadData will attempt to read arbitrary data from the given hash
63 ReadData(hash Hash) ([]byte, error)
64
65 // StoreTree will store a mapping key-->Hash as a Git tree
66 StoreTree(mapping []TreeEntry) (Hash, error)
67
68 // ReadTree will return the list of entries in a Git tree
69 ReadTree(hash Hash) ([]TreeEntry, error)
70
71 // StoreCommit will store a Git commit with the given Git tree
72 StoreCommit(treeHash Hash) (Hash, error)
73
74 // StoreCommit will store a Git commit with the given Git tree
75 StoreCommitWithParent(treeHash Hash, parent Hash) (Hash, error)
76
77 // GetTreeHash return the git tree hash referenced in a commit
78 GetTreeHash(commit Hash) (Hash, error)
79
80 // FindCommonAncestor will return the last common ancestor of two chain of commit
81 FindCommonAncestor(commit1 Hash, commit2 Hash) (Hash, error)
82
83 // UpdateRef will create or update a Git reference
84 UpdateRef(ref string, hash Hash) error
85
86 // ListRefs will return a list of Git ref matching the given refspec
87 ListRefs(refspec string) ([]string, error)
88
89 // RefExist will check if a reference exist in Git
90 RefExist(ref string) (bool, error)
91
92 // CopyRef will create a new reference with the same value as another one
93 CopyRef(source string, dest string) error
94
95 // ListCommits will return the list of tree hashes of a ref, in chronological order
96 ListCommits(ref string) ([]Hash, error)
97}
98
99// ClockedRepo is a Repo that also has Lamport clocks
100type ClockedRepo interface {
101 Repo
102
103 // GetOrCreateClock return a Lamport clock stored in the Repo.
104 // If the clock doesn't exist, it's created.
105 GetOrCreateClock(name string) (lamport.Clock, error)
106}
107
108// ClockLoader hold which logical clock need to exist for an entity and
109// how to create them if they don't.
110type ClockLoader struct {
111 // Clocks hold the name of all the clocks this loader deal with.
112 // Those clocks will be checked when the repo load. If not present or broken,
113 // Witnesser will be used to create them.
114 Clocks []string
115 // Witnesser is a function that will initialize the clocks of a repo
116 // from scratch
117 Witnesser func(repo ClockedRepo) error
118}
119
120func prepareTreeEntries(entries []TreeEntry) bytes.Buffer {
121 var buffer bytes.Buffer
122
123 for _, entry := range entries {
124 buffer.WriteString(entry.Format())
125 }
126
127 return buffer
128}
129
130func readTreeEntries(s string) ([]TreeEntry, error) {
131 split := strings.Split(strings.TrimSpace(s), "\n")
132
133 casted := make([]TreeEntry, len(split))
134 for i, line := range split {
135 if line == "" {
136 continue
137 }
138
139 entry, err := ParseTreeEntry(line)
140
141 if err != nil {
142 return nil, err
143 }
144
145 casted[i] = entry
146 }
147
148 return casted, nil
149}
150
151// TestedRepo is an extended ClockedRepo with function for testing only
152type TestedRepo interface {
153 ClockedRepo
154
155 // AddRemote add a new remote to the repository
156 AddRemote(name string, url string) error
157}