Browse Source

Comment the new code in the Git package

Brendan Abolivier 6 years ago
parent
commit
dce24544fb
Signed by: Brendan Abolivier <contact@brendanabolivier.com> GPG key ID: 8EF1500759F70623
1 changed files with 66 additions and 12 deletions
  1. 66
    12
      src/git/git.go

+ 66
- 12
src/git/git.go View File

17
 	gitssh "gopkg.in/src-d/go-git.v4/plumbing/transport/ssh"
17
 	gitssh "gopkg.in/src-d/go-git.v4/plumbing/transport/ssh"
18
 )
18
 )
19
 
19
 
20
+// Repository represents a Git repository, as an abstraction layer above the
21
+// go-git library in order to also store the current configuration and the
22
+// authentication data needed to talk to the Git remote.
20
 type Repository struct {
23
 type Repository struct {
21
 	Repo *gogit.Repository
24
 	Repo *gogit.Repository
22
 	cfg  config.GitSettings
25
 	cfg  config.GitSettings
23
 	auth *gitssh.PublicKeys
26
 	auth *gitssh.PublicKeys
24
 }
27
 }
25
 
28
 
29
+// NewRepository creates a new instance of the Repository structure and fills
30
+// it accordingly to the current configuration.
31
+// Returns a boolean if the clone path doesn't contain a valid Git repository
32
+// and needs the repository to be cloned from remote before it is usable.
33
+// Returns an error if there was an issue opening the clone path or loading
34
+// authentication data.
26
 func NewRepository(cfg config.GitSettings) (r *Repository, invalidRepo bool, err error) {
35
 func NewRepository(cfg config.GitSettings) (r *Repository, invalidRepo bool, err error) {
36
+	// Load the repository.
27
 	repo, err := gogit.PlainOpen(cfg.ClonePath)
37
 	repo, err := gogit.PlainOpen(cfg.ClonePath)
28
 	if err != nil {
38
 	if err != nil {
29
 		if err == gogit.ErrRepositoryNotExists {
39
 		if err == gogit.ErrRepositoryNotExists {
33
 		}
43
 		}
34
 	}
44
 	}
35
 
45
 
46
+	// Fill the structure instance with the gogit.Repository instance and the
47
+	// configuration.
36
 	r = &Repository{
48
 	r = &Repository{
37
 		Repo: repo,
49
 		Repo: repo,
38
 		cfg:  cfg,
50
 		cfg:  cfg,
39
 	}
51
 	}
40
 
52
 
53
+	// Load authentication data in the structure instance.
41
 	err = r.getAuth()
54
 	err = r.getAuth()
42
 	return
55
 	return
43
 }
56
 }
52
 // whether the clone path already exists, or synchronising the repo with the
65
 // whether the clone path already exists, or synchronising the repo with the
53
 // remote.
66
 // remote.
54
 func (r *Repository) Sync(dontClone bool) (err error) {
67
 func (r *Repository) Sync(dontClone bool) (err error) {
55
-	// Check whether the clone path already exists
68
+	// Check whether the clone path already exists.
56
 	exists, err := dirExists(r.cfg.ClonePath)
69
 	exists, err := dirExists(r.cfg.ClonePath)
57
 	if err != nil {
70
 	if err != nil {
58
 		return
71
 		return
59
 	}
72
 	}
60
 
73
 
61
-	// Check whether the clone path is a Git repository
74
+	// Check whether the clone path is a Git repository.
62
 	var isRepo bool
75
 	var isRepo bool
63
 	if isRepo, err = dirExists(r.cfg.ClonePath + "/.git"); err != nil {
76
 	if isRepo, err = dirExists(r.cfg.ClonePath + "/.git"); err != nil {
64
 		return
77
 		return
99
 		"clone_path": r.cfg.ClonePath,
112
 		"clone_path": r.cfg.ClonePath,
100
 	}).Info("Pushing to the remote")
113
 	}).Info("Pushing to the remote")
101
 
114
 
102
-	// Push to remote
115
+	// Push to remote.
103
 	if err = r.Repo.Push(&gogit.PushOptions{
116
 	if err = r.Repo.Push(&gogit.PushOptions{
104
 		Auth: r.auth,
117
 		Auth: r.auth,
105
 	}); err != nil {
118
 	}); err != nil {
106
-		// Check error against known non-errors
119
+		// Check error against known non-errors.
107
 		err = checkRemoteErrors(err, logrus.Fields{
120
 		err = checkRemoteErrors(err, logrus.Fields{
108
 			"repo":       r.cfg.User + "@" + r.cfg.URL,
121
 			"repo":       r.cfg.User + "@" + r.cfg.URL,
109
 			"clone_path": r.cfg.ClonePath,
122
 			"clone_path": r.cfg.ClonePath,
114
 	return err
127
 	return err
115
 }
128
 }
116
 
129
 
130
+// GetLatestCommit retrieves the latest commit from the local Git repository and
131
+// returns it.
132
+// Returns an error if there was an issue fetching the references or loading the
133
+// latest one.
117
 func (r *Repository) GetLatestCommit() (*object.Commit, error) {
134
 func (r *Repository) GetLatestCommit() (*object.Commit, error) {
118
-	// Retrieve latest hash
135
+	// Retrieve the list of references from the repository.
119
 	refs, err := r.Repo.References()
136
 	refs, err := r.Repo.References()
120
 	if err != nil {
137
 	if err != nil {
121
 		return nil, err
138
 		return nil, err
122
 	}
139
 	}
123
 
140
 
141
+	// Extract the latest reference.
124
 	ref, err := refs.Next()
142
 	ref, err := refs.Next()
125
 	if err != nil {
143
 	if err != nil {
126
 		return nil, err
144
 		return nil, err
127
 	}
145
 	}
128
 
146
 
147
+	// Load the commit matching the reference's hash and return it.
129
 	hash := ref.Hash()
148
 	hash := ref.Hash()
130
 	return r.Repo.CommitObject(hash)
149
 	return r.Repo.CommitObject(hash)
131
 }
150
 }
132
 
151
 
152
+// Log loads the Git repository's log, with the most recent commit having the
153
+// given hash.
154
+// Returns an error if the log couldn't be loaded.
133
 func (r *Repository) Log(fromHash string) (object.CommitIter, error) {
155
 func (r *Repository) Log(fromHash string) (object.CommitIter, error) {
134
 	hash := plumbing.NewHash(fromHash)
156
 	hash := plumbing.NewHash(fromHash)
135
 
157
 
138
 	})
160
 	})
139
 }
161
 }
140
 
162
 
163
+// GetModifiedAndRemovedFiles takes to commits and returns the name of files
164
+// that were added, modified or removed between these two commits. Note that
165
+// the added/modified files and the removed files are returned in two separated
166
+// slices, mainly because some features using this function need to load the
167
+// files' contents afterwards, and this is done differently depending on whether
168
+// the file was removed or not.
169
+// "from" refers to the oldest commit of both, and "to" to the latest one.
170
+// Returns empty slices and no error if both commits have the same hash.
171
+// Returns an error if there was an issue loading the repository's log, the
172
+// commits' stats, or retrieving a file from the repository.
141
 func (r *Repository) GetModifiedAndRemovedFiles(
173
 func (r *Repository) GetModifiedAndRemovedFiles(
142
 	from *object.Commit, to *object.Commit,
174
 	from *object.Commit, to *object.Commit,
143
 ) (modified []string, removed []string, err error) {
175
 ) (modified []string, removed []string, err error) {
176
+	// Initialise the slices.
144
 	modified = make([]string, 0)
177
 	modified = make([]string, 0)
145
 	removed = make([]string, 0)
178
 	removed = make([]string, 0)
146
 
179
 
153
 		return
186
 		return
154
 	}
187
 	}
155
 
188
 
189
+	// Iterate over the commits contained in the commit's log.
156
 	err = iter.ForEach(func(commit *object.Commit) error {
190
 	err = iter.ForEach(func(commit *object.Commit) error {
191
+		// If the commit was done by the manager, go to the next iteration.
157
 		if commit.Author.Email == r.cfg.CommitsAuthor.Email {
192
 		if commit.Author.Email == r.cfg.CommitsAuthor.Email {
158
 			return nil
193
 			return nil
159
 		}
194
 		}
160
 
195
 
196
+		// If the current commit is the oldest one requested, break the loop.
161
 		if commit.Hash.String() == from.Hash.String() {
197
 		if commit.Hash.String() == from.Hash.String() {
162
 			return storer.ErrStop
198
 			return storer.ErrStop
163
 		}
199
 		}
164
 
200
 
201
+		// Load stats from the current commit.
165
 		stats, err := commit.Stats()
202
 		stats, err := commit.Stats()
166
 		if err != nil {
203
 		if err != nil {
167
 			return err
204
 			return err
168
 		}
205
 		}
169
 
206
 
207
+		// Iterate over the files contained in the commit's stats.
170
 		for _, stat := range stats {
208
 		for _, stat := range stats {
209
+			// Try to access the file's content.
171
 			_, err := commit.File(stat.Name)
210
 			_, err := commit.File(stat.Name)
172
 			if err != nil && err != object.ErrFileNotFound {
211
 			if err != nil && err != object.ErrFileNotFound {
173
 				return err
212
 				return err
174
 			}
213
 			}
175
 
214
 
215
+			// If the content couldn't be retrieved, it means the file was
216
+			// removed in this commit, else it means that it was either added or
217
+			// modified.
176
 			if err == object.ErrFileNotFound {
218
 			if err == object.ErrFileNotFound {
177
 				removed = append(removed, stat.Name)
219
 				removed = append(removed, stat.Name)
178
 			} else {
220
 			} else {
186
 	return
228
 	return
187
 }
229
 }
188
 
230
 
231
+// GetFilesContentsAtCommit retrieves the state of the repository at a given
232
+// commit, and returns a map contaning the contents of all files in the repository
233
+// at this time.
234
+// Returns an error if there was an issue loading the commit's tree, or loading
235
+// a file's content.
189
 func (r *Repository) GetFilesContentsAtCommit(commit *object.Commit) (map[string][]byte, error) {
236
 func (r *Repository) GetFilesContentsAtCommit(commit *object.Commit) (map[string][]byte, error) {
190
 	var content string
237
 	var content string
191
 
238
 
239
+	// Load the commit's tree.
192
 	tree, err := commit.Tree()
240
 	tree, err := commit.Tree()
193
 	if err != nil {
241
 	if err != nil {
194
 		return nil, err
242
 		return nil, err
195
 	}
243
 	}
196
 
244
 
245
+	// Initialise the map that will be returned.
197
 	filesContents := make(map[string][]byte)
246
 	filesContents := make(map[string][]byte)
198
-
247
+	// Load the files from the tree.
199
 	files := tree.Files()
248
 	files := tree.Files()
200
 
249
 
250
+	// Iterate over the files.
201
 	err = files.ForEach(func(file *object.File) error {
251
 	err = files.ForEach(func(file *object.File) error {
252
+		// Try to access the file's content at the given commit.
202
 		content, err = file.Contents()
253
 		content, err = file.Contents()
203
 		if err != nil {
254
 		if err != nil {
204
 			return err
255
 			return err
205
 		}
256
 		}
206
 
257
 
258
+		// Append the content to the map.
207
 		filesContents[file.Name] = []byte(content)
259
 		filesContents[file.Name] = []byte(content)
208
 
260
 
209
 		return nil
261
 		return nil
217
 // Returns an error if there was an issue reading the private key file or
269
 // Returns an error if there was an issue reading the private key file or
218
 // parsing it.
270
 // parsing it.
219
 func (r *Repository) getAuth() error {
271
 func (r *Repository) getAuth() error {
272
+	// Load the private key.
220
 	privateKey, err := ioutil.ReadFile(r.cfg.PrivateKeyPath)
273
 	privateKey, err := ioutil.ReadFile(r.cfg.PrivateKeyPath)
221
 	if err != nil {
274
 	if err != nil {
222
 		return err
275
 		return err
223
 	}
276
 	}
224
 
277
 
278
+	// Parse the private key.
225
 	signer, err := ssh.ParsePrivateKey(privateKey)
279
 	signer, err := ssh.ParsePrivateKey(privateKey)
226
 	if err != nil {
280
 	if err != nil {
227
 		return err
281
 		return err
250
 // tree or pulling from the remote. In the latter case, if the error is a known
304
 // tree or pulling from the remote. In the latter case, if the error is a known
251
 // non-error, doesn't return any error.
305
 // non-error, doesn't return any error.
252
 func (r *Repository) pull() error {
306
 func (r *Repository) pull() error {
253
-	// Open the repository
307
+	// Open the repository.
254
 	repo, err := gogit.PlainOpen(r.cfg.ClonePath)
308
 	repo, err := gogit.PlainOpen(r.cfg.ClonePath)
255
 	if err != nil {
309
 	if err != nil {
256
 		return err
310
 		return err
257
 	}
311
 	}
258
 
312
 
259
-	// Get its worktree
313
+	// Get its worktree.
260
 	w, err := repo.Worktree()
314
 	w, err := repo.Worktree()
261
 	if err != nil {
315
 	if err != nil {
262
 		return err
316
 		return err
263
 	}
317
 	}
264
 
318
 
265
-	// Pull from remote
319
+	// Pull from remote.
266
 	if err = w.Pull(&gogit.PullOptions{
320
 	if err = w.Pull(&gogit.PullOptions{
267
 		RemoteName: "origin",
321
 		RemoteName: "origin",
268
 		Auth:       r.auth,
322
 		Auth:       r.auth,
269
 	}); err != nil {
323
 	}); err != nil {
270
-		// Check error against known non-errors
324
+		// Check error against known non-errors.
271
 		err = checkRemoteErrors(err, logrus.Fields{
325
 		err = checkRemoteErrors(err, logrus.Fields{
272
 			"clone_path": r.cfg.ClonePath,
326
 			"clone_path": r.cfg.ClonePath,
273
 			"error":      err,
327
 			"error":      err,
301
 func checkRemoteErrors(err error, logFields logrus.Fields) error {
355
 func checkRemoteErrors(err error, logFields logrus.Fields) error {
302
 	var nonError bool
356
 	var nonError bool
303
 
357
 
304
-	// Check against known non-errors
358
+	// Check against known non-errors.
305
 	switch err {
359
 	switch err {
306
 	case gogit.NoErrAlreadyUpToDate:
360
 	case gogit.NoErrAlreadyUpToDate:
307
 		nonError = true
361
 		nonError = true
314
 		break
368
 		break
315
 	}
369
 	}
316
 
370
 
317
-	// Log non-error
371
+	// Log non-error.
318
 	if nonError {
372
 	if nonError {
319
 		logrus.WithFields(logFields).Warn("Caught specific non-error")
373
 		logrus.WithFields(logFields).Warn("Caught specific non-error")
320
 
374