| 
				
			 | 
			
			
				@@ -10,87 +10,227 @@ import ( 
			 | 
		
	
		
			
			| 
				10
			 | 
			
				10
			 | 
			
			
				 	"github.com/sirupsen/logrus" 
			 | 
		
	
		
			
			| 
				11
			 | 
			
				11
			 | 
			
			
				 	"golang.org/x/crypto/ssh" 
			 | 
		
	
		
			
			| 
				12
			 | 
			
				12
			 | 
			
			
				 	gogit "gopkg.in/src-d/go-git.v4" 
			 | 
		
	
		
			
			| 
				
			 | 
			
				13
			 | 
			
			
				+	"gopkg.in/src-d/go-git.v4/plumbing" 
			 | 
		
	
		
			
			| 
				
			 | 
			
				14
			 | 
			
			
				+	"gopkg.in/src-d/go-git.v4/plumbing/object" 
			 | 
		
	
		
			
			| 
				
			 | 
			
				15
			 | 
			
			
				+	"gopkg.in/src-d/go-git.v4/plumbing/storer" 
			 | 
		
	
		
			
			| 
				13
			 | 
			
				16
			 | 
			
			
				 	"gopkg.in/src-d/go-git.v4/plumbing/transport" 
			 | 
		
	
		
			
			| 
				14
			 | 
			
				17
			 | 
			
			
				 	gitssh "gopkg.in/src-d/go-git.v4/plumbing/transport/ssh" 
			 | 
		
	
		
			
			| 
				15
			 | 
			
				18
			 | 
			
			
				 ) 
			 | 
		
	
		
			
			| 
				16
			 | 
			
				19
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				
			 | 
			
				20
			 | 
			
			
				+type Repository struct { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				21
			 | 
			
			
				+	Repo *gogit.Repository 
			 | 
		
	
		
			
			| 
				
			 | 
			
				22
			 | 
			
			
				+	cfg  config.GitSettings 
			 | 
		
	
		
			
			| 
				
			 | 
			
				23
			 | 
			
			
				+	auth *gitssh.PublicKeys 
			 | 
		
	
		
			
			| 
				
			 | 
			
				24
			 | 
			
			
				+} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				25
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				26
			 | 
			
			
				+func NewRepository(cfg config.GitSettings) (r *Repository, invalidRepo bool, err error) { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				27
			 | 
			
			
				+	repo, err := gogit.PlainOpen(cfg.ClonePath) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				28
			 | 
			
			
				+	if err != nil { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				29
			 | 
			
			
				+		if err == gogit.ErrRepositoryNotExists { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				30
			 | 
			
			
				+			invalidRepo = true 
			 | 
		
	
		
			
			| 
				
			 | 
			
				31
			 | 
			
			
				+		} else { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				32
			 | 
			
			
				+			return 
			 | 
		
	
		
			
			| 
				
			 | 
			
				33
			 | 
			
			
				+		} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				34
			 | 
			
			
				+	} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				35
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				36
			 | 
			
			
				+	r = &Repository{ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				37
			 | 
			
			
				+		Repo: repo, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				38
			 | 
			
			
				+		cfg:  cfg, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				39
			 | 
			
			
				+	} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				40
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				41
			 | 
			
			
				+	err = r.getAuth() 
			 | 
		
	
		
			
			| 
				
			 | 
			
				42
			 | 
			
			
				+	return 
			 | 
		
	
		
			
			| 
				
			 | 
			
				43
			 | 
			
			
				+} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				44
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				17
			 | 
			
				45
			 | 
			
			
				 // Sync synchronises a Git repository using a given configuration. "synchronises" 
			 | 
		
	
		
			
			| 
				18
			 | 
			
				46
			 | 
			
			
				 // means that, if the repo from the configuration isn't already cloned in the 
			 | 
		
	
		
			
			| 
				19
			 | 
			
				
			 | 
			
			
				-// directory specified in the configuration, it will clone the repository, 
			 | 
		
	
		
			
			| 
				20
			 | 
			
				
			 | 
			
			
				-// else it will simply pull it in order to be up to date with the remote. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				47
			 | 
			
			
				+// directory specified in the configuration, it will clone the repository (unless 
			 | 
		
	
		
			
			| 
				
			 | 
			
				48
			 | 
			
			
				+// if explicitely told not to), else it will simply pull it in order to be up to 
			 | 
		
	
		
			
			| 
				
			 | 
			
				49
			 | 
			
			
				+// date with the remote. 
			 | 
		
	
		
			
			| 
				21
			 | 
			
				50
			 | 
			
			
				 // Returns the go-git representation of the repository. 
			 | 
		
	
		
			
			| 
				22
			 | 
			
				51
			 | 
			
			
				 // Returns an error if there was an issue loading the SSH private key, checking 
			 | 
		
	
		
			
			| 
				23
			 | 
			
				52
			 | 
			
			
				 // whether the clone path already exists, or synchronising the repo with the 
			 | 
		
	
		
			
			| 
				24
			 | 
			
				53
			 | 
			
			
				 // remote. 
			 | 
		
	
		
			
			| 
				25
			 | 
			
				
			 | 
			
			
				-func Sync(cfg config.GitSettings) (r *gogit.Repository, err error) { 
			 | 
		
	
		
			
			| 
				26
			 | 
			
				
			 | 
			
			
				-	// Generate an authentication structure instance from the user and private 
			 | 
		
	
		
			
			| 
				27
			 | 
			
				
			 | 
			
			
				-	// key 
			 | 
		
	
		
			
			| 
				28
			 | 
			
				
			 | 
			
			
				-	auth, err := getAuth(cfg.User, cfg.PrivateKeyPath) 
			 | 
		
	
		
			
			| 
				29
			 | 
			
				
			 | 
			
			
				-	if err != nil { 
			 | 
		
	
		
			
			| 
				30
			 | 
			
				
			 | 
			
			
				-		return 
			 | 
		
	
		
			
			| 
				31
			 | 
			
				
			 | 
			
			
				-	} 
			 | 
		
	
		
			
			| 
				32
			 | 
			
				
			 | 
			
			
				- 
			 | 
		
	
		
			
			| 
				
			 | 
			
				54
			 | 
			
			
				+func (r *Repository) Sync(dontClone bool) (err error) { 
			 | 
		
	
		
			
			| 
				33
			 | 
			
				55
			 | 
			
			
				 	// Check whether the clone path already exists 
			 | 
		
	
		
			
			| 
				34
			 | 
			
				
			 | 
			
			
				-	exists, err := dirExists(cfg.ClonePath) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				56
			 | 
			
			
				+	exists, err := dirExists(r.cfg.ClonePath) 
			 | 
		
	
		
			
			| 
				35
			 | 
			
				57
			 | 
			
			
				 	if err != nil { 
			 | 
		
	
		
			
			| 
				36
			 | 
			
				58
			 | 
			
			
				 		return 
			 | 
		
	
		
			
			| 
				37
			 | 
			
				59
			 | 
			
			
				 	} 
			 | 
		
	
		
			
			| 
				38
			 | 
			
				60
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				39
			 | 
			
				61
			 | 
			
			
				 	// Check whether the clone path is a Git repository 
			 | 
		
	
		
			
			| 
				40
			 | 
			
				62
			 | 
			
			
				 	var isRepo bool 
			 | 
		
	
		
			
			| 
				41
			 | 
			
				
			 | 
			
			
				-	if isRepo, err = dirExists(cfg.ClonePath + "/.git"); err != nil { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				63
			 | 
			
			
				+	if isRepo, err = dirExists(r.cfg.ClonePath + "/.git"); err != nil { 
			 | 
		
	
		
			
			| 
				42
			 | 
			
				64
			 | 
			
			
				 		return 
			 | 
		
	
		
			
			| 
				43
			 | 
			
				65
			 | 
			
			
				 	} else if exists && !isRepo { 
			 | 
		
	
		
			
			| 
				44
			 | 
			
				66
			 | 
			
			
				 		err = fmt.Errorf( 
			 | 
		
	
		
			
			| 
				45
			 | 
			
				67
			 | 
			
			
				 			"%s already exists but is not a Git repository", 
			 | 
		
	
		
			
			| 
				46
			 | 
			
				
			 | 
			
			
				-			cfg.ClonePath, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				68
			 | 
			
			
				+			r.cfg.ClonePath, 
			 | 
		
	
		
			
			| 
				47
			 | 
			
				69
			 | 
			
			
				 		) 
			 | 
		
	
		
			
			| 
				48
			 | 
			
				70
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				49
			 | 
			
				71
			 | 
			
			
				 		return 
			 | 
		
	
		
			
			| 
				50
			 | 
			
				72
			 | 
			
			
				 	} 
			 | 
		
	
		
			
			| 
				51
			 | 
			
				73
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				52
			 | 
			
				74
			 | 
			
			
				 	logrus.WithFields(logrus.Fields{ 
			 | 
		
	
		
			
			| 
				53
			 | 
			
				
			 | 
			
			
				-		"repo":       cfg.User + "@" + cfg.URL, 
			 | 
		
	
		
			
			| 
				54
			 | 
			
				
			 | 
			
			
				-		"clone_path": cfg.ClonePath, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				75
			 | 
			
			
				+		"repo":       r.cfg.User + "@" + r.cfg.URL, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				76
			 | 
			
			
				+		"clone_path": r.cfg.ClonePath, 
			 | 
		
	
		
			
			| 
				55
			 | 
			
				77
			 | 
			
			
				 		"pull":       exists, 
			 | 
		
	
		
			
			| 
				56
			 | 
			
				78
			 | 
			
			
				 	}).Info("Synchronising the Git repository with the remote") 
			 | 
		
	
		
			
			| 
				57
			 | 
			
				79
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				58
			 | 
			
				80
			 | 
			
			
				 	// If the clone path already exists, pull from the remote, else clone it. 
			 | 
		
	
		
			
			| 
				59
			 | 
			
				81
			 | 
			
			
				 	if exists { 
			 | 
		
	
		
			
			| 
				60
			 | 
			
				
			 | 
			
			
				-		r, err = pull(cfg.ClonePath, auth) 
			 | 
		
	
		
			
			| 
				61
			 | 
			
				
			 | 
			
			
				-	} else { 
			 | 
		
	
		
			
			| 
				62
			 | 
			
				
			 | 
			
			
				-		r, err = clone(cfg.URL, cfg.ClonePath, auth) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				82
			 | 
			
			
				+		err = r.pull() 
			 | 
		
	
		
			
			| 
				
			 | 
			
				83
			 | 
			
			
				+	} else if !dontClone { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				84
			 | 
			
			
				+		err = r.clone() 
			 | 
		
	
		
			
			| 
				63
			 | 
			
				85
			 | 
			
			
				 	} 
			 | 
		
	
		
			
			| 
				64
			 | 
			
				86
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				65
			 | 
			
				87
			 | 
			
			
				 	return 
			 | 
		
	
		
			
			| 
				66
			 | 
			
				88
			 | 
			
			
				 } 
			 | 
		
	
		
			
			| 
				67
			 | 
			
				89
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				
			 | 
			
				90
			 | 
			
			
				+// Push uses a given repository and configuration to push the local history of 
			 | 
		
	
		
			
			| 
				
			 | 
			
				91
			 | 
			
			
				+// the said repository to the remote, using an authentication structure instance 
			 | 
		
	
		
			
			| 
				
			 | 
			
				92
			 | 
			
			
				+// created from the configuration to authenticate on the remote. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				93
			 | 
			
			
				+// Returns with an error if there was an issue creating the authentication 
			 | 
		
	
		
			
			| 
				
			 | 
			
				94
			 | 
			
			
				+// structure instance or pushing to the remote. In the latter case, if the error 
			 | 
		
	
		
			
			| 
				
			 | 
			
				95
			 | 
			
			
				+// is a known non-error, doesn't return any error. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				96
			 | 
			
			
				+func (r *Repository) Push() (err error) { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				97
			 | 
			
			
				+	logrus.WithFields(logrus.Fields{ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				98
			 | 
			
			
				+		"repo":       r.cfg.User + "@" + r.cfg.URL, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				99
			 | 
			
			
				+		"clone_path": r.cfg.ClonePath, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				100
			 | 
			
			
				+	}).Info("Pushing to the remote") 
			 | 
		
	
		
			
			| 
				
			 | 
			
				101
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				102
			 | 
			
			
				+	// Push to remote 
			 | 
		
	
		
			
			| 
				
			 | 
			
				103
			 | 
			
			
				+	if err = r.Repo.Push(&gogit.PushOptions{ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				104
			 | 
			
			
				+		Auth: r.auth, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				105
			 | 
			
			
				+	}); err != nil { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				106
			 | 
			
			
				+		// Check error against known non-errors 
			 | 
		
	
		
			
			| 
				
			 | 
			
				107
			 | 
			
			
				+		err = checkRemoteErrors(err, logrus.Fields{ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				108
			 | 
			
			
				+			"repo":       r.cfg.User + "@" + r.cfg.URL, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				109
			 | 
			
			
				+			"clone_path": r.cfg.ClonePath, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				110
			 | 
			
			
				+			"error":      err, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				111
			 | 
			
			
				+		}) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				112
			 | 
			
			
				+	} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				113
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				114
			 | 
			
			
				+	return err 
			 | 
		
	
		
			
			| 
				
			 | 
			
				115
			 | 
			
			
				+} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				116
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				117
			 | 
			
			
				+func (r *Repository) GetLatestCommit() (*object.Commit, error) { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				118
			 | 
			
			
				+	// Retrieve latest hash 
			 | 
		
	
		
			
			| 
				
			 | 
			
				119
			 | 
			
			
				+	refs, err := r.Repo.References() 
			 | 
		
	
		
			
			| 
				
			 | 
			
				120
			 | 
			
			
				+	if err != nil { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				121
			 | 
			
			
				+		return nil, err 
			 | 
		
	
		
			
			| 
				
			 | 
			
				122
			 | 
			
			
				+	} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				123
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				124
			 | 
			
			
				+	ref, err := refs.Next() 
			 | 
		
	
		
			
			| 
				
			 | 
			
				125
			 | 
			
			
				+	if err != nil { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				126
			 | 
			
			
				+		return nil, err 
			 | 
		
	
		
			
			| 
				
			 | 
			
				127
			 | 
			
			
				+	} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				128
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				129
			 | 
			
			
				+	hash := ref.Hash() 
			 | 
		
	
		
			
			| 
				
			 | 
			
				130
			 | 
			
			
				+	return r.Repo.CommitObject(hash) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				131
			 | 
			
			
				+} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				132
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				133
			 | 
			
			
				+func (r *Repository) Log(fromHash string) (object.CommitIter, error) { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				134
			 | 
			
			
				+	hash := plumbing.NewHash(fromHash) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				135
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				136
			 | 
			
			
				+	return r.Repo.Log(&gogit.LogOptions{ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				137
			 | 
			
			
				+		From: hash, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				138
			 | 
			
			
				+	}) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				139
			 | 
			
			
				+} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				140
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				141
			 | 
			
			
				+func (r *Repository) LineCountsDeltasIgnoreManagerCommits( 
			 | 
		
	
		
			
			| 
				
			 | 
			
				142
			 | 
			
			
				+	from *object.Commit, to *object.Commit, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				143
			 | 
			
			
				+) (lineCountsDeltas map[string]int, err error) { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				144
			 | 
			
			
				+	// We expect "from" to be the oldest commit, and "to" to be the most recent 
			 | 
		
	
		
			
			| 
				
			 | 
			
				145
			 | 
			
			
				+	// one. Because Log() works the other way (in anti-chronological order), 
			 | 
		
	
		
			
			| 
				
			 | 
			
				146
			 | 
			
			
				+	// we call it with "to" and not "from" because, that way, we'll go from "to" 
			 | 
		
	
		
			
			| 
				
			 | 
			
				147
			 | 
			
			
				+	// to "from". 
			 | 
		
	
		
			
			| 
				
			 | 
			
				148
			 | 
			
			
				+	iter, err := r.Log(to.Hash.String()) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				149
			 | 
			
			
				+	if err != nil { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				150
			 | 
			
			
				+		return 
			 | 
		
	
		
			
			| 
				
			 | 
			
				151
			 | 
			
			
				+	} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				152
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				153
			 | 
			
			
				+	lineCountsDeltas = make(map[string]int) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				154
			 | 
			
			
				+	err = iter.ForEach(func(commit *object.Commit) error { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				155
			 | 
			
			
				+		if commit.Author.Email == r.cfg.CommitsAuthor.Email { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				156
			 | 
			
			
				+			return nil 
			 | 
		
	
		
			
			| 
				
			 | 
			
				157
			 | 
			
			
				+		} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				158
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				159
			 | 
			
			
				+		if commit.Hash.String() == from.Hash.String() { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				160
			 | 
			
			
				+			return storer.ErrStop 
			 | 
		
	
		
			
			| 
				
			 | 
			
				161
			 | 
			
			
				+		} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				162
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				163
			 | 
			
			
				+		stats, err := commit.Stats() 
			 | 
		
	
		
			
			| 
				
			 | 
			
				164
			 | 
			
			
				+		if err != nil { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				165
			 | 
			
			
				+			return err 
			 | 
		
	
		
			
			| 
				
			 | 
			
				166
			 | 
			
			
				+		} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				167
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				168
			 | 
			
			
				+		for _, stat := range stats { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				169
			 | 
			
			
				+			// We're getting recent -> old additions and deletions. Because we 
			 | 
		
	
		
			
			| 
				
			 | 
			
				170
			 | 
			
			
				+			// want the opposite (old -> recent), we must invert the sign of both. 
			 | 
		
	
		
			
			| 
				
			 | 
			
				171
			 | 
			
			
				+			lineCountsDeltas[stat.Name] = stat.Deletion - stat.Addition 
			 | 
		
	
		
			
			| 
				
			 | 
			
				172
			 | 
			
			
				+		} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				173
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				174
			 | 
			
			
				+		return nil 
			 | 
		
	
		
			
			| 
				
			 | 
			
				175
			 | 
			
			
				+	}) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				176
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				177
			 | 
			
			
				+	return 
			 | 
		
	
		
			
			| 
				
			 | 
			
				178
			 | 
			
			
				+} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				179
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				180
			 | 
			
			
				+func GetFilesLineCountsAtCommit(commit *object.Commit) (map[string]int, error) { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				181
			 | 
			
			
				+	tree, err := commit.Tree() 
			 | 
		
	
		
			
			| 
				
			 | 
			
				182
			 | 
			
			
				+	if err != nil { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				183
			 | 
			
			
				+		return nil, err 
			 | 
		
	
		
			
			| 
				
			 | 
			
				184
			 | 
			
			
				+	} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				185
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				186
			 | 
			
			
				+	lineCounts := make(map[string]int) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				187
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				188
			 | 
			
			
				+	files := tree.Files() 
			 | 
		
	
		
			
			| 
				
			 | 
			
				189
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				190
			 | 
			
			
				+	var lines []string 
			 | 
		
	
		
			
			| 
				
			 | 
			
				191
			 | 
			
			
				+	err = files.ForEach(func(file *object.File) error { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				192
			 | 
			
			
				+		lines, err = file.Lines() 
			 | 
		
	
		
			
			| 
				
			 | 
			
				193
			 | 
			
			
				+		if err != nil { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				194
			 | 
			
			
				+			return err 
			 | 
		
	
		
			
			| 
				
			 | 
			
				195
			 | 
			
			
				+		} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				196
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				197
			 | 
			
			
				+		lineCounts[file.Name] = len(lines) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				198
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				199
			 | 
			
			
				+		return nil 
			 | 
		
	
		
			
			| 
				
			 | 
			
				200
			 | 
			
			
				+	}) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				201
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				202
			 | 
			
			
				+	return lineCounts, err 
			 | 
		
	
		
			
			| 
				
			 | 
			
				203
			 | 
			
			
				+} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				204
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				68
			 | 
			
				205
			 | 
			
			
				 // getAuth returns the authentication structure instance needed to authenticate 
			 | 
		
	
		
			
			| 
				69
			 | 
			
				206
			 | 
			
			
				 // on the remote, using a given user and private key path. 
			 | 
		
	
		
			
			| 
				70
			 | 
			
				207
			 | 
			
			
				 // Returns an error if there was an issue reading the private key file or 
			 | 
		
	
		
			
			| 
				71
			 | 
			
				208
			 | 
			
			
				 // parsing it. 
			 | 
		
	
		
			
			| 
				72
			 | 
			
				
			 | 
			
			
				-func getAuth(user string, privateKeyPath string) (*gitssh.PublicKeys, error) { 
			 | 
		
	
		
			
			| 
				73
			 | 
			
				
			 | 
			
			
				-	privateKey, err := ioutil.ReadFile(privateKeyPath) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				209
			 | 
			
			
				+func (r *Repository) getAuth() error { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				210
			 | 
			
			
				+	privateKey, err := ioutil.ReadFile(r.cfg.PrivateKeyPath) 
			 | 
		
	
		
			
			| 
				74
			 | 
			
				211
			 | 
			
			
				 	if err != nil { 
			 | 
		
	
		
			
			| 
				75
			 | 
			
				
			 | 
			
			
				-		return nil, err 
			 | 
		
	
		
			
			| 
				
			 | 
			
				212
			 | 
			
			
				+		return err 
			 | 
		
	
		
			
			| 
				76
			 | 
			
				213
			 | 
			
			
				 	} 
			 | 
		
	
		
			
			| 
				77
			 | 
			
				214
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				78
			 | 
			
				215
			 | 
			
			
				 	signer, err := ssh.ParsePrivateKey(privateKey) 
			 | 
		
	
		
			
			| 
				79
			 | 
			
				216
			 | 
			
			
				 	if err != nil { 
			 | 
		
	
		
			
			| 
				80
			 | 
			
				
			 | 
			
			
				-		return nil, err 
			 | 
		
	
		
			
			| 
				
			 | 
			
				217
			 | 
			
			
				+		return err 
			 | 
		
	
		
			
			| 
				81
			 | 
			
				218
			 | 
			
			
				 	} 
			 | 
		
	
		
			
			| 
				82
			 | 
			
				219
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				83
			 | 
			
				
			 | 
			
			
				-	return &gitssh.PublicKeys{User: user, Signer: signer}, nil 
			 | 
		
	
		
			
			| 
				
			 | 
			
				220
			 | 
			
			
				+	r.auth = &gitssh.PublicKeys{User: r.cfg.User, Signer: signer} 
			 | 
		
	
		
			
			| 
				
			 | 
			
				221
			 | 
			
			
				+	return nil 
			 | 
		
	
		
			
			| 
				84
			 | 
			
				222
			 | 
			
			
				 } 
			 | 
		
	
		
			
			| 
				85
			 | 
			
				223
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				86
			 | 
			
				224
			 | 
			
			
				 // clone clones a Git repository into a given path, using a given auth. 
			 | 
		
	
		
			
			| 
				87
			 | 
			
				225
			 | 
			
			
				 // Returns the go-git representation of the Git repository. 
			 | 
		
	
		
			
			| 
				88
			 | 
			
				226
			 | 
			
			
				 // Returns an error if there was an issue cloning the repository. 
			 | 
		
	
		
			
			| 
				89
			 | 
			
				
			 | 
			
			
				-func clone(repo string, clonePath string, auth *gitssh.PublicKeys) (*gogit.Repository, error) { 
			 | 
		
	
		
			
			| 
				90
			 | 
			
				
			 | 
			
			
				-	return gogit.PlainClone(clonePath, false, &gogit.CloneOptions{ 
			 | 
		
	
		
			
			| 
				91
			 | 
			
				
			 | 
			
			
				-		URL:  repo, 
			 | 
		
	
		
			
			| 
				92
			 | 
			
				
			 | 
			
			
				-		Auth: auth, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				227
			 | 
			
			
				+func (r *Repository) clone() (err error) { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				228
			 | 
			
			
				+	r.Repo, err = gogit.PlainClone(r.cfg.ClonePath, false, &gogit.CloneOptions{ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				229
			 | 
			
			
				+		URL:  r.cfg.URL, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				230
			 | 
			
			
				+		Auth: r.auth, 
			 | 
		
	
		
			
			| 
				93
			 | 
			
				231
			 | 
			
			
				 	}) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				232
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				233
			 | 
			
			
				+	return err 
			 | 
		
	
		
			
			| 
				94
			 | 
			
				234
			 | 
			
			
				 } 
			 | 
		
	
		
			
			| 
				95
			 | 
			
				235
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				96
			 | 
			
				236
			 | 
			
			
				 // pull opens the repository located at a given path, and pulls it from the 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -99,32 +239,34 @@ func clone(repo string, clonePath string, auth *gitssh.PublicKeys) (*gogit.Repos 
			 | 
		
	
		
			
			| 
				99
			 | 
			
				239
			 | 
			
			
				 // Returns an error if there was an issue opening the repo, getting its work 
			 | 
		
	
		
			
			| 
				100
			 | 
			
				240
			 | 
			
			
				 // tree or pulling from the remote. In the latter case, if the error is a known 
			 | 
		
	
		
			
			| 
				101
			 | 
			
				241
			 | 
			
			
				 // non-error, doesn't return any error. 
			 | 
		
	
		
			
			| 
				102
			 | 
			
				
			 | 
			
			
				-func pull(clonePath string, auth *gitssh.PublicKeys) (*gogit.Repository, error) { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				242
			 | 
			
			
				+func (r *Repository) pull() error { 
			 | 
		
	
		
			
			| 
				103
			 | 
			
				243
			 | 
			
			
				 	// Open the repository 
			 | 
		
	
		
			
			| 
				104
			 | 
			
				
			 | 
			
			
				-	r, err := gogit.PlainOpen(clonePath) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				244
			 | 
			
			
				+	repo, err := gogit.PlainOpen(r.cfg.ClonePath) 
			 | 
		
	
		
			
			| 
				105
			 | 
			
				245
			 | 
			
			
				 	if err != nil { 
			 | 
		
	
		
			
			| 
				106
			 | 
			
				
			 | 
			
			
				-		return nil, err 
			 | 
		
	
		
			
			| 
				
			 | 
			
				246
			 | 
			
			
				+		return err 
			 | 
		
	
		
			
			| 
				107
			 | 
			
				247
			 | 
			
			
				 	} 
			 | 
		
	
		
			
			| 
				108
			 | 
			
				248
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				109
			 | 
			
				249
			 | 
			
			
				 	// Get its worktree 
			 | 
		
	
		
			
			| 
				110
			 | 
			
				
			 | 
			
			
				-	w, err := r.Worktree() 
			 | 
		
	
		
			
			| 
				
			 | 
			
				250
			 | 
			
			
				+	w, err := repo.Worktree() 
			 | 
		
	
		
			
			| 
				111
			 | 
			
				251
			 | 
			
			
				 	if err != nil { 
			 | 
		
	
		
			
			| 
				112
			 | 
			
				
			 | 
			
			
				-		return nil, err 
			 | 
		
	
		
			
			| 
				
			 | 
			
				252
			 | 
			
			
				+		return err 
			 | 
		
	
		
			
			| 
				113
			 | 
			
				253
			 | 
			
			
				 	} 
			 | 
		
	
		
			
			| 
				114
			 | 
			
				254
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				115
			 | 
			
				255
			 | 
			
			
				 	// Pull from remote 
			 | 
		
	
		
			
			| 
				116
			 | 
			
				256
			 | 
			
			
				 	if err = w.Pull(&gogit.PullOptions{ 
			 | 
		
	
		
			
			| 
				117
			 | 
			
				257
			 | 
			
			
				 		RemoteName: "origin", 
			 | 
		
	
		
			
			| 
				118
			 | 
			
				
			 | 
			
			
				-		Auth:       auth, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				258
			 | 
			
			
				+		Auth:       r.auth, 
			 | 
		
	
		
			
			| 
				119
			 | 
			
				259
			 | 
			
			
				 	}); err != nil { 
			 | 
		
	
		
			
			| 
				120
			 | 
			
				260
			 | 
			
			
				 		// Check error against known non-errors 
			 | 
		
	
		
			
			| 
				121
			 | 
			
				261
			 | 
			
			
				 		err = checkRemoteErrors(err, logrus.Fields{ 
			 | 
		
	
		
			
			| 
				122
			 | 
			
				
			 | 
			
			
				-			"clone_path": clonePath, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				262
			 | 
			
			
				+			"clone_path": r.cfg.ClonePath, 
			 | 
		
	
		
			
			| 
				123
			 | 
			
				263
			 | 
			
			
				 			"error":      err, 
			 | 
		
	
		
			
			| 
				124
			 | 
			
				264
			 | 
			
			
				 		}) 
			 | 
		
	
		
			
			| 
				125
			 | 
			
				265
			 | 
			
			
				 	} 
			 | 
		
	
		
			
			| 
				126
			 | 
			
				266
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				127
			 | 
			
				
			 | 
			
			
				-	return r, err 
			 | 
		
	
		
			
			| 
				
			 | 
			
				267
			 | 
			
			
				+	r.Repo = repo 
			 | 
		
	
		
			
			| 
				
			 | 
			
				268
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				269
			 | 
			
			
				+	return err 
			 | 
		
	
		
			
			| 
				128
			 | 
			
				270
			 | 
			
			
				 } 
			 | 
		
	
		
			
			| 
				129
			 | 
			
				271
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				130
			 | 
			
				272
			 | 
			
			
				 // dirExists is a snippet checking if a directory exists on the disk. 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -141,39 +283,6 @@ func dirExists(path string) (bool, error) { 
			 | 
		
	
		
			
			| 
				141
			 | 
			
				283
			 | 
			
			
				 	return true, err 
			 | 
		
	
		
			
			| 
				142
			 | 
			
				284
			 | 
			
			
				 } 
			 | 
		
	
		
			
			| 
				143
			 | 
			
				285
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				144
			 | 
			
				
			 | 
			
			
				-// Push uses a given repository and configuration to push the local history of 
			 | 
		
	
		
			
			| 
				145
			 | 
			
				
			 | 
			
			
				-// the said repository to the remote, using an authentication structure instance 
			 | 
		
	
		
			
			| 
				146
			 | 
			
				
			 | 
			
			
				-// created from the configuration to authenticate on the remote. 
			 | 
		
	
		
			
			| 
				147
			 | 
			
				
			 | 
			
			
				-// Returns with an error if there was an issue creating the authentication 
			 | 
		
	
		
			
			| 
				148
			 | 
			
				
			 | 
			
			
				-// structure instance or pushing to the remote. In the latter case, if the error 
			 | 
		
	
		
			
			| 
				149
			 | 
			
				
			 | 
			
			
				-// is a known non-error, doesn't return any error. 
			 | 
		
	
		
			
			| 
				150
			 | 
			
				
			 | 
			
			
				-func Push(r *gogit.Repository, cfg config.GitSettings) error { 
			 | 
		
	
		
			
			| 
				151
			 | 
			
				
			 | 
			
			
				-	// Get the authentication structure instance 
			 | 
		
	
		
			
			| 
				152
			 | 
			
				
			 | 
			
			
				-	auth, err := getAuth(cfg.User, cfg.PrivateKeyPath) 
			 | 
		
	
		
			
			| 
				153
			 | 
			
				
			 | 
			
			
				-	if err != nil { 
			 | 
		
	
		
			
			| 
				154
			 | 
			
				
			 | 
			
			
				-		return err 
			 | 
		
	
		
			
			| 
				155
			 | 
			
				
			 | 
			
			
				-	} 
			 | 
		
	
		
			
			| 
				156
			 | 
			
				
			 | 
			
			
				- 
			 | 
		
	
		
			
			| 
				157
			 | 
			
				
			 | 
			
			
				-	logrus.WithFields(logrus.Fields{ 
			 | 
		
	
		
			
			| 
				158
			 | 
			
				
			 | 
			
			
				-		"repo":       cfg.User + "@" + cfg.URL, 
			 | 
		
	
		
			
			| 
				159
			 | 
			
				
			 | 
			
			
				-		"clone_path": cfg.ClonePath, 
			 | 
		
	
		
			
			| 
				160
			 | 
			
				
			 | 
			
			
				-	}).Info("Pushing to the remote") 
			 | 
		
	
		
			
			| 
				161
			 | 
			
				
			 | 
			
			
				- 
			 | 
		
	
		
			
			| 
				162
			 | 
			
				
			 | 
			
			
				-	// Push to remote 
			 | 
		
	
		
			
			| 
				163
			 | 
			
				
			 | 
			
			
				-	if err = r.Push(&gogit.PushOptions{ 
			 | 
		
	
		
			
			| 
				164
			 | 
			
				
			 | 
			
			
				-		Auth: auth, 
			 | 
		
	
		
			
			| 
				165
			 | 
			
				
			 | 
			
			
				-	}); err != nil { 
			 | 
		
	
		
			
			| 
				166
			 | 
			
				
			 | 
			
			
				-		// Check error against known non-errors 
			 | 
		
	
		
			
			| 
				167
			 | 
			
				
			 | 
			
			
				-		err = checkRemoteErrors(err, logrus.Fields{ 
			 | 
		
	
		
			
			| 
				168
			 | 
			
				
			 | 
			
			
				-			"repo":       cfg.User + "@" + cfg.URL, 
			 | 
		
	
		
			
			| 
				169
			 | 
			
				
			 | 
			
			
				-			"clone_path": cfg.ClonePath, 
			 | 
		
	
		
			
			| 
				170
			 | 
			
				
			 | 
			
			
				-			"error":      err, 
			 | 
		
	
		
			
			| 
				171
			 | 
			
				
			 | 
			
			
				-		}) 
			 | 
		
	
		
			
			| 
				172
			 | 
			
				
			 | 
			
			
				-	} 
			 | 
		
	
		
			
			| 
				173
			 | 
			
				
			 | 
			
			
				- 
			 | 
		
	
		
			
			| 
				174
			 | 
			
				
			 | 
			
			
				-	return err 
			 | 
		
	
		
			
			| 
				175
			 | 
			
				
			 | 
			
			
				-} 
			 | 
		
	
		
			
			| 
				176
			 | 
			
				
			 | 
			
			
				- 
			 | 
		
	
		
			
			| 
				177
			 | 
			
				286
			 | 
			
			
				 // processRemoteErrors checks an error against known non-errors returned when 
			 | 
		
	
		
			
			| 
				178
			 | 
			
				287
			 | 
			
			
				 // communicating with the remote. If the error is a non-error, returns nil and 
			 | 
		
	
		
			
			| 
				179
			 | 
			
				288
			 | 
			
			
				 // logs it with the provided fields. If not, returns the error. 
			 |