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 // RemoveRef will remove a Git reference
87 RemoveRef(ref string) error
88
89 // ListRefs will return a list of Git ref matching the given refspec
90 ListRefs(refspec string) ([]string, error)
91
92 // RefExist will check if a reference exist in Git
93 RefExist(ref string) (bool, error)
94
95 // CopyRef will create a new reference with the same value as another one
96 CopyRef(source string, dest string) error
97
98 // ListCommits will return the list of tree hashes of a ref, in chronological order
99 ListCommits(ref string) ([]Hash, error)
100}
101
102// ClockedRepo is a Repo that also has Lamport clocks
103type ClockedRepo interface {
104 Repo
105
106 // GetOrCreateClock return a Lamport clock stored in the Repo.
107 // If the clock doesn't exist, it's created.
108 GetOrCreateClock(name string) (lamport.Clock, error)
109}
110
111// ClockLoader hold which logical clock need to exist for an entity and
112// how to create them if they don't.
113type ClockLoader struct {
114 // Clocks hold the name of all the clocks this loader deal with.
115 // Those clocks will be checked when the repo load. If not present or broken,
116 // Witnesser will be used to create them.
117 Clocks []string
118 // Witnesser is a function that will initialize the clocks of a repo
119 // from scratch
120 Witnesser func(repo ClockedRepo) error
121}
122
123func prepareTreeEntries(entries []TreeEntry) bytes.Buffer {
124 var buffer bytes.Buffer
125
126 for _, entry := range entries {
127 buffer.WriteString(entry.Format())
128 }
129
130 return buffer
131}
132
133func readTreeEntries(s string) ([]TreeEntry, error) {
134 split := strings.Split(strings.TrimSpace(s), "\n")
135
136 casted := make([]TreeEntry, len(split))
137 for i, line := range split {
138 if line == "" {
139 continue
140 }
141
142 entry, err := ParseTreeEntry(line)
143
144 if err != nil {
145 return nil, err
146 }
147
148 casted[i] = entry
149 }
150
151 return casted, nil
152}
153
154// TestedRepo is an extended ClockedRepo with function for testing only
155type TestedRepo interface {
156 ClockedRepo
157
158 // AddRemote add a new remote to the repository
159 AddRemote(name string, url string) error
160}