func StartServer(mountDir string) error {
	c, err := client.NewEnvClient()
	if err != nil {
		return errors.Wrap(err, "Could not create Docker client")
	}
	image := "fjukstad/lfs-server"
	_, err = c.ImagePull(context.Background(), image,
		types.ImagePullOptions{})
	if err != nil {
		return errors.Wrap(err, "Could not pull iamge")
	}
	hostPath, err := filepath.Abs(mountDir)
	if err != nil {
		return errors.Wrap(err,
			"Could not create absolute git-lfs directory path")
	}
	bind := hostPath + ":/lfs"
	ps := make(nat.PortSet)
	ps["9999/tcp"] = struct{}{}
	pm := make(nat.PortMap)
	pm["9999/tcp"] = []nat.PortBinding{nat.PortBinding{"0.0.0.0", "9999"}}
	resp, err := c.ContainerCreate(context.Background(),
		&container.Config{Image: image,
			ExposedPorts: ps},
		&container.HostConfig{
			Binds:        []string{bind},
			PortBindings: pm},
		&network.NetworkingConfig{},
		"git-lfs-server")
	if err != nil || resp.ID == " " {
		return errors.Wrap(err, "Could not create git-lfs server container")
	}
	containerId := resp.ID
	err = c.ContainerStart(context.Background(), containerId,
		types.ContainerStartOptions{})
	return err
}
Starts a git-lfs server in a Docker container
package lfs
import (
	"context"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/network"
	"github.com/docker/docker/client"
	"github.com/docker/go-connections/nat"
	git "gopkg.in/libgit2/git2go.v26"
	"github.com/pkg/errors"
)
func Track(filename, repositoryLocation string) (string, error) {
	cmd := exec.Command("git-lfs", "track", filename)
	cmd.Dir = repositoryLocation
	out, err := cmd.Output()
	time.Sleep(2 * time.Second)
	output := string(out)
	output = strings.TrimRight(output, "\n")
	return output, err
}
func AddAndCommitData(path, msg string) (string, error) {
	changes, repositoryLocation, err := AddData(path)
	if err != nil {
		return "", err
	}
	var commitId string
	if changes {
		commitId, err = commit(repositoryLocation, msg)
		if err != nil {
			return "", err
		}
	}
	return commitId, nil
}
func Add(filename string) error {
	repo, _, err := openContainingRepository(filename)
	if err != nil {
		return err
	}
	changed, err := fileChanged(repo, filename)
	if err != nil {
		return err
	}
	if changed {
		err := addToIndex(repo, filename)
		if err != nil {
			return err
		}
	}
	return nil
}
func openContainingRepository(filename string) (repo *git.Repository,
	repositoryLocation string, err error) {
	path := filepath.Dir(filename)
	path, err = filepath.Abs(path)
	if err != nil {
		return nil, "", err
	}
	return openRepository(path)
}
func AddAndCommit(filename, msg string) (string, error) {
	repo, repositoryLocation, err := openContainingRepository(filename)
	if err != nil {
		return "", err
	}
	changed, err := fileChanged(repo, filename)
	if err != nil {
		return "", err
	}
	var commitId string
	if changed {
		err := addToIndex(repo, filename)
		if err != nil {
			return "", err
		}
		commitId, err = commit(repositoryLocation, msg)
		if err != nil {
			return "", err
		}
	} else {
		return "", errors.New("No changes. Nothing to commit")
	}
	return commitId, nil
}
func AddData(path string) (changes bool, repositoryLocation string, err error) {
	wd, err := os.Getwd()
	if err != nil {
		return false, "", err
	}
	defer os.Chdir(wd)
	repo, repositoryLocation, err := openRepository(path)
	if err != nil {
		return false, "", err
	}
	os.Chdir(repositoryLocation)
	dataPath, err := filepath.Rel(repositoryLocation, path)
	if err != nil {
		return false, "", err
	}
	dataPattern := "" + dataPath + "/**"
	gitAttr := ".gitattributes"
	b, err := ioutil.ReadFile(gitAttr)
	if err != nil {
		pe := err.(*os.PathError)
		if pe.Err.Error() != "no such file or directory" {
			return false, "", err
		}
	}
	if !strings.Contains(string(b), dataPattern) {
		output, err := Track(dataPattern, repositoryLocation)
		if err != nil {
			return false, "", errors.Wrap(err, "Could not track files using git-lfs: "+output)
		}
	}
	changed, err := fileChanged(repo, gitAttr)
	if err != nil {
		return false, "", err
	}
	if changed {
		err := addToIndex(repo, gitAttr)
		if err != nil {
			return false, "", err
		}
		changes = changed
	}
	changed = false
	err = filepath.Walk(dataPath, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return errors.Wrap(err, "Could not add "+path+"to repository")
		}
		if info.IsDir() {
			return nil
		}
		changedFile, err := fileChanged(repo, path)
		if err != nil {
			return err
		}
		if changedFile {
			changed = true
			return nil
		}
		return nil
	})
	if err != nil {
		return false, "", err
	}
	if changed {
		output, err := add(dataPath, repositoryLocation)
		if err != nil {
			return false, "", errors.Wrap(err, "Could not add files "+output)
		}
	}
	changes = changed
	return changes, repositoryLocation, nil
}
func addToIndex(repo *git.Repository, path string) error {
	index, err := repo.Index()
	if err != nil {
		return err
	}
	err = index.AddByPath(path)
	if err != nil {
		return err
	}
	_, err = index.WriteTree()
	if err != nil {
		return err
	}
	err = index.Write()
	if err != nil {
		return err
	}
	return err
}
func popLastDirectory(path string) string {
	list := strings.Split(path, "/")
	list = list[0 : len(list)-1]
	path = "/" + filepath.Join(list...)
	return path
}
func fileChanged(repo *git.Repository, path string) (bool, error) {
	status, err := repo.StatusFile(path)
	if err != nil {
		return false, err
	}
	if status == git.StatusWtNew || status == git.StatusWtModified ||
		status == git.StatusWtDeleted {
		return true, nil
	}
	return false, nil
}
func commit(path, msg string) (string, error) {
	repo, err := git.OpenRepository(path)
	if err != nil {
		return "", err
	}
	index, err := repo.Index()
	if err != nil {
		return "", err
	}
	treeId, err := index.WriteTree()
	if err != nil {
		return "", err
	}
	tree, err := repo.LookupTree(treeId)
	if err != nil {
		return "", err
	}
	err = index.Write()
	if err != nil {
		return "", err
	}
	var sig = &git.Signature{
		"walrus",
		"walrus@github.com/fjukstad/walrus",
		time.Now(),
	}
	var commitId *git.Oid
	currentBranch, err := repo.Head()
	if err != nil {
		commitId, err = repo.CreateCommit("HEAD", sig, sig, msg, tree)
	} else {
		currentTip, err := repo.LookupCommit(currentBranch.Target())
		if err != nil {
			return "", err
		}
		commitId, err = repo.CreateCommit("HEAD", sig, sig, msg, tree, currentTip)
	}
	if err != nil {
		return "", err
	}
	err = index.Write()
	if err != nil {
		return "", err
	}
	return commitId.String(), nil
}
func openRepository(path string) (repo *git.Repository, repositoryPath string, err error) {
	wd, err := os.Getwd()
	if err != nil {
		return nil, "", err
	}
	for {
		repo, err = git.OpenRepository(path)
		if err != nil {
			path = popLastDirectory(path)
			if path == "/" {
				path = wd
				log.Println("Output directory is not in a git repository. Creating one in " + path)
				repo, err = git.InitRepository(wd, false)
				if err != nil {
					return nil, "", errors.Wrap(err, "Could not initialize git repository")
				}
				break
			}
		} else {
			break
		}
	}
	return repo, path, nil
}
func add(path, repositoryLocation string) (string, error) {
	cmd := exec.Command("git", "add", path)
	cmd.Dir = repositoryLocation
	out, err := cmd.Output()
	output := string(out)
	output = strings.TrimRight(output, "\n")
	return output, err
}