func (g *GitLab) GetDiff(project, user, sha string) (string, error) {
	u := fmt.Sprintf("/%s/%s/commit/%s.diff", g.group, project, sha)
	req, err := g.client.NewRequest("GET", u, nil, nil)
	if err != nil {
		return "", err
	}
	req.URL, err = req.URL.Parse(u)
	if err != nil {
		return "", err
	}
	var diff bytes.Buffer
	resp, err := g.client.Do(req, &diff)
	if err != nil {
		if resp != nil && resp.StatusCode == http.StatusUnauthorized {
			return "", fmt.Errorf(invalidGitLabToken, g.group)
		}
		return "", fmt.Errorf("Error retrieving commit %s: %v", sha, err)
	}
	if diff.Len() == 0 {
		return "", nil
	}
	const layout = "Mon Jan 2 3:04 2006"
	t := time.Now()
	msg := fmt.Sprintf("Commit : %s\nDate   : %s\nUser   : %s\n<br />%s",
		sha,
		t.Format(layout),
		user,
		diff.String(),
	)
	return msg, nil
}
GetDiff implements the Git interface
package git
import (
	"bytes"
	"encoding/base64"
	"fmt"
	"net/http"
	"net/url"
	"path/filepath"
	"strings"
	"time"
	"github.com/xanzy/go-gitlab"
)
const (
	invalidGitLabToken = "The token configured for GitLab group %s is not valid!"
)
func (g *GitLab) GetContent(project, path string) (*File, interface{}, error) {
	ns := fmt.Sprintf("%s/%s", g.group, project)
	treeOpts := &gitlab.ListTreeOptions{
		Path: gitlab.String(path),
	}
	tree, resp, err := g.client.Repositories.ListTree(ns, treeOpts)
	if err != nil {
		if resp != nil {
			switch resp.StatusCode {
			case http.StatusNotFound:
				return nil, nil, nil
			case http.StatusUnauthorized:
				return nil, nil, fmt.Errorf(invalidGitLabToken, g.group)
			}
		}
		return nil, nil, fmt.Errorf("Error retrieving tree for %s: %v", path, err)
	}
	if len(tree) > 0 {
		var files []string
		for _, file := range tree {
			files = append(files, filepath.Join(path, file.Name))
		}
		return nil, files, nil
	}
	fileOpts := &gitlab.GetFileOptions{
		Ref: gitlab.String("master"),
	}
	file, resp, err := g.client.RepositoryFiles.GetFile(ns, path, fileOpts)
	if err != nil {
		if resp != nil {
			switch resp.StatusCode {
			case http.StatusNotFound:
				return nil, nil, nil
			case http.StatusUnauthorized:
				return nil, nil, fmt.Errorf(invalidGitLabToken, g.group)
			}
		}
		return nil, nil, fmt.Errorf("Error retrieving file %s: %v", path, err)
	}
	f := &File{
		Content: file.Content,
		SHA:     file.CommitID,
	}
	if file.Encoding == "base64" {
		content, err := base64.StdEncoding.DecodeString(file.Content)
		if err != nil {
			return nil, nil, fmt.Errorf("Error decoding file %s, %v", path, err)
		}
		f.Content = string(content)
	}
	return f, nil, nil
}
func (g *GitLab) CreateFile(project, path, msg string, usr *User, content []byte) (string, error) {
	ns := fmt.Sprintf("%s/%s", g.group, project)
	opts := &gitlab.CreateFileOptions{
		Branch:        gitlab.String("master"),
		AuthorEmail:   &usr.Mail,
		AuthorName:    &usr.Name,
		Content:       gitlab.String(string(content)),
		CommitMessage: gitlab.String(msg),
	}
	_, resp, err := g.client.RepositoryFiles.CreateFile(ns, path, opts)
	if err != nil {
		if resp != nil && resp.StatusCode == http.StatusUnauthorized {
			return "", fmt.Errorf(invalidGitLabToken, g.group)
		}
		return "", fmt.Errorf("Error creating file %s: %v", path, err)
	}
	return g.shaOfLatestCommit(project)
}
func (g *GitLab) UpdateFile(project, path, sha, msg string, usr *User, content []byte) (string, error) {
	ns := fmt.Sprintf("%s/%s", g.group, project)
	opts := &gitlab.UpdateFileOptions{
		Branch:        gitlab.String("master"),
		AuthorEmail:   &usr.Mail,
		AuthorName:    &usr.Name,
		Content:       gitlab.String(string(content)),
		CommitMessage: gitlab.String(msg),
	}
	_, resp, err := g.client.RepositoryFiles.UpdateFile(ns, path, opts)
	if err != nil {
		if resp != nil && resp.StatusCode == http.StatusUnauthorized {
			return "", fmt.Errorf(invalidGitLabToken, g.group)
		}
		return "", fmt.Errorf("Error updating file %s: %v", path, err)
	}
	return g.shaOfLatestCommit(project)
}
func (g *GitLab) DeleteFile(project, path, sha, msg string, usr *User) (string, error) {
	ns := fmt.Sprintf("%s/%s", g.group, project)
	opts := &gitlab.DeleteFileOptions{
		Branch:        gitlab.String("master"),
		AuthorEmail:   &usr.Mail,
		AuthorName:    &usr.Name,
		CommitMessage: gitlab.String(msg),
	}
	resp, err := g.client.RepositoryFiles.DeleteFile(ns, path, opts)
	if err != nil {
		if resp != nil && resp.StatusCode == http.StatusUnauthorized {
			return "", fmt.Errorf(invalidGitLabToken, g.group)
		}
		return "", fmt.Errorf("Error deleting file %s: %v", path, err)
	}
	return g.shaOfLatestCommit(project)
}
func (g *GitLab) DeleteDirectory(project, msg string, dir interface{}, usr *User) error {
	ns := fmt.Sprintf("%s/%s", g.group, project)
	for _, file := range dir.([]string) {
		fn := strings.TrimPrefix(file, "data_bags/")
		msg := fmt.Sprintf(msg, strings.TrimSuffix(fn, ".json"))
		opts := &gitlab.DeleteFileOptions{
			Branch:        gitlab.String("master"),
			AuthorEmail:   &usr.Mail,
			AuthorName:    &usr.Name,
			CommitMessage: gitlab.String(msg),
		}
		resp, err := g.client.RepositoryFiles.DeleteFile(ns, file, opts)
		if err != nil {
			if resp != nil && resp.StatusCode == http.StatusUnauthorized {
				return fmt.Errorf(invalidGitLabToken, g.group)
			}
			return fmt.Errorf("Error deleting file %s: %v", file, err)
		}
	}
	return nil
}