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