aboutsummaryrefslogtreecommitdiffstats
path: root/repository/repo.go
blob: 80bb7ce7e5fc65ad35036d2e9de10bddc13998ac (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
// Package repository contains helper methods for working with a Git repo.
package repository

import (
	"errors"
	"io"

	"github.com/blevesearch/bleve"
	"github.com/go-git/go-billy/v5"
	"golang.org/x/crypto/openpgp"

	"github.com/MichaelMure/git-bug/util/lamport"
)

var (
	// ErrNotARepo is the error returned when the git repo root can't be found
	ErrNotARepo = errors.New("not a git repository")
	// ErrClockNotExist is the error returned when a clock can't be found
	ErrClockNotExist = errors.New("clock doesn't exist")
)

// Repo represents a source code repository.
type Repo interface {
	RepoConfig
	RepoKeyring
	RepoCommon
	RepoStorage
	RepoBleve
	RepoData

	Close() error
}

type RepoCommonStorage interface {
	RepoCommon
	RepoStorage
}

// ClockedRepo is a Repo that also has Lamport clocks
type ClockedRepo interface {
	Repo
	RepoClock
}

// RepoConfig access the configuration of a repository
type RepoConfig interface {
	// LocalConfig give access to the repository scoped configuration
	LocalConfig() Config

	// GlobalConfig give access to the global scoped configuration
	GlobalConfig() Config

	// AnyConfig give access to a merged local/global configuration
	AnyConfig() ConfigRead
}

// RepoKeyring give access to a user-wide storage for secrets
type RepoKeyring interface {
	// Keyring give access to a user-wide storage for secrets
	Keyring() Keyring
}

// RepoCommon represent the common function the we want all the repo to implement
type RepoCommon interface {
	// GetUserName returns the name the the user has used to configure git
	GetUserName() (string, error)

	// GetUserEmail returns the email address that the user has used to configure git.
	GetUserEmail() (string, error)

	// GetCoreEditor returns the name of the editor that the user has used to configure git.
	GetCoreEditor() (string, error)

	// GetRemotes returns the configured remotes repositories.
	GetRemotes() (map[string]string, error)
}

// RepoStorage give access to the filesystem
type RepoStorage interface {
	// LocalStorage return a billy.Filesystem giving access to $RepoPath/.git/git-bug
	LocalStorage() billy.Filesystem
}

// RepoBleve give access to Bleve to implement full-text search indexes.
type RepoBleve interface {
	// GetBleveIndex return a bleve.Index that can be used to index documents
	GetBleveIndex(name string) (bleve.Index, error)

	// ClearBleveIndex will wipe the given index
	ClearBleveIndex(name string) error
}

type Commit struct {
	Hash       Hash
	Parents    []Hash    // hashes of the parents, if any
	TreeHash   Hash      // hash of the git Tree
	SignedData io.Reader // if signed, reader for the signed data (likely, the serialized commit)
	Signature  io.Reader // if signed, reader for the (non-armored) signature
}

// RepoData give access to the git data storage
type RepoData interface {
	// FetchRefs fetch git refs matching a directory prefix to a remote
	// Ex: prefix="foo" will fetch any remote refs matching "refs/foo/*" locally.
	// The equivalent git refspec would be "refs/foo/*:refs/remotes/<remote>/foo/*"
	FetchRefs(remote string, prefix string) (string, error)

	// PushRefs push git refs matching a directory prefix to a remote
	// Ex: prefix="foo" will push any local refs matching "refs/foo/*" to the remote.
	// The equivalent git refspec would be "refs/foo/*:refs/foo/*"
	//
	// Additionally, PushRefs will update the local references in refs/remotes/<remote>/foo to match
	// the remote state.
	PushRefs(remote string, prefix string) (string, error)

	// StoreData will store arbitrary data and return the corresponding hash
	StoreData(data []byte) (Hash, error)

	// ReadData will attempt to read arbitrary data from the given hash
	ReadData(hash Hash) ([]byte, error)

	// StoreTree will store a mapping key-->Hash as a Git tree
	StoreTree(mapping []TreeEntry) (Hash, error)

	// ReadTree will return the list of entries in a Git tree
	// The given hash could be from either a commit or a tree
	ReadTree(hash Hash) ([]TreeEntry, error)

	// StoreCommit will store a Git commit with the given Git tree
	StoreCommit(treeHash Hash, parents ...Hash) (Hash, error)

	// StoreCommit will store a Git commit with the given Git tree. If signKey is not nil, the commit
	// will be signed accordingly.
	StoreSignedCommit(treeHash Hash, signKey *openpgp.Entity, parents ...Hash) (Hash, error)

	// ReadCommit read a Git commit and returns some of its characteristic
	ReadCommit(hash Hash) (Commit, error)

	// GetTreeHash return the git tree hash referenced in a commit
	// Deprecated
	GetTreeHash(commit Hash) (Hash, error)

	// ResolveRef returns the hash of the target commit of the given ref
	ResolveRef(ref string) (Hash, error)

	// UpdateRef will create or update a Git reference
	UpdateRef(ref string, hash Hash) error

	// RemoveRef will remove a Git reference
	// RemoveRef is idempotent.
	RemoveRef(ref string) error

	// ListRefs will return a list of Git ref matching the given refspec
	ListRefs(refPrefix string) ([]string, error)

	// RefExist will check if a reference exist in Git
	RefExist(ref string) (bool, error)

	// CopyRef will create a new reference with the same value as another one
	CopyRef(source string, dest string) error

	// FindCommonAncestor will return the last common ancestor of two chain of commit
	// Deprecated
	FindCommonAncestor(commit1 Hash, commit2 Hash) (Hash, error)

	// ListCommits will return the list of tree hashes of a ref, in chronological order
	ListCommits(ref string) ([]Hash, error)
}

// RepoClock give access to Lamport clocks
type RepoClock interface {
	// AllClocks return all the known clocks
	AllClocks() (map[string]lamport.Clock, error)

	// GetOrCreateClock return a Lamport clock stored in the Repo.
	// If the clock doesn't exist, it's created.
	GetOrCreateClock(name string) (lamport.Clock, error)

	// Increment is equivalent to c = GetOrCreateClock(name) + c.Increment()
	Increment(name string) (lamport.Time, error)

	// Witness is equivalent to c = GetOrCreateClock(name) + c.Witness(time)
	Witness(name string, time lamport.Time) error
}

// ClockLoader hold which logical clock need to exist for an entity and
// how to create them if they don't.
type ClockLoader struct {
	// Clocks hold the name of all the clocks this loader deal with.
	// Those clocks will be checked when the repo load. If not present or broken,
	// Witnesser will be used to create them.
	Clocks []string
	// Witnesser is a function that will initialize the clocks of a repo
	// from scratch
	Witnesser func(repo ClockedRepo) error
}

// TestedRepo is an extended ClockedRepo with function for testing only
type TestedRepo interface {
	ClockedRepo
	repoTest
}

// repoTest give access to test only functions
type repoTest interface {
	// AddRemote add a new remote to the repository
	AddRemote(name string, url string) error

	// GetLocalRemote return the URL to use to add this repo as a local remote
	GetLocalRemote() string

	// EraseFromDisk delete this repository entirely from the disk
	EraseFromDisk() error
}